У меня есть библиотека C, которой более 10 лет, которая, как я полагаю, в старые добрые времена работала нормально, но когда я попытался использовать ее с источником C ++ (содержащим основную функцию) на днях, я столкнулся с некоторые трудности.

Изменить: чтобы уточнить, библиотека C прекрасно компилируется с gcc и генерирует объектный файл old_c_library.o. Эта библиотека должна использоваться таким образом, чтобы заголовочный файл C old_c_library.h был #include d в вашем исходном файле main.c C. Затем ваш основной исходный файл C должен быть скомпилирован и скомпилирован вместе с old_c_library.o через gcc. Здесь я хочу использовать вместо этого исходный файл C ++ main.cpp и скомпилировать / связать его с g++.

Во время компиляции исходного файла C ++ возникли следующие три проблемы:

  1. один из файлов заголовков библиотеки C содержит зарезервированное слово C ++ new (это имя целого числа), что привело к фатальной ошибке; и
  2. один из файлов заголовков библиотеки C содержит вызов calloc (отсутствует явное приведение типа), что привело к фатальной ошибке; и
  3. различные файлы библиотеки C содержат код, в котором происходит сравнение знаковых и беззнаковых целых чисел, что приводит к предупреждению.

Изменить: я попытался использовать "трюк" #extern "C" { #include "obsolete_c_library.h" }, как предлагалось в комментариях, но это не решило ни одной из моих проблем.

Я могу решить проблему 1, переименовав все экземпляры зарезервированных слов и заменив их - в основном - чем-то еще. Я могу решить проблему 2, указав тип вызова calloc. Я мог бы попытаться отсортировать предупреждения по предложенным здесь идеям: Как отключить предупреждения GCC для нескольких строк кода.

Но я все еще задаюсь вопросом, есть ли способ преодолеть эти трудности элегантным высокоуровневым способом, без фактического воздействия на исходную библиотеку?


Соответствующие: Где C не является подмножеством C ++? и Могу ли я привести результат malloc? и Как использовать extern для обмена переменными между исходными файлами ?.

13
Matsmath 26 Апр 2016 в 17:05

2 ответа

Лучший ответ

Вообще говоря, небезопасно добавлять файлы заголовков C #include в исходные тексты C ++, если эти заголовки не были созданы в ожидании такого использования. В некоторых случаях его можно заставить работать, но вы должны быть готовы либо изменить заголовки, либо написать свои собственные объявления для функций и глобальных переменных, к которым вы хотите получить доступ.

Как минимум, если заголовки C объявляют какие-либо функции, и вы не перекомпилируете эти функции в C ++, вы должны убедиться, что объявлениям назначена связь C в вашем коде C ++. Заголовки C нередко учитывают это автоматически с помощью директив условной компиляции, но если они этого не делают, вы можете сделать это с другой стороны, заключив включение (я) в блок связывания C:

extern "C" {
#include "myclib.h"
}

Если заголовки C объявляют глобальные объекты, имена которых конфликтуют с ключевыми словами C ++, и на которые не нужно ссылаться , тогда вы можете использовать препроцессор, чтобы переопределить их:

#define new extern_new
#include "myclib.h"
#undef  new

Это не обязательно сработает, но попробовать стоит. Не забудьте #undef такие макросы после включения заголовков C, как показано.

Могут быть и другие забавные трюки, которые вы можете использовать с макросами для адаптации определенных заголовков к C ++, но в какой-то момент имеет смысл просто скопировать / переписать необходимые объявления (и только те) либо в вашем основном источнике C ++, либо в вашем собственном Заголовок C ++. Обратите внимание, что это не устраняет необходимость объявлять связь C - это требование исходит из того, что библиотека была скомпилирована компилятором C, а не компилятором C ++.

2
John Bollinger 26 Апр 2016 в 15:20
  1. Вы можете написать дубликат заголовка c, с той лишь разницей, что не будет декларации extern int new. Затем используйте только что созданный дружественный к C ++ заголовок вместо старого несовместимого.

    Если вам нужно ссылаться на эту переменную из C ++, вам нужно сделать что-то более сложное. Вам нужно будет написать новую библиотеку-оболочку на c, которая предоставляет функции чтения и записи указатель на c ++, который можно использовать для доступа к неудачно названной переменной.

    Если некоторые встроенные функции ссылаются на переменную, вы можете исключить их из повторяющегося заголовка и, если вам нужно вызвать их из C ++, повторно реализовать их в удобной для C ++ манере. Только не давайте повторной реализации то же имя в extern "C", потому что это может привести к конфликту с исходной встроенной функцией, которая, возможно, используется некоторым существующим кодом c.

  2. Выполните такое же дублирование заголовка, как описано в 1., исключив проблемную встроенную функцию и повторно реализовав ее при необходимости.

  3. Предупреждения можно игнорировать или отключить, как вы уже знаете. Нет необходимости изменять исходные заголовки. Вы можете переписать любые {const, type} -unsafe встроенные функции с версиями, которые не генерируют предупреждения, но вы должны учитывать, стоит ли ваше время того.

Недостатком этого подхода является то, что теперь у вас есть две версии некоторых / всех заголовков. Новые, используемые c ++, и старые, которые могут использоваться некоторым старым кодом.


Если вы можете отказаться от требования не касаться исходной библиотеки и вам не нужно работать с существующим скомпилированным двоичным файлом, тогда элегантным решением будет просто переименовать проблемные переменные (1.). Сделайте несовместимые с C ++ встроенные функции (2.) не встроенными и переместите их в исходные файлы C. Исправьте небезопасный код (3.), который генерирует предупреждения, или, если предупреждение специфично для C ++, просто сделайте функцию не встроенной.

2
eerorika 26 Апр 2016 в 15:10