【问题标题】:Exposing only void pointers in an API在 API 中仅公开 void 指针
【发布时间】:2014-02-13 00:08:09
【问题描述】:

我见过很多 C 库,它们不会将它们在内部处理的对象表示为不同的类型,而是在让您访问它们之前用 void 指针包装它们。在实践中,“私有”源文件看起来像:

typedef struct {
    int value;
} object;

void * object_new(void)
{
    object *o = malloc(sizeof(object));
    o->value = 1;
    return o;
}

int object_get(void *o)
{
    return (object *)o->value;
}

void * object_free(void *o)
{
    free(o);
}

在主标题中你只有:

void * object_new(void);
int object_get(void *o);
void * object_free(void *o);

现在我想知道:他们这样做有什么特别的原因吗?如果想法是确保 API 用户无法访问对象的内部,那么仅在主库标头中公开类型名称并隐藏底层结构的细节(或其他任何内容)是否足够?实际对象)在实现文件中?

【问题讨论】:

  • 你知道有什么开源库可以做到这一点吗?我对这种模式很好奇,如果有真实世界的例子可供参考,那就太好了。
  • 这个数据库库,例如:github.com/pmwkaa/sophia。它只公开常量,并且它在内部处理的三个主要对象(数据库、游标和“环境”)只能通过 void 指针访问,尽管它们在实现中适当地进行了类型定义。
  • @MBlanc GLib 很大

标签: c api void


【解决方案1】:

隐藏void 指针后面的类型的原因可能是(被误导的)试图隐藏(在modular programming 的意义上)内部细节。这很危险,因为它会抛出编译器可能会立即执行的任何类型检查。

更好的是:

for-user.h:

struct internalstuff;
void somefunc(struct internalstuff *p);

for-internal-use.h:

#include "for-user.h"

struct internalstuff { ...};

implementation.c:

#include "for-internal-use.h";


void somefunc(struct internalstuff *p)
{
   ...
}

这样,没有人会将internalstuff 与随机字符串或malloc(3) 的原始结果混淆,而不会得到至少一个警告。只要您只在 C 中提及指向 struct internalstuff 的指针,手头上没有 struct 的定义就可以了。

使用class 可以在C++ 中完成相同的操作,如果Objective C 不允许这样做,我会感到惊讶。但是面向对象的编程语言有自己的、更灵活的工具来实现这一点。在那里,您可以定义要导出的基本基类,同时使用内部扩展。请查看一本不错的 C++ 书籍以了解详细信息(此处有大量列表)。

【讨论】:

  • 事实上,你指出的正是最初让我感到困惑的地方,那就是:为什么要忽略类型信息?但我认为这种模式背后有一些我不知道的“聪明”东西。
【解决方案2】:

在对象世界(Obj-C 和 C++)中,我相信原因主要与继承有关。如果从基类创建子类,那么在创建该类的新实例时返回值的类型没有问题。仅使用直接 C,似乎没有明确的原因,因为没有透露内部细节或创建依赖项。

【讨论】:

    【解决方案3】:

    你是对的.. 在大多数情况下的想法是限制 API 用户访问对象的内部。虽然类型名称的决定实际上只是风格问题。如果您按照建议在标头中公开类型名称(某些 API 会这样做),它可能看起来像:

    typedef void* object;
    

    从编译器的角度来看,这样做并没有真正的优势或劣势。虽然它确实让 API 用户更好地了解正在发生的事情:

    object object_new(void);
    int object_get(object o);
    void object_free(object o);
    

    【讨论】:

    • 这给了读者提示,但编译器不会在意。仍然很危险,也许更多是因为读者没有意识到潜在的麻烦。
    • 对,这只提供可读性。不过,我喜欢你更好地使用结构的建议;我将来肯定会使用这种模式。不过,读者总是能够在 C 中做危险的事情。人们可以尝试通过强制转换来完成这些事情,这真是太神奇了。
    猜你喜欢
    • 1970-01-01
    • 2014-05-30
    • 2013-12-18
    • 1970-01-01
    • 2020-04-01
    • 2021-07-22
    • 2011-04-01
    相关资源
    最近更新 更多