Я работаю над проектом, в котором много унаследованного кода на Си . Мы начали писать на C ++ с намерением в конечном итоге также преобразовать устаревший код. Я немного озадачен тем, как взаимодействуют C и C ++. Я понимаю , что обертывание C кода extern "C"
на C ++ компилятор не будет искажать C кодовых имен, но я не совсем уверен , как это реализовать.
Итак, в верхней части каждого заголовочного файла C (после включения защиты), мы имеем
#ifdef __cplusplus
extern "C" {
#endif
а внизу пишем
#ifdef __cplusplus
}
#endif
Между ними у нас есть все наши include, typedefs и прототипы функций. У меня есть несколько вопросов, чтобы понять, правильно ли я это понимаю:
Если у меня есть файл C ++ A.hh, который включает файл заголовка C Bh, включает другой файл заголовка C C , как это работает? Я думаю, что когда компилятор
__cplusplus
войдет в Bh, будет определен, поэтому он обернет кодextern "C"
(и__cplusplus
не будет определен внутри этого блока). Таким образом, когда он входит в Ch,__cplusplus
он не будет определен и код не будет включенextern "C"
. Это верно?Что-то не так с упаковкой кода
extern "C" { extern "C" { .. } }
? Что будетextern "C"
делать второй ?Мы не помещаем эту обертку в файлы .c, а только в файлы .h. Итак, что произойдет, если функция не имеет прототипа? Думает ли компилятор, что это функция C ++?
Мы также используем некоторый сторонний код, который написан на C , и не имеет такой оболочки. Каждый раз, когда я включаю заголовок из этой библиотеки, я помещаю
extern "C"
вокруг #include. Это правильный способ справиться с этим?Наконец, это хорошая идея? Есть ли что-то еще, что мы должны сделать? Мы будем смешивать C и C ++ в обозримом будущем, и я хочу убедиться, что мы охватываем все наши базы.
источник
To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed.
(я получил по ссылке )Ответы:
extern "C"
на самом деле не меняет способ, которым компилятор читает код. Если ваш код находится в файле .c, он будет скомпилирован как C, если он находится в файле .cpp, он будет скомпилирован как C ++ (если вы не сделаете что-то странное для своей конфигурации).Что
extern "C"
влияет на связь. Функции C ++ при компиляции имеют искаженные имена - это то, что делает возможной перегрузку. Имя функции изменяется в зависимости от типов и количества параметров, поэтому две функции с одинаковыми именами будут иметь разные имена символов.Код внутри по-
extern "C"
прежнему является кодом C ++. Существуют ограничения на то, что вы можете делать во внешнем блоке «C», но все они связаны с связью. Вы не можете определить какие-либо новые символы, которые не могут быть построены с помощью связи C. Это означает, что нет классов или шаблонов, например.extern "C"
Гнездо блоков красиво. Там также,extern "C++"
если вы окажетесь безнадежно в ловушке внутриextern "C"
регионов, но это не очень хорошая идея с точки зрения чистоты.Теперь конкретно по вашим пронумерованным вопросам:
Относительно # 1: __cplusplus останется определенным внутри
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"
блока. Это хорошо, хотя, потому что, если у него нет прототипа, он может быть вызван только другими функциями в том же файле, и тогда вам вообще не важно, как выглядит связь, потому что вы не планируете иметь эту функцию в любом случае вызываться чем-либо вне того же модуля компиляции.Для # 4 у вас точно есть. Если вы включаете заголовок для кода, который имеет связь с C (например, код, который был скомпилирован компилятором C), тогда вы должны
extern "C"
использовать заголовок - таким образом вы сможете связываться с библиотекой. (В противном случае ваш компоновщик будет искать функции с именами, например,_Z1hic
когда вы искалиvoid h(int, char)
5: этот тип микширования является обычной причиной для использования
extern "C"
, и я не вижу ничего плохого в том, чтобы делать это таким образом - просто убедитесь, что вы понимаете, что делаете.источник
extern "C++"
когда ваш заголовок / код C ++ захвачен глубоко внутри некоторого кода на C__cplusplus
том, чтобы определитьC++
, используется ли он противC
, поэтому определение его вручную / явно не поддается его цели ...extern "C"
не меняет наличие или отсутствие__cplusplus
макроса. Он просто меняет связь и сортировку имен завернутых объявлений.Вы можете вложить
extern "C"
блоки довольно счастливо.Если вы компилируете свои
.c
файлы как C ++, то все, что не находится вextern "C"
блоке и безextern "C"
прототипа, будет рассматриваться как функция C ++. Если вы скомпилируете их как C, тогда, конечно, все будет функцией C.да
Таким образом вы можете безопасно смешивать C и C ++.
источник
.c
файлы как C ++, то все компилируется как код C ++, даже если он находится вextern "C"
блоке.extern "C"
Код не может использовать функции , которые зависят от C ++ , призывающих конвенций (например , перегрузка операторов) , но тело функции по - прежнему скомпилированные в C ++, со всем , что влечет за собой.Несколько уловок, которые являются сборниками к отличному ответу Эндрю Шелански и немного не согласны с ним , на самом деле не изменяют способ, которым компилятор читает код
Поскольку ваши прототипы функций скомпилированы как C, вы не можете перегружать одни и те же имена функций разными параметрами - это одна из ключевых особенностей искажения имени компилятора. Это описывается как проблема с компоновкой, но это не совсем так - вы получите ошибки как от компилятора, так и от компоновщика.
Ошибки компилятора будут возникать, если вы попытаетесь использовать функции C ++ объявления прототипа, такие как перегрузка.
Ошибки компоновщик будет происходить позже , потому что ваша функция будет отображаться не будет найден, если вы не имеете EXTERN «C» обертку вокруг деклараций и заголовок включается в смеси источника С и С ++.
Одна из причин, по которой людям не рекомендуется использовать компиляцию C в качестве параметра C ++, заключается в том, что это означает, что их исходный код больше не является переносимым. Этот параметр является параметром проекта, поэтому, если файл .c добавлен в другой проект, он не будет скомпилирован как c ++. Я бы предпочел, чтобы люди потратили время на переименование файловых суффиксов в .cpp.
источник
Речь идет о ABI, чтобы позволить приложениям C и C ++ использовать интерфейсы C без каких-либо проблем.
Поскольку язык C очень прост, генерация кода была стабильной в течение многих лет для различных компиляторов, таких как GCC, Borland C \ C ++, MSVC и т. Д.
В то время как C ++ становится все более и более популярным, в новый домен C ++ необходимо добавить много вещей (например, в конечном итоге Cfront был заброшен в AT & T, потому что C не может охватить все необходимые функции). Такие, как функции шаблонов и генерация кода во время компиляции, в прошлом, разные производители компиляторов фактически делали фактическую реализацию компилятора и компоновщика C ++ отдельно, фактические ABI вообще не совместимы с программой C ++ на разных платформах.
Люди могут по-прежнему хотеть реализовать настоящую программу на C ++, но все еще сохраняют старый интерфейс C и ABI как обычно, заголовочный файл должен объявлять extern "C" {} , он говорит компилятору генерировать совместимый / старый / простой / легкий C ABI для функций интерфейса, если компилятор является компилятором C, а не компилятором C ++.
источник