问题
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