简体   繁体   English

如何在通用标头中类型定义实现定义的结构?

[英]How do I typedef an implementation-defined struct in a generic header?

I have a C project that is designed to be portable to various (PC and embedded) platforms. 我有一个C项目,旨在将其移植到各种(PC和嵌入式)平台上。

Application code will use various calls that will have platform-specific implementations, but share a common (generic) API to aid in portability. 应用程序代码将使用各种调用,这些调用将具有特定于平台的实现,但共享一个通用的(通用)API以帮助实现可移植性。 I'm trying to settle on the most appropriate way to declare the function prototypes and structures. 我试图确定最合适的方法来声明函数原型和结构。

Here's what I've come up with so far: 到目前为止,这是我想出的:

main.c: main.c:

#include "generic.h"

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

    ret = foo(data);
    ...
}

generic.h: (platform-agnostic include) generic.h :(与平台无关的包含)

typedef struct impl_t gen_t;

int foo (gen_t *data);

impl.h: (platform-specific declaration) impl.h :(特定于平台的声明)

#include "generic.h"

typedef struct impl_t {
    /* ... */
} gen_t;

impl.c: (platform-specific implementation) impl.c :(特定于平台的实现)

int foo (gen_t *data) {
    ...
}

Build: 建立:

gcc -c -fPIC -o platform.o impl.c
gcc -o app  main.c platform.o

Now, this appears to work... in that it compiles OK. 现在,这似乎可以正常工作……因为它可以编译。 However, I don't usually tag my structures since they're never accessed outside of the typedef 'd alias. 但是,我通常不标记我的结构,因为它们从未在typedef d别名之外访问。 It's a small nit-pick, but I'm wondering if there's a way to achieve the same effect with anonymous structs? 这是一个小巧的选择,但是我想知道是否有一种方法可以使用匿名结构实现相同的效果?

I'm also asking for posterity, since I searched for a while and the closest answer I found was this: ( Link ) 我也要求后代,因为我搜索了一段时间,发现的最接近的答案是:( Link

In my case, that wouldn't be the right approach, as the application specifically shouldn't ever include the implementation headers directly -- the whole point is to decouple the program from the platform. 在我的情况下,这不是正确的方法,因为应用程序明确不应该直接包含实现标头-关键是要使程序与平台脱钩。

I see a couple of other less-than-ideal ways to resolve this, for example: 我看到了其他几种不太理想的方法来解决此问题,例如:

generic.h: generic.h:

#ifdef PLATFORM_X
#include "platform_x/impl.h"
#endif

/* or */

int foo (struct impl_t *data);

Neither of these seems particularly appealing, and definitely not my style. 这些似乎都不是特别吸引人,而且绝对不是我的风格。 While I don't want to swim upstream, I also don't want conflicting style when there might be a nicer way to implement exactly what I had in mind. 虽然我不想游刃有余,但是当可能有更好的方法来实现我所想的时,我也不希望样式冲突。 So I think the typedef solution is on the right track, and it's just the struct tag baggage I'm left with. 所以我认为typedef解决方案是在正确的轨道上,而这只是我剩下的struct标签。

Thoughts? 有什么想法吗?

Your current technique is correct. 您当前的技术是正确的。 Trying to use an anonymous (untagged) struct defeats what you're trying to do — you'd have to expose the details of definition of the struct everywhere, which means you no longer have an opaque data type. 尝试使用匿名(未标记) struct破坏您要执行的操作-您必须在各处公开struct定义的详细信息,这意味着您不再具有不透明的数据类型。


In a comment , user3629249 said: 在一条评论中user3629249说:

The order of the header file inclusions means there is a forward reference to the struct by the generic.h file; 头文件包含的顺序意味着generic.h文件对结构的前向引用; that is, before the struct is defined, it is used. 也就是说,在定义结构之前,先使用它。 It is unlikely this would compile. 这不太可能编译。

This observation is incorrect for the headers shown in the question; 对于问题中显示的标题,这种观察是不正确的。 it is accurate for the sample main() code (which I hadn't noticed until adding this response). 它对于示例main()代码是准确的main()直到添加此响应之前我才注意到)。

The key point is that the interface functions shown take or return pointers to the type gen_t , which in turn maps to a struct impl_t pointer. 关键是所显示的接口函数采用或返回指向gen_t类型的指针,该指针又映射到struct impl_t指针。 As long as the client code does not need to allocate space for the structure, or dereference a pointer to a structure to access a member of the structure, the client code does not need to know the details of the structure. 只要客户代码不需要为该结构分配空间,或取消引用结构的指针来访问该结构的成员,客户代码就不需要知道该结构的细节。 It is sufficient to have the structure type declared as existing. 将结构类型声明为存在就足够了。 You could use either of these to declare the existence of struct impl_t : 您可以使用其中任何一个来声明struct impl_t的存在:

struct impl_t;

typedef struct impl_t gen_t;

The latter also introduces the alias gen_t for the type struct impl_t . 后者还引入了别名gen_t该类型struct impl_t See also Which part of the C standard allows this code to compile? 另请参见C标准的哪个部分允许编译此代码? and Does the C standard consider that there are one or two struct uperms entry types in this header? C标准是否认为此标头中存在一种或两种struct uperms条目类型?

The original main() program in the question was: 问题中原始的main()程序是:

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

    ret = foo(&data);
    …
}

