gpt4 book ai didi

c - Typedef 抽象类型指针指向具体类型指针,以避免在 C 编程中进行强制转换

转载 作者:行者123 更新时间:2023-11-30 16:12:03 25 4
gpt4 key购买 nike

我有一个头文件,其中包含对抽象数据类型的指针进行操作以隐藏头中的依赖关系的函数。为了适应多个平台, header 中声明的函数在源树中的单独文件中具有多个实现/平台,每个实现/平台都使用自己的具体基础类型来实现接口(interface)。当然,最终程序中仅链接这些实现之一,具体取决于构建中选择的平台。

foo.h

#pragma once
typedef struct foo Foo; // abstract: never defined in the program and used as pointer
Foo* foo_create(void);
void foo_transmogrify(Foo* f);

fooImplBar.c

#include "foo.h"
#include "bar.h" // from a library that defines Bar

Foo* foo_create(void)
{
Bar* bar = bar_create();
return (Foo*) bar; // the cast is needed here to avoid the error
}

void foo_transmogrify(Foo* f)
{
bar_transmogrify((Bar*) f); // another cast here
}

fooImplBaz.c

#include "foo.h"
#include "baz.h" // from a library that defines Baz

Foo* foo_create(void)
{
Baz* baz = baz_create();
return (Foo*) baz; // the cast is needed here to avoid the error
}

void foo_transmogrify(Foo* f)
{
baz_transmogrify((Baz*) f); // another cast here
}

有办法避免这些转换吗?

换句话说,有没有办法在各自的文件夹中将 Bar*Baz* 键入到 Foo* ?我无法使用实现文件夹中的 Foo 定义更改 BarBaz 定义,因为它们(Bar 和 Baz)来自库。因此,这里在 header 中转发声明并在 .c 文件中定义的模式不适用。避免强制转换的一种方法是完全使用 void* 而不是 Foo*,但代价是丢失类型信息,我发现这比强制转换更糟糕。

最佳答案

变体 1

您可以尝试使用 union 。这将使接口(interface)“foo”依赖于所有实现(此处:“bar”和“baz”)。优点是不需要改变实现。

foo.h

#include "bar.h"
#include "baz.h"

typedef union {
Bar *bar;
Baz *baz;
} Foo;

Foo foo_create(void);
void foo_transmogrify(Foo f);

fooImplBar.c

#include "foo.h"

Foo foo_create(void)
{
Foo foo;
foo.bar = bar_create();
return foo;
}

void foo_transmogrify(Foo f)
{
bar_transmogrify(f.bar);
}

fooImplBaz.c

#include "foo.h"

Foo foo_create(void)
{
Foo foo;
foo.baz = baz_create();
return foo;
}

void foo_transmogrify(Foo f)
{
baz_transmogrify(f.baz);
}

您保留所有类型信息。

我在一个简单的项目中进行了尝试,但是 BarBaz 在各自的 header 中定义不完整。它也应该适用于完整类型。

酒吧.h

typedef struct bar Bar;
Bar* bar_create(void);
void bar_transmogrify(Bar* f);

酒吧.c

#include <stdio.h>
#include <stdlib.h>

#include "bar.h"

typedef struct bar {
int i;
} Bar;

Bar* bar_create(void)
{
Bar* bar = malloc(sizeof (Bar));
bar->i = 23;
return bar;
}

void bar_transmogrify(Bar* f)
{
printf("Bar: %d\n", f->i);
}

baz.h

typedef struct baz Baz;
Baz* baz_create(void);
void baz_transmogrify(Baz* f);

baz.c

#include <stdio.h>
#include <stdlib.h>

#include "baz.h"

typedef struct baz {
float f;
} Baz;

Baz* baz_create(void)
{
Baz* baz = malloc(sizeof (Baz));
baz->f = 3.14159;
return baz;
}

void baz_transmogrify(Baz* f)
{
printf("Baz: %f\n", f->f);
}

main.c

#include "foo.h"

int main(void)
{
Foo f;
f = foo_create();
foo_transmogrify(f);
return 0;
}

是的,我知道,如果在某些严重的场景中使用此代码,则会出现内存泄漏。需要一些ba[rz]_destroy()。但这只是一个很容易解决的小问题。

