我有一个头文件,其中包含对抽象数据类型的指针进行操作以隐藏头文件中的依赖项的函数。为了容纳多个平台,标头中声明的函数在源树的单独文件中具有多个实现/平台,每个都使用其自己的具体基础类型来实现接口。当然,取决于构建中选择的平台,这些实现中只有一个在最终程序中链接。
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*
?我无法在实现文件夹中使用Bar
定义更改Baz
和Foo
定义,因为它们(Bar和Baz)来自库。因此,此处在头中进行前向声明并在.c文件中进行定义的模式不适用。避免强制转换的一种方法是完全使用void*
而不是Foo*
,以丢失类型信息为代价,我发现它比强制转换更糟糕。
您可以尝试使用union:
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);
}
您保留所有类型信息。
我用一个简单的项目尝试过,但是Bar
和Baz
在各自的标题中定义不完整。它也应该适用于完整的类型。
bar.h
typedef struct bar Bar;
Bar* bar_create(void);
void bar_transmogrify(Bar* f);
bar.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("Impl1: %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("Impl2: %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