我有一个C项目,旨在将其移植到各种(PC和嵌入式)平台上。
应用程序代码将使用各种调用,这些调用将具有特定于平台的实现,但共享一个通用的(通用)API以帮助实现可移植性。我试图确定最合适的方法来声明函数原型和结构。
到目前为止,这是我想出的:
main.c:
#include "generic.h"
int main (int argc, char *argv[]) {
int ret;
gen_t *data;
ret = foo(data);
...
}
generic.h :(与平台无关的包含)
typedef struct impl_t gen_t;
int foo (gen_t *data);
impl.h :(特定于平台的声明)
#include "generic.h"
typedef struct impl_t {
/* ... */
} gen_t;
impl.c :(特定于平台的实现)
int foo (gen_t *data) {
...
}
建造:
gcc -c -fPIC -o platform.o impl.c
gcc -o app main.c platform.o
现在,这似乎可以正常工作……因为它可以编译。但是,我通常不标记结构,因为它们从未在typedef
'd别名之外访问。这是一个小巧的选择,但是我想知道是否有一种方法可以用匿名结构实现相同的效果?
我也要求后代,因为我搜索了一段时间,发现的最接近的答案是:(Link)
在我的情况下,这不是正确的方法,因为应用程序明确地不应直接包含实现标头-关键是要使程序与平台脱钩。
我看到了其他几种不太理想的方法来解决此问题,例如:
generic.h:
#ifdef PLATFORM_X
#include "platform_x/impl.h"
#endif
/* or */
int foo (struct impl_t *data);
这些似乎都没有特别吸引人,而且绝对不是我的风格。尽管我不想游刃有余,但是当可能有更好的方法来实现我所想的时,我也不希望样式冲突。所以我认为typedef解决方案是在正确的轨道上,而这只是我剩下的struct标签。
有什么想法吗?
您当前的技术是正确的。尝试使用匿名(未标记)功能将struct
失败,您将不得不做的事情–您必须公开struct
到处定义的详细信息,这意味着您不再拥有不透明的数据类型。
在一条评论中,user3629249说:
头文件包含的顺序意味着文件有对结构的前向引用
generic.h
;也就是说,在定义结构之前,先使用它。这不太可能编译。
对于问题中显示的标题,这种观察是不正确的。它对于示例main()
代码是准确的(直到添加此响应之前我才注意到)。
关键是显示的接口函数采用或返回类型的指针,而类型gen_t
又映射到struct impl_t
指针。只要客户代码不需要为该结构分配空间,也不需要取消对结构的指针的引用来访问该结构的成员,则客户代码不需要知道该结构的细节。将结构类型声明为存在就足够了。您可以使用其中任何一个来声明存在struct impl_t
:
struct impl_t;
typedef struct impl_t gen_t;
后者还引入gen_t
了类型的别名struct impl_t
。另请参见C标准的哪一部分可以编译此代码?和C标准是否认为struct uperms
此标头中存在一种或两种条目类型?
问题中的原始main()
程序是:
int main (int argc, char *argv[]) {
int ret;
gen_t data;
ret = foo(&data);
…
}
此代码不能以gen_t
不透明(非指针)类型进行编译。可以正常工作:
typedef struct impl_t *gen_t;
它不能与以下内容一起编译:
typedef struct impl_t gen_t;
因为编译器必须知道为分配正确的空间的结构的data
大小,但是编译器无法通过定义不透明类型来知道该大小。(请参阅对typedef
指针的一个好主意吗?有关对结构的指针的类型定义。)
因此,main()
代码应类似于:
#include "generic.h"
int main(int argc, char **argv)
{
gen_t *data = bar(argc, argv);
int ret = foo(data);
...
}
其中(对于本示例)bar()
被定义为extern gen_t *bar(int argc, char **argv);
,因此它返回一个指向不透明类型的指针gen_t
。
对于始终使用名称struct tagname
还是使用a更好typedef
。Linux内核是不使用该typedef
机制的大量代码体。所有结构都是明确的struct tagname
。另一方面,C ++消除了对显式的需求typedef
;写作:
struct impl_t;
在C ++程序中,意味着名称impl_t
现在是类型的名称。由于不透明的结构类型需要一个标记(否则最终会用到void *
所有东西,这在很多方面都是不好的,但是主要原因是使用会失去所有类型的安全性void *
;请记住,typedef
为基础类型引入别名,而不是一种新的独特类型),我用C编写的代码模拟C ++:
typedef struct Generic Generic;
我避免_t
在我的类型上使用后缀,因为POSIX保留了_t
供实现使用*的实现(另请参见类型后跟_t
代表什么?)。您可能很幸运,可以摆脱它。我已经在代码库上工作过,其中的类型类似于dec_t
和loc_t
由代码库定义(它不是实现的一部分,其中“实现”是指C编译器及其支持的代码,或C库及其支持的代码),这两种类型都造成了数十年的痛苦,因为移植代码的某些系统定义了这些类型,这也是系统的特权。我设法摆脱的名字之一;其他我没有。'真痛苦!如果必须使用_t
(这是指示某种类型的一种简便方法),我建议也使用一个独特的前缀:例如,对于pqr_typename_t
某些项目pqr
。
*请参阅POSIX标准中“名称空间”中第二个表的底行。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句