Сочетание C ++ и C - как работает #ifdef __cplusplus?

261

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

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

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

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

  • Если у меня есть файл С++ A.hh, который включает заголовочный файл C B.h, включает в себя другой заголовок C C.h, как это работает? я думаю что когда компилятор переходит в B.h, __cplusplus будет определено, поэтому будет завершать код с помощью extern "C"__cplusplus не будет определенные внутри этого блока). Так, когда он переходит в C.h, __cplusplus не будет определено и код не будет завернут в extern "C". Правильно ли это?

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

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

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

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

  • 6
    Посмотрите на C ++ FQA: смешивание C и C ++ и C ++ FAQ: смешивание C и C ++ .
  • 2
    Вкратце, это лучшее объяснение: To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed. (Я получил по ссылке )
Показать ещё 1 комментарий
Теги:
c-preprocessor
extern-c

4 ответа

218
Лучший ответ

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

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

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

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

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

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

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

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

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

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

  • 7
    Хорошо для упоминания extern "C++" когда ваш заголовок / код C ++ захвачен глубоко внутри некоторого кода C
  • 1
    Я написал простую программу на Си. Внутри я добавил блок #ifdef __cplusplus и добавил printf ("__cplusplus определен \ n"); в этом. Если я скомпилирую его с помощью gcc, «__cplusplus определенный» не будет напечатан, но если я скомпилирую его с помощью g ++, он будет напечатан. Итак, я понимаю, что __cplusplus означает, что компилятор - это компилятор C ++ (вы это сказали). Разве это не правильно? (потому что я видел, как вы говорили: «__cplusplus должен быть определен внутри внешних блоков« C »». Можем ли мы определить __cplusplus явно?
Показать ещё 2 комментария
35
  • extern "C" не изменяет наличие или отсутствие макроса __cplusplus. Это просто изменяет привязку и манипулирование именами завернутых объявлений.

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

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

  • Да

  • Вы можете смело смешивать C и С++ таким образом.

  • 0
    Если вы компилируете файлы .c как C ++, то все компилируется как код C ++, даже если он находится во extern "C" блоке extern "C" . Внешний код extern "C" не может использовать функции, которые зависят от соглашений о вызовах C ++ (например, перегрузка операторов), но тело функции по-прежнему компилируется как C ++ со всеми вытекающими последствиями.
18

Пару gotchas, которые являются комбинациями с Andrew Shelansky превосходным ответом и не согласны с этим, действительно не меняют способ, которым компилятор читает код

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

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

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

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

  • 1
    Это была загадочная причина, вырвать мои волосы. Действительно нужно где-то опубликовать.
0

Это о ABI, чтобы позволить и приложениям на C, и C++ использовать интерфейсы C без каких-либо проблем.

Поскольку язык C очень прост, генерация кода была стабильной в течение многих лет для различных компиляторов, таких как GCC, Borland C\C++, MSVC и т.д.

В то время как C++ становится все более и более популярным, в новый домен C++ необходимо добавить много вещей (например, наконец, Cfront был заброшен в AT & T, потому что C не может охватить все функции, в которых он нуждается). Например, из-за особенностей шаблона и генерации кода во время компиляции различные поставщики компиляторов фактически реализовали фактическую реализацию компилятора и компоновщика C++ отдельно, фактические ABI вообще не совместимы с программой C++ на разных платформ.

Люди могут по-прежнему хотеть реализовать настоящую программу в C++, но все еще сохраняют старый интерфейс C и ABI как обычно, заголовочный файл должен объявить extern "C" {}.

Сообщество Overcoder
Наверх
Меню