不透明数据类型在C中没有不透明指针

时间:2013-07-14 19:29:46

标签: c

我想创建一个opaque类型,同时仍然允许该类型的用户实例化它,例如通过键入

在堆栈上
struct Foo obj;
/*...*/
Foo_init(&obj);
Foo_do_something(&obj, param);
/*...*/

通常的不透明指针方法不允许此示例的第一行。 作为一种解决方法,我正在公开但不透明的数据"公共头文件中的数组 具有固定的大小。这似乎适用于一些例子,但我有点不确定两点:

  1. 像Foo_get_bar和Foo_set_bar一样,转换数据数组的地址是否安全?这在我的测试中没问题,但看起来有问题。

  2. 如果FOO_DATA_SIZE保持不变,那么在用户代码中期望ABI兼容性是否合理?


  3. main.c(示例用户代码)

    #include <stdio.h>
    #include <limits.h>
    #include "foo.h"
    
    int main() {
        struct Foo foo;
        Foo_init(&foo);
        Foo_set_bar(&foo, INT_MAX);
        int bar = Foo_get_bar(&foo);
        printf("Got bar: %d\n", bar);
    }
    

    foo.h(公共标题)

    #pragma once
    #include <inttypes.h>
    #define FOO_DATA_SIZE (64)
    
    struct Foo {
        uint8_t data[FOO_DATA_SIZE];
    };
    
    void Foo_init(struct Foo *f);
    void Foo_set_bar(struct Foo *f, int barval);
    int Foo_get_bar(struct Foo *f); 
    

    foo.c(实施)

    #include "foo.h"
    #include <stdio.h>
    #include <string.h>
    #include <inttypes.h>
    
    typedef int64_t bar_t;
    
    struct Foo_private {
        bar_t bar;
    };
    
    _Static_assert(sizeof(struct Foo_private) <= FOO_DATA_SIZE,
        "FOO_DATA_SIZE is insufficient for struct Foo_private");
    
    void Foo_init(struct Foo *foo) {
        struct Foo_private foodata;
        foodata.bar = (bar_t)0;
        memcpy(foo->data, &foodata, sizeof(struct Foo_private));
    }
    
    void Foo_set_bar(struct Foo *foo, int barval) {
        struct Foo_private *foodata = (void*)&(foo->data);
        foodata->bar = (bar_t)barval;
        int stored = (int)foodata->bar;
        if (stored != barval) {
            fprintf(stderr, "Foo_set_bar(%"PRId64"): warning: bar rounded to %"PRId64"\n", 
                (int64_t)barval, (int64_t)stored);
        }
    }
    
    int Foo_get_bar(struct Foo *foo) {
        struct Foo_private *foodata = (void*)&(foo->data);
        bar_t bar = foodata->bar;
        return (int)bar;
    }
    

1 个答案:

答案 0 :(得分:4)

我已经查看了这些帖子中的信息:

Why one should not hide a structure implementation that way?

Static allocation of opaque data types

以及评论。我认为我有一个有效的答案:我改用了一个不透明的指针类型,但我现在正在暴露一个函数调用,告诉用户它有多大,以便他可以调用alloca或malloc或其他任何东西来分配空间。基本上要求是分配由用户执行,而不是执行。

修改标题:

#pragma once
#include <inttypes.h>

struct Foo;
typedef struct Foo Foo;

void Foo_init(Foo *f);
void Foo_set_bar(Foo *f, int barval);
int Foo_get_bar(Foo *f); 
size_t Foo_data_size(void);

#define Foo_alloca() alloca(Foo_data_size())
#define Foo_new()  malloc(Foo_data_size())
#define Foo_delete(PTR) do { \
    free(PTR); \
    PTR = NULL; \
    } while(0)

修改后的实施定义:

typedef int64_t bar_t;

struct Foo {
    volatile bar_t bar;
};

void Foo_init(struct Foo *foo) {
    struct Foo foodata;
    foodata.bar = (bar_t)0;
    memcpy(foo, &foodata, Foo_data_size());
}

size_t Foo_data_size() {
    return sizeof(struct Foo);
}

//...

然后在用户代码中,使用具有Foo_data_size()提供的大小的alloca或使用便利宏。

这种方法消除了固定大小的限制,并希望解决上面提到的对齐问题。

私有结构声明中的单词volatile是不相关的。如果不以这种方式声明,gcc至少在win32上试图优化我在不兼容的表示中存储某些值的约束检查。

使用示例:

#include "foo.h"

//...

{
    Foo *foo = Foo_alloca();
    Foo_init(foo);
    Foo_set_bar(foo, INT_MAX);
    int bar = Foo_get_bar(foo);
    printf("Got bar: %d\n", bar);
}