Объединение 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 заголовочный файл B. h,
    включает в себя еще один C заголовочный файл С. Ч.,
    как это работает? Я думаю, что
    когда компилятор переходит в B. h,
    __cplusplus будет определен, так что это
    обернет код с extern "C"
    __cplusplus не будет
    определено внутри этого блока). Так,
    когда он входит в C. h,
    __cplusplus не будет определен
    и код не будет завернут в
    extern "C". Это правильно?


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


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


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


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


605   3  

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 или 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" и я не вижу в этом ничего плохого делать это таким образом, просто убедитесь, что вы понимаете, что вы делаете.

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

  2. вложения extern "C" блоки вполне счастливо.

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

  4. да

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

несколько gotchas, которые являются коллорариями к превосходному ответу Андрея Шеланского и немного не согласны с на самом деле не меняет способ, которым компилятор читает код

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

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

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

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

Comments

    Ничего не найдено.