This code cannot be compiled with gen_t as an opaque (non-pointer) type. 不能使用gen_t作为不透明(非指针)类型来编译此代码。 It would work OK with: 可以正常工作:

typedef struct impl_t *gen_t;

It would not compile with: 它不能与以下内容一起编译:

typedef struct impl_t gen_t;

because the compiler must know how big the structure is to allocate the correct space for data , but the compiler cannot know that size by definition of what an opaque type is. 因为编译器必须知道为data分配正确空间的结构有多大,但是编译器无法通过定义不透明类型来知道该大小。 (See Is it a good idea to typedef pointers? for typedefing pointers to structures.) (请参阅typedef指针是一个好主意吗?有关结构的typedef指针 。)

Thus, the main() code should be more like: 因此, main()代码应该更像:

#include "generic.h"

int main(int argc, char **argv)
{
    gen_t *data = bar(argc, argv);
    int ret = foo(data);
    ...
}

where (for this example) bar() is defined as extern gen_t *bar(int argc, char **argv); 其中(对于此示例) bar()被定义为extern gen_t *bar(int argc, char **argv); , so it returns a pointer to the opaque type gen_t . ,因此它返回一个指向不透明类型gen_t的指针。

Opinion is split over whether it is better to always use struct tagname or to use a typedef for the name. 对于始终使用struct tagname还是将typedef用作名称更好是存在分歧的。 The Linux kernel is one substantial body of code that does not use the typedef mechanism; Linux内核是不使用typedef机制的大量代码体。 all structures are explicitly struct tagname . 所有结构都是显式struct tagname On the other hand, C++ does away with the need for the explicit typedef ; 另一方面,C ++消除了对显式typedef writing: 写作:

struct impl_t;

in a C++ program means that the name impl_t is now the name of a type. 在C ++程序中,意味着名称impl_t现在是类型的名称。 Since opaque structure types require a tag (or you end up using void * for everything, which is bad for a whole legion of reasons, but the primary reason is that you lose all type safety using void * ; remember, typedef introduces an alias for an underlying type, not a new distinct type), the way I code in C simulates C++: 由于不透明的结构类型需要一个标记(或者您最终在所有内容上都使用void * ,这在很多方面都是不好的,但是主要原因是您使用void *失去了所有类型安全性;请记住, typedef引入了别名)基本类型,而不是新的独特类型),这是我在C语言中编写的代码模拟C ++的方式:

typedef struct Generic Generic;

I avoid using the _t suffix on my types because POSIX reserves the _t for the implementation to use * (see also What does a type followed by _t represent? ). 我避免在类型上使用_t后缀,因为POSIX保留_t供实现使用* (另请参见类型后面紧跟_t代表什么? )。 You may be lucky and get away with it. 您可能很幸运,可以摆脱它。 I've worked on code bases where types like dec_t and loc_t were defined by the code base (which was not part of the implementation — where 'the implementation' means the C compiler and its supporting code, or the C library and its supporting code), and both those types caused pain for decades because some of the systems where the code was ported defined those types, as is the system's prerogative. 我在代码库中工作过,其中代码库定义了诸如dec_tloc_t类的类型(这不是实现的一部分,其中“实现”是指C编译器及其支持的代码,或者C库及其支持的代码),这两种类型都造成了数十年的痛苦,因为移植代码的某些系统定义了这些类型,这也是系统的特权。 One of the names I managed to get rid of; 我设法摆脱的名字之一; the other I didn't. 其他我没有。 'Twas painful! '真痛苦! If you must use _t (it is a convenient way to indicate that something is a type), I recommend using a distinctive prefix too: pqr_typename_t for some project pqr , for example. 如果必须使用_t (这是一种指示某种类型的便捷方法),我建议也使用一个独特的前缀: pqr_typename_t对于某些项目pqr ,使用pqr_typename_t

* See the bottom line of the second table in The Name Space in the POSIX standard. *请参阅POSIX标准中“名称空间”中第二个表的底行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM