在 C++ 中重新定义 C 函数

Redefine a C function in C++

我有一个带有函数的 C 库

#ifdef __cplusplus
extern "C" {
#endif

void exitWithError(const char* func) {
    printf("woopsie in %s", func);
    exit(1);
}

#ifdef __cplusplus
}
#endif

exitWithError 由库内部的其他 C 代码独占调用。 C 库始终在 "C mode" 中编译,即通过 g++ -x c ...,符合 C99,因此不能包含任何 C++ 代码。 (我现在意识到这使得 #ifdef __cplusplus 检查完全多余,但是哼哼,这里不相关)。

我正在寻求在使用此 C 库的 C++14 项目中重新定义 exitWithError,以便我可以更改处理错误的方式(请参阅我的 )。这会改变 C 库的内部行为。我试图避免更改底层 C 库代码,但这不是绝对必要的。

事实证明,在我的调用 C++ 代码中,我可以用

简单地覆盖 exit 的行为
extern "C" void exit(int status) {
    throw 1;
}

这很好用,但具有调用 printf 的 C exitWithError 函数。我希望删除该行为,因此当 C 库调用 exitWithError.

时不会打印任何内容

试图用同样的技巧重新定义 exitWithError...

extern "C" void exitWithError(const char* func) {
    throw 1;
}

在编译过程中自然会导致 duplicate symbol 错误。

exit 可以重新定义吗?有什么方法可以 "undefine" exitWithError 以便调用 C++ 代码可以重新定义它吗?

我怀疑我可以在编译用于我的 C++ 项目的 C 库时传递一个额外的编译器标志(例如 -DSILENCE_FOOL),并将 C 库代码修改为

void exitWithError(const char* func) {
#ifndef SILENCE_FOOL
    printf("woopsie in %s", func);
#endif
    exit(1);
}

但我更喜欢不对 C 代码进行任何修改的方法。

最后,是否有更明智的方法(重构 C 库)允许 C 库的用户 "hook in" 或覆盖 exitWithError 行为?这是一个当用户提供不正确的参数时调用的函数(在我的 C++ 代码中,我将用 std::invalid_argument 覆盖它)。

I can, in my calling C++ code, simply override the behaviour of exit with

extern "C" void exit(int status) {
    throw 1; }

exit是C标准库保留的标识符,void exit(int)是xitWithError C++标准库保留的函数签名。所以,除非你的目标系统是独立的,否则这个定义违反了语言标准。

Trying to redefine exitWithError with the same trick...

extern "C" void exitWithError(const char* func) {
    throw 1;
}

这违反了一个定义规则。一个函数最多只能有一个定义(例外情况适用,但 none 对您有帮助)。

Is there any way to "undefine" exitWithError so that the calling C++ code can redefine it?

标准 C 或 C++ 中没有任何内容。

What about exit enabled it to be redefined?

一些编译器,例如 GCC,有一个叫做 "weak symbol"s 的特性,它允许链接器从多个不同的定义中进行选择。您使用的 C 标准库可能将 exit 定义为此类弱符号。

您可以使用相同的技术来声明 exitWithError weak 的 C 库定义。但与其他解决方案一样,这也需要修改库。请注意,这种方法在替换 exit 和替换库函数时都是非标准的。


is there a more sensible way (refactoring the C library) to allow users of the C library to "hook in" or override the exitWithError behaviour?

可以用函数指针引入多态性。示例:

// in library (C):
void exitWithErrorDefault(const char* func) {
    printf("woopsie in %s", func);
    exit(1);
}

typedef void errorHandler_f(const char*);
errorHandler_f* exitWithErrorHandler = exitWithErrorDefault;

void exitWithError(const char* func) {
    exitWithErrorHandler(func);
}

// usage (C++)
void exitWithErrorCpp(const char* func) {
    throw 1; // bad
}
int main()
{
    exitWithErrorHandler = exitWithErrorCpp;

从 C 调用的函数中抛出异常通常不是一个好主意。C 没有异常,而且 C 函数通常不是异常安全的。这通常会导致内存或其他资源泄漏。

在这种特殊情况下,可能不会尝试在调用 exitWithError 之后释放资源,因为它预计会终止进程。如果您在捕获异常后不尝试继续该过程,那么您可能不需要关心泄漏。