编辑:将 foo_t 更改为 foo 作为类型名,因为 POSIX 保留以 _t 结尾的类型 编辑:将 _foo_s 更改为 foo_s 因为 C 声明以下划线开头的名称
我对同时拥有以下内容的最佳方法感到困惑:
- 库实现可以看到结构成员,但库的用户看不到
- 编译器检查头文件中的函数定义是否与实现匹配
- 使用 C99
我的第一个尝试是执行以下操作:
foo.h(为简洁起见省略了包含保护):
typedef struct foo_s foo;
struct foo_s;
foo* foo_create(void);
void foo_do_something(foo *foo);
foo.c:
#include "foo.h"
struct foo_s {
/* some_hidden_members */
};
foo* foo_create() {
/* allocate memory etc */
}
void foo_do_something(foo *foo) {
/* do something with foo */
}
这似乎适用于 gcc。包括foo.h
的所有人只看到匿名前向声明和struct foo_s
的真实布局仅在 foo.c
中已知.
当我尝试使用使用 clang 的 include-what-you-use 时,我开始闻到上面的内容有些奇怪。当我用它来检查 foo.c
它告诉我foo.h
不应包含 struct foo_s
的前向声明.我认为这是 iwyu 中的一个错误,因为显然这对包含 foo.h
的任何其他人来说都不是问题。 .
在这一点上让我从头开始我的第二个要求。 foo.c
包括 foo.h
以便编译器可以确保在 foo.h
中声明的每个函数匹配 foo.c
中的实现.我认为我需要这个,因为我经常遇到段错误,因为我的实现的函数签名与其他代码使用的 header 中的签名不匹配。
后来我尝试用 clang 编译代码(我用 -Wall -Wextra -Werror
编译)并被告知:
error: redefinition of typedef 'foo' is a C11 feature
我不希望我的代码依赖于 C11 功能,我确实希望确保公共(public) header 中的函数与实现相匹配。我该如何解决?
我看到一种方法是拆分foo.h
进入foo.h
和 foo_private.h
:
foo.h(为简洁起见省略了包含保护):
struct foo_s;
#include "foo_private.h"
foo_private.h(为简洁起见省略了包含保护):
typedef struct foo_s foo;
foo* foo_create(void);
void foo_do_something(foo *foo);
然后我会包括 foo_private.h
在foo.c
和其他代码将包括 foo.h
.这意味着 foo.c
没有看到 foo_s
的前向声明clang 和 iwyu 应该会很高兴。这也意味着检查我的函数的实现以匹配 header 。
但是虽然这可行,但我想知道这是否是最佳解决方案,因为:
- 一个头文件只有两行似乎是一种浪费
- 我不知道有其他项目是这样做的(在我的/usr/include 中我也没有看到)
那么满足顶部列出的三个标准的解决方案是什么?或者是我找到的解决方案?
最佳答案
对数据隐藏的崇高意图表示敬意!
下面的怎么样?
foo.h(为简洁起见省略了包含保护):
typedef struct foo_t foo_t; // note change 0
// note change 1
foo_t* foo_create(void);
void foo_do_something(foo_t *foo);
foo.c:
#include "foo.h"
struct foo_t { // note change 2
/* some_hidden_members */
};
foo_t* foo_create() {
/* allocate memory etc */
}
void foo_do_something(foo_t *foo) {
/* do something with foo */
}
关于 header 中的匿名前向声明冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25939478/