Переопределить функцию C в C ++ - PullRequest
0 голосов
/ 28 октября 2019

У меня есть библиотека 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», то есть через g++ -x c ..., в соответствии с C99, поэтому не может содержать никакого кода C ++. (Теперь я понял, что это делает проверку #ifdef __cplusplus полностью излишней, но хо-хум, здесь неактуальна).

Я пытаюсь переопределить exitWithError в проекте C ++ 14, который используетэта библиотека C, так что я могу изменить способ обработки ошибок (см. мой связанный вопрос ). Это изменит внутреннее поведение библиотеки C. Я пытаюсь избежать изменений в базовом коде библиотеки C, но это не является строгой необходимостью.

Оказывается, я могу в своем коде вызова C ++ просто переопределить поведение exit с помощью

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

Это прекрасно работает, но имеет функцию C exitWithError, вызывающую printf. Я хочу удалить это поведение, поэтому ничего не печатается, когда библиотека C вызывает exitWithError.

Попытка переопределить exitWithError с помощью того же трюка ...

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

вызываетduplicate symbol ошибка во время компиляции, естественно.

А как же exit позволил переопределить его? Есть ли способ "undefine" exitWithError, чтобы вызывающий код C ++ мог его переопределить?

Я подозреваю, что мог бы передать дополнительный флаг компилятора (например, -DSILENCE_FOOL) при компиляции библиотеки C для использования вмой проект C ++, и измените код библиотеки C на

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

, но я бы предпочел метод без каких-либо изменений кода C.

И, наконец, есть ли более разумный способ(рефакторинг библиотеки C), чтобы позволить пользователям библиотеки C "подключиться" или переопределить поведение exitWithError? Эта функция вызывается, когда пользователь вводит неверные аргументы (в моем коде C ++ я переопределю его с std::invalid_argument).

1 Ответ

2 голосов
/ 28 октября 2019

Я могу в своем вызывающем коде C ++ просто переопределить поведение выхода с помощью

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

exit - идентификатора, зарезервированного стандартной библиотекой C, и void exit(int) является сигнатурой функции, зарезервированной стандартной библиотекой xitWithErrore C ++. Таким образом, если ваша целевая система не является автономной, это определение нарушает языковой стандарт.

Попытка переопределить exitWithError с помощью того же трюка ...

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

Это нарушаетодно определение правила. Для функции может быть не более одного определения (применимы исключения, но ни одно из них вам не поможет).

Есть ли способ «отменить» exitWithError, чтобы вызывающий код C ++ мог переопределитьэто?

Ничего в стандартном C или C ++.

А как насчет exit, позволяющего переопределить его?

Некоторые компиляторы, такие какGCC имеет функцию, называемую «слабым символом», которая позволяет компоновщику выбирать из множества разных определений. Используемая вами стандартная библиотека C, вероятно, определяет exit как такой слабый символ.

Вы можете использовать ту же технику, чтобы объявить определение библиотеки C слабым exitWithError. Но, как и другие решения, это также требует модификации библиотеки. Обратите внимание, что этот подход нестандартен как при замене exit, так и при замене библиотечной функции.


есть более разумный способ (рефакторинг библиотеки C), чтобы позволить пользователямБиблиотека C для «подключения» или переопределения поведения exitWithError?

Вы можете ввести полиморфизм с помощью указателя на функцию. Пример:

// 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, так как ожидается, что процесс завершится. Если вы не пытаетесь продолжить процесс после перехвата исключения, то вам, вероятно, не нужно заботиться о утечке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...