编译过程如下:

gcc -Wall -Wextra -pedantic -c fooImplBar.c -o fooImplBar.o
gcc -Wall -Wextra -pedantic -c fooImplBaz.c -o fooImplBaz.o
gcc -Wall -Wextra -pedantic -c bar.c -o bar.o
gcc -Wall -Wextra -pedantic -c baz.c -o baz.o
gcc -Wall -Wextra -pedantic -c main.c -o main.o
gcc -Wall -Wextra -pedantic main.o fooImplBar.o bar.o -o mainImplBar
gcc -Wall -Wextra -pedantic main.o fooImplBaz.o baz.o -o mainImplBaz

变体 2:

每个实现都完成了不完全定义的结构。您可能需要调整您的实现,因为它需要使用相同的类型名称。

“foo.h”与您的相同。

fooImplBar.c

#include "foo.h"
#include "bar.h"

Foo* foo_create(void)
{
Foo* bar = bar_create();
return bar;
}

void foo_transmogrify(Foo* f)
{
bar_transmogrify(f);
}

fooImplBaz.c

#include "foo.h"
#include "baz.h"

Foo* foo_create(void)
{
Foo* baz = baz_create();
return baz;
}

void foo_transmogrify(Foo* f)
{
baz_transmogrify(f);
}

您保留类型信息,因为它是相同的类型。然而,每个实现都将以自己的方式定义类型。

酒吧.h

typedef struct foo {
int i;
} Foo;

Foo* bar_create(void);
void bar_transmogrify(Foo* f);

酒吧.c

#include <stdio.h>
#include <stdlib.h>

#include "bar.h"

Foo* bar_create(void)
{
Foo* bar = malloc(sizeof (Foo));
bar->i = 23;
return bar;
}

void bar_transmogrify(Foo* f)
{
printf("Bar: %d\n", f->i);
}

baz.h

typedef struct foo {
float f;
} Foo;

Foo* baz_create(void);
void baz_transmogrify(Foo* f);

baz.c

#include <stdio.h>
#include <stdlib.h>

#include "baz.h"

Foo* baz_create(void)
{
Foo* baz = malloc(sizeof (Foo));
baz->f = 3.14159;
return baz;
}

void baz_transmogrify(Foo* f)
{
printf("Baz: %f\n", f->f);
}

main.c

#include "foo.h"

int main(void)
{
Foo* f;
f = foo_create();
foo_transmogrify(f);
return 0;
}

编译过程与变体 1 类似。

<小时/>

如果我正确理解您的介绍,不同的实现可能位于不同的文件夹中。然后,您可以对文件、类型和函数进行相同的命名,因为您不会在一个应用程序中使用多个文件、类型和函数。

这比较简单,但可能与您的设置不兼容。

这就是示例的样子。 “foo.h”和“main.c”是相同的。

酒吧/foo.c

#include <stdio.h>
#include <stdlib.h>

#include "../foo.h"

typedef struct foo {
int i;
} Foo;

Foo* foo_create(void)
{
Foo* foo = malloc(sizeof (Foo));
foo->i = 23;
return foo;
}

void foo_transmogrify(Foo* f)
{
printf("Bar: %d\n", f->i);
}

baz/foo.c

#include <stdio.h>
#include <stdlib.h>

#include "../foo.h"

typedef struct foo {
float f;
} Foo;

Foo* foo_create(void)
{
Foo* foo = malloc(sizeof (Foo));
foo->f = 3.14159;
return foo;
}

void foo_transmogrify(Foo* f)
{
printf("Baz: %f\n", f->f);
}

您不再需要包装器“fooImplBar”和“fooImplBaz”。

编译:

gcc -Wall -Wextra -pedantic -c bar/foo.c -o bar/foo.o
gcc -Wall -Wextra -pedantic -c baz/foo.c -o baz/foo.o
gcc -Wall -Wextra -pedantic -c main.c -o main.o
gcc -Wall -Wextra -pedantic main.o bar/foo.o -o mainBar
gcc -Wall -Wextra -pedantic main.o baz/foo.o -o mainBaz

关于c - Typedef 抽象类型指针指向具体类型指针,以避免在 C 编程中进行强制转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58439142/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com