Redefine a C function in C++

被刻印的时光 ゝ 提交于 2020-01-16 14:03:06

问题


I have a C library with a function

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

exitWithError is exclusively called by other C code internal to the library. The C library is always compiled in "C mode", i.e. via g++ -x c ..., conforming to C99, so cannot feature any C++ code. (I've realised now this makes the #ifdef __cplusplus check completely superfluous, but ho-hum, not relevant here).

I'm seeking to redefine exitWithError in a C++14 project which uses this C library, so that I can change how errors are handled (see my related question). This would change the C library's internal behaviour. I am trying to avoid change to the underlying C library code, but that's not a strict necessity.

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

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

This works great, but features the C exitWithError function calling printf. I wish to remove that behaviour, so nothing is printed when the C library invokes exitWithError.

Trying to redefine exitWithError with the same trick...

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

causes a duplicate symbol error during compiling, naturally.

What about exit enabled it to be redefined? Is there any way to "undefine" exitWithError so that the calling C++ code can redefine it?

I suspect I could pass an additional compiler flag (e.g. -DSILENCE_FOOL) when compiling the C library for use in my C++ project, and modify the C library code to

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

but I'd prefer a method without any modification to the C code.

And finally, 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? It's a function invoked when the user supplies incorrect arguments (in my C++ code, I'll override it with std::invalid_argument).


回答1:


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

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

exit is an identifier reserved by the C standard library, and void exit(int) is a function signature reserved by thexitWithErrore C++ standard library. So, unless your target system is freestanding, this definition violates the language standard.

Trying to redefine exitWithError with the same trick...

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

This violates the one definition rule. There may be at most one definition for a function (exceptions apply, but none that are of help to you).

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

Nothing in standard C or C++.

What about exit enabled it to be redefined?

Some compilers, such as GCC, have a feature called "weak symbol"s, which allows the linker to choose from multiple different definitions. The C standard library that you used probably defines exit as such weak symbol.

You could use the same technique to declare the C library definition of exitWithError weak. But like the other solutions, this also requires modification of the library. Note that this approach is non-standard both when replacing exit and when replacing the library function.


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?

You can introduce polymorphism with a function pointer. Example:

// 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;

It is generally a bad idea to throw in a function called from C. C has no exceptions and C functions are generally not exception safe. It is quite typical for this to result in memory or other resource leak.

In this particular case, there's probably no attempt to release resources after call to exitWithError anyway since it's expected to terminate the process. If you don't attempt to continue the process after catching the exception, then you probably don't need to care about a leak.



来源:https://stackoverflow.com/questions/58595394/redefine-a-c-function-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!