用C不透明的数据类型不透明指针 [英] Opaque Data Type in C without opaque pointer

查看:312
本文介绍了用C不透明的数据类型不透明指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个不透明的类型,而类型仍然允许用户如对其进行实例化堆栈通过键入上

 结构美孚OBJ;
/*...*/
Foo_init(放大器; OBJ);
Foo_do_something(放大器; OBJ,参数);
/*...*/

惯用的不透明指针方法不允许该实施例的第一行。
作为一种变通方法,我把一个公共不透明的,但数据数组中的公共头文件中
具有固定大小。这似乎对几个例子的工作,但我有点不确定两点:


  1. 它是安全的,因为在Foo_get_bar和Foo_set_bar做投数据数组的地址?这工作好在我的测试,但它看起来可疑。


  2. 如果FOO_DATA_SIZE保持不变是合理的预期用户code ABI兼容性?



main.c中(例如用户code)

 的#include<&stdio.h中GT;
#包括LT&;&limits.h中GT;
的#includefoo.h中诠释主(){
    结构富富;
    Foo_init(安培;富);
    Foo_set_bar(安培;富,INT_MAX);
    INT吧= Foo_get_bar(安培;富);
    的printf(得到巴数:%d \\ n,吧);
}


foo.h中(公头)

 的#pragma一次
#包括LT&;&inttypes.h GT;
#定义FOO_DATA_SIZE(64)结构美孚{
    uint8_t有数据[FOO_DATA_SIZE]
};无效Foo_init(结构美孚* F);
无效Foo_set_bar(结构美孚*楼INT barval);
INT Foo_get_bar(结构美孚* F);


的foo.c(试行)

 的#includefoo.h中
#包括LT&;&stdio.h中GT;
#包括LT&;&string.h中GT;
#包括LT&;&inttypes.h GT;类型定义的int64_t bar_t;结构Foo_private {
    bar_t吧;
};_Static_assert(的sizeof(结构Foo_private)LT = FOO_DATA_SIZE,
    FOO_DATA_SIZE不足以结构Foo_private);无效Foo_init(结构美孚*富){
    结构Foo_private foodata;
    foodata.bar =(bar_t)0;
    的memcpy(foo->数据&安培; foodata,的sizeof(结构Foo_private));
}无效Foo_set_bar(结构美孚*富,诠释barval){
    结构Foo_private * foodata =(无效*)及(foo->数据);
    foodata->巴=(bar_t)barval;
    诠释存储=(int)的foodata->酒吧;
    如果(存储!= barval){
        fprintf中(标准错误,Foo_set_bar(%PRId64):警告:酒吧四舍五入至%PRId64\\ n
            (的int64_t)barval,(的int64_t)存储);
    }
}INT Foo_get_bar(结构美孚*富){
    结构Foo_private * foodata =(无效*)及(foo->数据);
    bar_t栏= foodata->酒吧;
    回报(INT)栏;
}


解决方案

我已经审查了这些职位的信息:

<一个href=\"http://stackoverflow.com/questions/17619015/why-one-should-not-hide-a-structure-implementation-that-way\">Why人们不应该隐瞒的结构实现呀?

不透明数据类型的静态分配

,以及评论。我觉得我有问题的解答:我转向使用不透明的指针类型,但我现在露出一个函数调用,它告诉用户这是多么大,这样他可以调用alloca来的malloc或任何以分配的空间。基本上要求是分配由用户执行,而不是执行

修改标题:

 的#pragma一次
#包括LT&;&inttypes.h GT;结构美孚;
typedef结构富富;无效Foo_init(美孚* F);
无效Foo_set_bar(富*楼INT barval);
INT Foo_get_bar(美孚* F);
为size_t Foo_data_size(无效);#定义Foo_alloca()的alloca(Foo_data_size())
#定义Foo_new()的malloc(Foo_data_size())
#定义Foo_delete(PTR)做{\\
    免费(PTR); \\
    PTR = NULL; \\
    }而(0)

修改实现定义:

 的typedef的int64_t bar_t;结构美孚{
    挥发性bar_t吧;
};无效Foo_init(结构美孚*富){
    结构美孚foodata;
    foodata.bar =(bar_t)0;
    的memcpy(富,和放大器; foodata,Foo_data_size());
}为size_t Foo_data_size(){
    返回的sizeof(结构美孚);
}// ...

然后在用户code,用,alloca与Foo_data_size提供的尺寸()或使用一个方便的宏。

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

不相关的私人struct声明挥发性字。如果没有声明是这样,GCC,至少在Win32试图优化掉在不兼容的重新presentations存储特定的值我的约束检查。

实例:

 的#includefoo.h中// ...{
    美孚*富= Foo_alloca();
    Foo_init(富);
    Foo_set_bar(富,INT_MAX);
    INT吧= Foo_get_bar(富);
    的printf(得到巴数:%d \\ n,吧);
}

I'd like to create an opaque type while still allowing users of the type to instantiate it e.g. on the stack by typing

struct Foo obj;
/*...*/
Foo_init(&obj);
Foo_do_something(&obj, param);
/*...*/

The customary opaque pointer approach doesn't allow the first line of this example. As a workaround I am placing a public but opaque "data" array inside the public header file with a fixed size. This seems to work for a few examples but I'm a bit unsure about two points:

  1. Is it safe to cast the address of the data array as is done in Foo_get_bar and Foo_set_bar? This works okay in my tests but it looks questionable.

  2. If FOO_DATA_SIZE remains fixed is it reasonable to expect ABI compatibility in user code?


main.c (Example user code)

#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 (Public header)

#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 (Implementation)

#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;
}

解决方案

I've reviewed the information in these posts:

Why one should not hide a structure implementation that way?

Static allocation of opaque data types

as well as the comments. I think I have an answer that works: I switched to using an opaque pointer type but am now exposing a function call which tells the user how large it is so that he can call alloca or malloc or whatever in order to allocate the space. Basically the requirement is that allocation is performed by the user, not to the implementation.

Modified header:

#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)

Modified implementation definition:

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);
}

//...

Then in the user code, use alloca with the size provided by Foo_data_size() or use a convenience macro.

This approach removes the fixed-size limitation and hopefully addresses the alignment issues mentioned.

Unrelated is the word volatile in the private struct declaration. Without declaring it this way, gcc at least on win32 tried to optimize away my constraint checks on storing certain values in incompatible representations.

Example usage:

#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);
}

这篇关于用C不透明的数据类型不透明指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