标题中的匿名前向声明冲突

时间:2014-09-19 17:39:11

标签: c gcc clang c99 forward-declaration

编辑:将foo_t更改为foo作为类型名称,因为POSIX保留以_t结尾的类型 编辑:将_foo_s更改为foo_s,因为C声明以下划线开头的名称

我对于同时拥有以下内容的最佳方式感到困惑:

  1. 库实现会看到struct成员,但库的用户不会
  2. 编译器检查标头中的函数定义是否与实现匹配
  3. 使用C99
  4. 我对此的第一次尝试是做以下事情:

    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中知道。

    当我尝试使用include-what-you-use使用clang时,我开始闻到上述奇怪的东西。当我用它来检查foo.c时,它通知我foo.h不应包含struct foo_s的前向声明。我认为这是iwyu中的一个错误,因为对于任何包含foo.h的人来说,这显然不会有问题。

    此时让我从一开始就提出第二个要求。 foo.c包含foo.h,以便编译器可以确保foo.h中声明的每个函数都与foo.c中的实现相匹配。我想我需要这个,因为我经常遇到分段错误,因为我的实现的函数签名与其他代码使用的头文件中的函数签名不匹配。

    后来我尝试用clang编译代码(我用-Wall -Wextra -Werror编译)并被告知:

    error: redefinition of typedef 'foo' is a C11 feature
    

    我不希望我的代码依赖于C11功能,我确实希望确保公共标头中的函数与实现相匹配。我该如何解决?

    我看到了将foo.h分为foo.hfoo_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应该感到高兴。这也意味着我的函数的实现被检查以匹配标题。

    但是虽然这有效,但我想知道这是否是最佳解决方案,因为:

    • 只有两行头文件
    • 似乎是浪费
    • 我不知道其他类似的项目(查看我的/ usr / include我也看不到任何一个)

    那么解决方案是什么才能满足顶部列出的三个criterea?或者是我找到的解决方案?

1 个答案:

答案 0 :(得分:4)

赞赏数据隐藏的崇高意图!

以下情况如何?

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 */
}