C 如何在泛型标头中定义实现定义的结构?

C 如何在泛型标头中定义实现定义的结构?,c,portability,C,Portability,我有一个C项目,设计为可移植到各种(PC和嵌入式)平台 应用程序代码将使用各种调用,这些调用将具有特定于平台的实现,但共享一个公共(通用)API以帮助实现可移植性。我正试图找到最合适的方法来声明函数原型和结构 以下是我到目前为止的想法: main.c: #include "generic.h" int main (int argc, char *argv[]) { int ret; gen_t *data; ret = foo(data); ... }

我有一个C项目,设计为可移植到各种(PC和嵌入式)平台

应用程序代码将使用各种调用,这些调用将具有特定于平台的实现,但共享一个公共(通用)API以帮助实现可移植性。我正试图找到最合适的方法来声明函数原型和结构

以下是我到目前为止的想法:

main.c:

#include "generic.h"

int main (int argc, char *argv[]) {
    int  ret;
    gen_t  *data;

    ret = foo(data);
    ...
}
gcc -c -fPIC -o platform.o impl.c
gcc -o app  main.c platform.o
#ifdef PLATFORM_X
#include "platform_x/impl.h"
#endif

/* or */

int foo (struct impl_t *data);
generic.h:(平台不可知包括)

impl.h:(特定于平台的声明)

impl.c:(特定于平台的实施)

构建:

#include "generic.h"

int main (int argc, char *argv[]) {
    int  ret;
    gen_t  *data;

    ret = foo(data);
    ...
}
gcc -c -fPIC -o platform.o impl.c
gcc -o app  main.c platform.o
#ifdef PLATFORM_X
#include "platform_x/impl.h"
#endif

/* or */

int foo (struct impl_t *data);
现在,这似乎奏效了。。。在这方面,它编译得很好。但是,我通常不会标记我的结构,因为它们在
typedef
'd别名之外从未被访问过。这是一个小小的吹毛求疵,但我想知道是否有一种方法可以实现匿名结构的相同效果

我也在问后代,因为我搜索了一段时间,找到的最接近的答案是:()

在我的例子中,这不是正确的方法,因为应用程序不应该直接包含实现头——关键是要将程序与平台解耦

我还发现了其他一些不太理想的解决方法,例如:

generic.h:

#include "generic.h"

int main (int argc, char *argv[]) {
    int  ret;
    gen_t  *data;

    ret = foo(data);
    ...
}
gcc -c -fPIC -o platform.o impl.c
gcc -o app  main.c platform.o
#ifdef PLATFORM_X
#include "platform_x/impl.h"
#endif

/* or */

int foo (struct impl_t *data);
这两个似乎都不是特别吸引人,也绝对不是我的风格。虽然我不想逆流而上,但我也不想在可能有更好的方式来实现我的想法时出现冲突的风格。因此,我认为typedef解决方案是正确的,我只剩下struct tag baggage


想法?

你目前的技术是正确的。尝试使用匿名(未标记的)
struct
会挫败您的尝试-您必须到处公开
struct
定义的详细信息,这意味着您不再拥有不透明的数据类型


在一份声明中,他说:

头文件包含的顺序意味着
generic.h
文件正向引用结构;也就是说,在定义结构之前,使用它。这不太可能实现

对于问题中显示的标题,此观察结果不正确;它对于示例
main()
code(在添加此响应之前我没有注意到)是准确的

关键的一点是,显示的接口函数获取或返回指向类型
gen\u t
的指针,该类型又映射到
struct impl\t
指针。只要客户机代码不需要为结构分配空间,或取消引用指向结构的指针以访问结构的成员,客户机代码就不需要知道结构的详细信息。将结构类型声明为现有就足够了。您可以使用其中一个来声明存在
struct impl\t

struct impl_t;

typedef struct impl_t gen_t;
后者还为类型
struct impl\u t
引入了别名
gen\u t
。另见和

问题中的原始
main()
程序是:

int main (int argc, char *argv[]) {
    int  ret;
    gen_t  data;

    ret = foo(&data);
    …
}
此代码不能用
gen\u t
作为不透明(非指针)类型编译。它可以在以下情况下工作:

typedef struct impl_t *gen_t;
它不会编译为:

typedef struct impl_t gen_t;
因为编译器必须知道结构有多大才能为
数据分配正确的空间
,但编译器无法通过定义不透明类型来知道该大小。(有关指向结构的typedefing指针,请参阅。)

因此,
main()
代码应该更像:

#include "generic.h"

int main(int argc, char **argv)
{
    gen_t *data = bar(argc, argv);
    int ret = foo(data);
    ...
}
其中(在本例中)
bar()
定义为
extern gen\u t*bar(int argc,char**argv)
,因此它返回一个指向不透明类型
gen\u t
的指针

对于始终使用
struct标记名
还是使用
typedef
作为名称,意见分歧。Linux内核是一个不使用
typedef
机制的大量代码体;所有结构都显式地
struct标记名
。另一方面,C++不需要显式<代码> TyPulfF;写作:

struct impl_t;
在C++程序中,名称“代码>隐式< /代码>现在是一个类型的名称。由于不透明结构类型需要一个标记(或者最终对所有内容使用
void*
,这对许多原因都是不好的,但主要原因是使用
void*
会失去所有类型安全性;请记住,
typedef
为基础类型引入别名,而不是新的独特类型),我用C编写代码的方式模拟了C++:

typedef struct Generic Generic;
我避免在我的类型上使用
\u t
后缀,因为POSIX保留了
\u t
供实现使用*(另请参阅)。你可能很幸运,侥幸逃脱了惩罚。我曾经在代码库上工作过,其中
dec_t
loc_t
等类型是由代码库定义的(代码库不是实现的一部分,其中“实现”是指C编译器及其支持代码,或C库及其支持代码),这两种类型都造成了数十年的痛苦,因为一些移植代码的系统定义了这些类型,这是系统的特权。其中一个名字,我设法摆脱;另一个我没有太痛苦了!如果您必须使用
\u t
(这是一种方便的方式来指示某事物是一种类型),我建议您也使用一个独特的前缀:
pqr\u typename\u t
,例如,对于某些项目
pqr


*请参阅POSIX标准中第二个表的底线。

您可以将
gen\u t
设置为指针类型(句柄)。然后客户端将不得不使用您的api来初始化/使用/销毁它。或者稍加修改,使公开的结构包含指向实际实现的不透明指针。否则,t