C中的可重入库设计

Vin*_*vic 6 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*);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

/* library handles all state data allocation */
static int* quux_state; 
static char** quux_address;
Run Code Online (Sandbox Code Playgroud)

在下面的例子中,客户端能够搞乱状态,非常不受欢迎

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

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

Jos*_*phH 21

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

即.在.h头文件中放:

typedef struct QuuxState QuuxState;

QuuxState *spork_quux(FILE*);
Run Code Online (Sandbox Code Playgroud)

并在.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;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是:

  1. 您可以更改结构的内容,而无需重新编译所有客户端代码
  2. 客户端代码无法访问成员,即.quuxState-> state会给编译器错误
  3. 调试器仍然可以完全看到QuuxState结构,因此您可以轻松地查看值并设置观察点等.
  4. 不需要铸造
  5. 你要返回的类型是一个特定的类型,所以你会得到一些编译器检查正确的东西被传递(与void*指针相比)

唯一的缺点是你必须分配一块内存 - 但是假设你的库正在做任何不重要的事情(如果它正在进行文件I/O,那肯定是非平凡的),单个malloc的开销可以忽略不计.

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

void QuuxSpork_readLine(QuuxState *state)
{
    ....
}

void QuuxSpork_destroy(QuuxState *state)
{
    free(state);
}
Run Code Online (Sandbox Code Playgroud)

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

  • +1这几乎是C中用于封装对象的标准模式.你会看到它被遍地使用.很好地解释了. (4认同)
  • 很好的回答,约瑟夫!+1 (2认同)
  • Re:'劣势':代码可以静态分配一个struct QuuxState结构数组,每次返回一个新结构直到它用完为止.当程序完成spuxing quux时,它会调用'unspork_quux()'或其他东西来返回值.这对可以一次喷出的quux的数量设置了有限的限制.这仍然是一个问题,然后您可以在用完静态分配后动态分配,在测试返回指针是否位于静态数组的边界内时,运行未定义行为的(名义上的而非实际的)风险. (2认同)