Объединение C ++ и C – как работает #ifdef __cplusplus?

Я работаю над проектом, который имеет много унаследованного кода C. Мы начали писать на C ++, имея в виду, наконец, преобразовать устаревший код. Я немного запутался в том, как взаимодействуют C и C ++. Я понимаю, что, завернув код C extern "C" компилятор C ++ не будет калечить имена кода C , но я не совсем уверен, как это реализовать.

Итак, в верхней части каждого файла заголовка C (после включения охранников), мы имеем

 #ifdef __cplusplus extern "C" { #endif 

и внизу мы пишем

 #ifdef __cplusplus } #endif 

Между этими двумя, у нас есть все наши включенные, typedefs и прототипы функций. У меня есть несколько вопросов, чтобы понять, правильно ли я это понимаю:

  1. Если у меня есть файл C ++ A.hh, который включает в себя файл заголовка C Bh, включает другой файл заголовка C Ch, как это работает? Я думаю, что когда компилятор переходит в Bh, будет определен __cplusplus , поэтому он будет обертывать код extern "C"__cplusplus не будет определен внутри этого блока). Таким образом, когда он переходит в Ch, __cplusplus не будет определен, и код не будет завернут в extern "C" . Это верно?

  2. Есть ли что-то не так с обертыванием fragmentа кода с extern "C" { extern "C" { .. } } ? Что сделает второй extern "C" ?

  3. Мы не помещаем эту оболочку в файлы .c, а только файлы .h. Итак, что произойдет, если функция не имеет прототипа? Компилятор считает, что это C ++-функция?

  4. Мы также используем сторонний код, который написан на C , и не имеет такого обертки вокруг него. Каждый раз, когда я включаю заголовок из этой библиотеки, я помещаю extern "C" вокруг #include. Это правильный способ справиться с этим?

  5. Наконец, это хорошая идея? Есть ли что-нибудь еще, что мы должны делать? В обозримом будущем мы будем смешивать C и C ++, и я хочу убедиться, что мы покрываем все наши базы.

    extern "C" самом деле не изменяет способ чтения компилятором кода. Если ваш код находится в .c файле, он будет скомпилирован как C, если он находится в файле .cpp, он будет скомпилирован как C ++ (если вы не сделаете что-то странное для своей конфигурации).

    То, что extern "C" делает, влияет на связь. Функции C ++ при компиляции имеют искаженные имена – вот что делает возможным перегрузку. Имя функции изменяется на основе типов и количества параметров, так что две функции с тем же именем будут иметь разные имена символов.

    Код внутри extern "C" все еще является кодом C ++. Есть ограничения на то, что вы можете сделать во внешнем блоке «C», но все они связаны с привязкой. Вы не можете определить какие-либо новые символы, которые нельзя построить с помощью C-ссылки. Это означает, что нет classов или шаблонов, например.

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

    Теперь, в частности, относительно ваших нумерованных вопросов:

    Что касается № 1: __cplusplus должен быть определен внутри extern "C" блоков extern "C" . Это не имеет значения, так как блоки должны аккуратно гнездиться.

    Что касается № 2: __cplusplus будет определен для любой единицы компиляции, которая выполняется через компилятор C ++. Как правило, это означает, что файлы .cpp и любые файлы включены в этот файл .cpp. То же самое .h (или .hh или .hpp или what-have-you) можно интерпретировать как C или C ++ в разное время, если к ним относятся разные единицы компиляции. Если вы хотите, чтобы прототипы в файле .h ссылались на имена символов C, они должны иметь extern "C" при интерпретации C ++ и не должны иметь extern "C" при интерпретации C – следовательно, #ifdef __cplusplus проверка.

    Чтобы ответить на ваш вопрос №3: функции без прототипов будут иметь C ++-связь, если они находятся в .cpp-файлах, а не внутри extern "C" блока extern "C" . Это прекрасно, хотя, потому что, если у него нет прототипа, его можно вызвать только другими функциями в том же файле, а затем вам вообще не нравится, как выглядит ссылка, потому что вы не планируете использовать эту функцию все равно вызывать что-либо за пределами того же модуля компиляции.

    Для №4 у вас это точно. Если вы включаете заголовок для кода, который имеет C-ссылку (например, код, который был скомпилирован компилятором C), тогда вы должны extern "C" заголовок extern "C" – таким образом вы сможете связываться с библиотекой. (В противном случае ваш компоновщик будет искать функции с именами, такими как _Z1hic когда вы искали void h(int, char)

    5: Такое смешивание является распространенной причиной использования extern "C" , и я не вижу ничего плохого в том, чтобы делать это таким образом – просто убедитесь, что вы понимаете, что вы делаете.

    1. extern "C" не изменяет наличие или отсутствие макроса __cplusplus . Он просто изменяет привязку и обработку имен завернутых объявлений.

    2. Вы можете довольно легко extern "C" блокировку extern "C" .

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

    4. да

    5. Таким образом вы можете смешать C и C ++.

    Несколько ошибок, которые являются комбинациями с превосходным ответом Эндрю Шелански и не согласны с ним, действительно не меняют способ, которым компилятор читает код

    Поскольку ваши прототипы функций скомпилированы как C, вы не можете перегружать одни и те же имена функций с разными параметрами – это одна из ключевых особенностей управления именем компилятора. Он описывается как проблема связи, но это не совсем так – вы получите ошибки как от компилятора, так и от компоновщика.

    Ошибки компилятора будут, если вы попытаетесь использовать функции C ++ для описания прототипа, такие как перегрузка.

    Ошибки компоновщика появятся позже, потому что ваша функция окажется не найденной, если у вас нет обертки extern «C» вокруг деклараций, и заголовок включен в смесь источников C и C ++.

    Одна из причин препятствовать людям использовать компиляцию C как C ++, потому что это означает, что их исходный код больше не переносится. Этот параметр является настройкой проекта, поэтому, если файл .c удаляется в другой проект, он не будет скомпилирован как c ++. Я бы предпочел, чтобы люди потратили время на переименование суффиксов файлов на .cpp.

    Interesting Posts

    Почему Chrome устанавливается непосредственно в каталог пользователя вместо файлов программы?

    Найдите пару элементов из массива, сумма которого равна заданному числу

    Лучшая страtagsя обработки больших файлов CSV в Apache Camel

    Студия Android добавляет внешний проект для сборки.gradle

    Linq – максимальное значение для каждой группы

    Чтение двухстрочных заголовков в R

    Javamail Не удалось преобразовать сокет в TLS GMail

    Как связать вычислительную мощность старых компьютеров вместе?

    Метод getDispatcherType () не определен для типа HttpServletRequest

    нумерация по группам

    Построить матрицу смежности в MATLAB

    Изменения в базе данных Access не сохраняются при запуске приложения в Visual Studio

    Использование shared_ptr в dll-интерфейсах

    Как распечатать большую веб-страницу на одностраничном pdf с настраиваемыми параметрами?

    Условное форматирование ячейки на основе значения в 4 смежных ячейках

    Давайте будем гением компьютера.