C 如何在泛型标头中定义实现定义的结构?
我有一个C项目,设计为可移植到各种(PC和嵌入式)平台 应用程序代码将使用各种调用,这些调用将具有特定于平台的实现,但共享一个公共(通用)API以帮助实现可移植性。我正试图找到最合适的方法来声明函数原型和结构 以下是我到目前为止的想法: main.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); ... }
#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