C中的可重入库设计

时间:2010-07-13 13:25:21

标签: c encapsulation reentrancy

假设我正在建立一个库,以便在C中使用quuxes。

Quuxes需要两个状态变量才能成功进行sporked:

static int quux_state;
static char* quux_address;

/* function to spork quuxes found in a file, 
   reads a line from the file each time it's called. */
void spork_quux(FILE*);

如果我将这些数据存储为全局变量,那么只有一个客户端能够一次启用quuxes,否则状态变量将被第二个调用者破坏,并可能发生灾难。

问题是在C中设计可重入库的最佳方法是什么?

我接受了以下案件,但没有令人满意的结论。

在下面的例子中,问题是如何将客户关联到每个州?

/* library handles all state data allocation */
static int* quux_state; 
static char** quux_address;

在下列情况下,客户端可以搞乱状态,非常不受欢迎

/* let each client store state */
typedef struct { int state; char* address; } QuuxState; 
QuuxState spork_quux(FILE*);

那么,如何正确地做到这一点?

3 个答案:

答案 0 :(得分:21)

使用结构,但不要将定义公开给客户端。

即。在.h头文件中:

typedef struct QuuxState QuuxState;

QuuxState *spork_quux(FILE*);

并在.c实现文件中:

struct QuuxState
{
    int state;
    char* address;
};

QuuxState *spork_quuxFILE *f)
{
    QuuxState *quuxState = calloc(1, sizeof(*quuxState));
    if (!quuxState)
        return NULL;

    quuxState->state = ....;

    ........

    return quuxState;
}

这种方法的优点是:

  1. 您可以在不重新编译所有客户端代码的情况下更改结构的内容
  2. 客户端代码无法访问成员,即。 quuxState-> state会给出编译器错误
  3. 调试器仍然可以完全看到QuuxState结构,因此您可以轻松地查看值并设置观察点等。
  4. 无需施法
  5. 您返回的类型是特定类型,因此您将获得一些编译器检查是否正在传递正确的内容(与void *指针相比)
  6. 唯一的缺点是你必须分配一块内存 - 但是假设你的库正在做任何不重要的事情(如果它正在进行文件I / O,那肯定是非平凡的),单个malloc的开销可以忽略不计

    您可能希望将上述函数重命名为“QuuxSpork_create”,并添加更多函数来处理执行逐行工作并在完成后销毁状态。

    void QuuxSpork_readLine(QuuxState *state)
    {
        ....
    }
    
    void QuuxSpork_destroy(QuuxState *state)
    {
        free(state);
    }
    

    像这样工作的库的随机示例是POSIX线程库,pthreads。

答案 1 :(得分:3)

使用结构,但不要告诉客户端它是结构。传递一个不透明的指针 - void *,或者更好的指向空虚拟结构的指针 - 并在需要时将其转换回来。

答案 2 :(得分:1)

大多数库函数处理此问题的方法是将状态信息以他们需要的任何数据类型提供给用户。在你的情况下,一个结构。 (采取strtok vs strtok_r)。我相信这开创了一个先例,你应该将它传递给用户。无效*有效。你甚至可以输入它以使它看起来很漂亮。

此外,strtok_r通过编辑命令行参数来完成此操作,而不是返回指向该状态的指针。我希望我使用的任何重入函数都遵循类似的格式。当然,我的大脑已被一些非常疯狂的C代码扭曲了。

void spork_quux(FILE*, QuuxState **);