Объединение C++ и C - как работает #ifdef cplusplus?
Я работаю над проектом, который имеет большое наследие C код. Мы начали писать на C++ с намерением в конечном итоге преобразовать устаревший код. Я немного смущен тем, как C и C++ взаимодействовать. Я понимаю это, обертывая C код extern "C"
компилятор C++ не будет калечить C имена кода, но я не совсем уверен, как это реализовать.
Итак, в верхней части каждого C заголовочный файл (после включения ГВ), у нас есть
#ifdef __cplusplus
extern "C" {
#endif
а внизу пишем
#ifdef __cplusplus
}
#endif
между ними у нас есть все наши прототипы includes, typedefs и function. У меня есть несколько вопросов, чтобы увидеть, правильно ли я это понимаю:
если у меня есть файл C++ A. hh, который включает в себя C заголовочный файл Б. Ч., включает в себя другой C заголовочный файл С. Ч., как это работает? Я думаю, что когда компилятор переходит в B. h,
__cplusplus
будет определен, поэтому он обернет код с помощьюextern "C"
(и__cplusplus
не будет определено внутри этого блока). Так, когда он входит в C. h,__cplusplus
не будет определено и код не будет завернут вextern "C"
. Правильно ли это?что-то не так с обертывание фрагмента кода
extern "C" { extern "C" { .. } }
? Что будет второйextern "C"
делать?мы не кладем эту обертку вокруг этот.c файлами, только .H-файлы. Итак, что происходит, если функция не имеет прототипа? Считает ли компилятор, что это функция C++?
мы также используем некоторую третью сторону код, который написан в C, и нет такой обертки вокруг он. Каждый раз, когда я включаю заголовок из той библиотеки, которую я поместил Ан
extern "C"
вокруг #include. Это правильный способ справиться с это?наконец, это хорошая идея? Есть что-нибудь еще, что мы должны сделать? Мы будем смешивать C и C++ в обозримом будущем, и я хочу убедиться, что мы покрываем все наши базы.
3 ответов
extern "C"
на самом деле не меняет способ, которым компилятор читает код. Если ваш код находится в .C файл, он будет скомпилирован как C, если он находится в a .cpp-файл, он будет скомпилирован как C++ (если вы не сделаете что-то странное для своей конфигурации).
что extern "C"
влияет на связь. Функции C++ при компиляции имеют искаженные имена - это то, что делает перегрузку возможной. Имя функции изменяется на основе типов и количества параметров, так что два функции с одинаковым именем будут иметь разные имена символов.
код внутри extern "C"
по-прежнему является кодом C++. Есть ограничения на то, что вы можете сделать в блоке extern "C", но все они касаются связи. Вы не можете определить все новые символы, которые не могут быть построены с компоновкой. Например, это означает отсутствие классов или шаблонов.
extern "C"
блоки красиво гнезда. Есть также extern "C++"
если вы окажетесь безнадежно в ловушке внутри extern "C"
регионы, но это не такая хорошая идея с точки зрения чистоты.
теперь, в частности, относительно ваших нумерованных вопросов:
относительно #1: __cplusplus должен быть определен внутри extern "C"
блоки. Впрочем, это не имеет значения, поскольку блоки должны быть аккуратно уложены.
относительно #2: __cplusplus будет определен для любой единицы компиляции, которая выполняется через компилятор C++. Как правило, это означает .cpp-файлы и любые файлы, включенные в это .файл cpp. Тот же.ч (или. hh или .hpp или что у вас есть) можно интерпретировать как 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"
не изменяет наличие или отсутствие__cplusplus
макрос. Это просто изменяет связь и искажение имен завернутых деклараций.вложения
extern "C"
блоки вполне счастливо.если вы скомпилировали
.c
файлы как C++, то ничего неextern "C"
блок, и безextern "C"
prototype будет рассматриваться как функция C++. Если вы скомпилируете их как C, то, конечно, все будет C функция.да
вы можете безопасно смешивать C и c++ таким образом.
пара gotchas, которые являются коллорариями к превосходному ответу Андрея Шеланского и немного не согласны с на самом деле не меняет способ, которым компилятор читает код
поскольку ваши прототипы функций скомпилированы как C, вы не можете иметь перегрузку одних и тех же имен функций с разными параметрами - это одна из ключевых особенностей искажения имени компилятора. Это описывается как проблема связи, но это не совсем так - вы получите ошибки от компилятора и компоновщика.
ошибки компилятора будут, если вы попытаетесь использовать функции C++ объявления прототипа, такие как перегрузка.
ошибки компоновщика возникнут позже, потому что ваша функция не будет найдена, если вы это сделаете не есть extern "C" обертка вокруг объявлений, а заголовок включен в смесь источников C и c++.
одна причина обескуражить людей от использования скомпилировать C как c++ настройка, потому что это означает, что их исходный код больше не переносится. Этот параметр является параметром проекта, и поэтому, если a .файл c отбрасывается в другой проект, он не будет компилироваться как c++. Я бы предпочел, чтобы люди находили время для переименования суффиксов файлов .СРР.