сочетание c++ и c - как работает #ifdef \_\_cplusplus?

<p>я работаю над проектом, в котором много устаревшего кода <strong>c</strong>. мы начали писать на c++ с намерением в конечном итоге также преобразовать унаследованный код. я немного озадачен тем, как взаимодействуют <strong>c</strong> и c++. я понимаю, что, оборачивая код <strong>c</strong> в <code>extern "c"</code>, компилятор c++ не будет манипулировать именами кода <strong>c</strong>, но я не совсем уверен, как это реализовать.</p><p>итак, в верхней части каждого заголовочного файла <strong>c</strong> (после включения защиты) мы имеем</p><pre><code>#ifdef \_\_cplusplus extern "c" { #endif </code></pre><p>а внизу пишем</p><pre><code>#ifdef \_\_cplusplus } #endif </code></pre><p>между ними у нас есть все наши include, typedefs и прототипы функций. у меня есть несколько вопросов, чтобы понять, правильно ли я это понимаю:</p><ol> <li><p>если у меня есть файл c++ a.hh, который включает заголовочный файл <strong>c</strong> b.h, включает другой заголовочный файл <strong>c</strong> c.h, как это работает? я думаю, что когда компилятор войдет в b.h, будет определен <code>\_\_cplusplus</code>, поэтому он обернет код с <code>extern "c"</code> (и <code>\_\_cplusplus</code> не будет определен внутри этого блока). таким образом, когда он входит в c.h, <code>\_\_cplusplus</code> не будет определен и код не будет заключен в <code>extern "c"</code>. это правильно?</p></li> <li><p>что-то не так с упаковкой кода с <code>extern "c" { extern "c" { .. } }</code>? что будет второй <code>extern "c"</code> do?</p></li> <li><p>мы не помещаем эту обертку в файлы .c, а только в файлы .h. итак, что произойдет, если функция не имеет прототипа? думает ли компилятор, что это функция c++?</p></li> <li><p>мы также используем какой-то сторонний код , который написан в <strong>c</strong>, и у нет такой оболочки вокруг it. каждый раз, когда я добавляю заголовок из этой библиотеки, я помещаю an <code>extern "c"</code> вокруг #include . это правильный способ иметь дело с .\_\_?</p></li> <li><p>наконец, это хорошая идея? есть ли что-то еще, что мы должны сделать? мы будем смешивать <strong>c</strong> и c++ в обозримом будущем, и я хочу убедиться, что мы покрываем все наши базы.</p></li> </ol>

<p><code>extern "c"</code> на самом деле не меняет способ, которым компилятор читает код. если ваш код находится в файле .c, он будет скомпилирован как c, если он находится в файле .cpp, он будет скомпилирован как c++ (если вы не сделаете что-то странное для своей конфигурации).</p><p><code>extern "c"</code> влияет на связь. функции c++ при компиляции имеют искаженные имена - это то, что делает возможной перегрузку. имя функции изменяется в зависимости от типов и количества параметров, поэтому две функции с одинаковыми именами будут иметь разные имена символов.</p><p>код внутри <code>extern "c"</code> по-прежнему является кодом c++. существуют ограничения на то, что вы можете делать во внешнем блоке «c», но все они связаны с сцеплением. вы не можете определить какие-либо новые символы, которые не могут быть построены с помощью связи c. это означает, что нет классов или шаблонов, например.</p><p><code>extern "c"</code> красиво блокирует гнездо. также есть <code>extern "c++"</code>, если вы оказались безнадежно пойманным в ловушку внутри <code>extern "c"</code> регионов, но это не очень хорошая идея с точки зрения чистоты.</p><p>теперь конкретно по вашим пронумерованным вопросам:</p><p>относительно # 1: \_\_cplusplus должен быть определен внутри блоков <code>extern "c"</code>. это не имеет значения, так как блоки должны аккуратно вкладываться.</p><p>что касается # 2: \_\_cplusplus будет определен для любого модуля компиляции, который запускается через компилятор c++. как правило, это означает, что файлы .cpp и любые файлы включены в этот файл .cpp. один и тот же .h (или .hh или .hpp или what-have-you) может интерпретироваться как c или c++ в разное время, если их содержат разные модули компиляции. если вы хотите, чтобы прототипы в файле .h ссылались на имена символов c, то они должны иметь <code>extern "c"</code> при интерпретации как c++, и они не должны иметь <code>extern "c"</code> при интерпретации как c - следовательно, проверка <code>#ifdef \_\_cplusplus</code>.</p><p>чтобы ответить на ваш вопрос № 3: функции без прототипов будут иметь связь с c++, если они находятся в файлах .cpp, а не внутри блока <code>extern "c"</code>. это хорошо, хотя, потому что, если у него нет прототипа, он может быть вызван только другими функциями в том же файле, и тогда вам вообще не важно, как выглядит связь, потому что вы не планируете иметь эту функцию в любом случае вызываться чем-либо вне того же модуля компиляции.</p><p>для # 4 у вас точно есть. если вы включаете заголовок для кода, связанного с c (например, код, который был скомпилирован компилятором c), то вы должны <code>extern "c"</code> заголовок - таким образом вы сможете связываться с библиотекой. (в противном случае ваш компоновщик будет искать функции с именами, такими как <code>\_z1hic</code>, когда вы искали <code>void h(int, char)</code></p><p>5: этот тип микширования является распространенной причиной использования <code>extern "c"</code>, и я не вижу в этом ничего плохого - просто убедитесь, что вы понимаете, что делаете.</p>

<ol> <li><p><code>extern "c"</code> не изменяет наличие или отсутствие макроса <code>\_\_cplusplus</code>. он просто меняет связь и сортировку имен завернутых объявлений.</p></li> <li><p>вы можете вложить блоки <code>extern "c"</code> довольно счастливо.</p></li> <li><p>если вы компилируете свои файлы <code>.c</code> как c++, то все, что не находится в блоке <code>extern "c"</code> и без прототипа <code>extern "c"</code>, будет рассматриваться как функция c++. если вы скомпилируете их как c, тогда, конечно, все будет функцией c.</p></li> <li><p>да</p></li> <li><p>вы можете безопасно смешивать c и c++ таким образом.</p></li> </ol>

<p>несколько уловок, которые являются сборниками отличного ответа эндрю шелански и немного не согласны с <em>, на самом деле не изменяют способ, которым компилятор читает код</em></p><p>поскольку ваши прототипы функций скомпилированы как c, вы не можете перегружать одни и те же имена функций разными параметрами - это одна из ключевых особенностей искажения имени компилятора. это описывается как проблема с компоновкой, но это не совсем так - вы получите ошибки как от компилятора, так и от компоновщика.</p><p>ошибки компилятора будут возникать, если вы попытаетесь использовать возможности c++ объявления прототипа, такие как перегрузка. </p><p>ошибки компоновщика появятся позже, потому что ваша функция окажется не найденной, если у вас есть <strong>not</strong> обертка <em>extern "c"</em> вокруг объявлений, а заголовок включен в смесь c и c++ источник.</p><p>одна из причин не рекомендовать людям использовать параметр <em>compile c как c++</em> состоит в том, что это означает, что их исходный код больше не является переносимым. этот параметр является настройкой проекта, поэтому, если файл .c добавлен в другой проект, он не будет скомпилирован как c ++. я бы предпочел, чтобы люди потратили время на переименование файловых суффиксов в .cpp.</p>

<p>речь идет о abi, чтобы позволить приложениям c и c++ использовать интерфейсы c без каких-либо проблем.</p><p>поскольку язык c очень прост, генерация кода была стабильной в течение многих лет для различных компиляторов, таких как gcc, borland c\c++, msvc и т.д.</p><p>в то время как c++ становится все более популярным, в новый домен c++ необходимо добавить много вещей (например, в конечном итоге cfront был заброшен в at & t, поскольку c не может охватить все необходимые функции). такие как<strong> template </strong>feature и генерация кода во время компиляции, из прошлого, разные производители компиляторов фактически делали фактическую реализацию компилятора c++ и компоновщика отдельно, реальные abi вообще не совместимы с программой c++ на разных платформ.</p><p>люди могут по-прежнему хотеть реализовать настоящую программу на c++, но все еще сохраняют старый интерфейс c и abi как обычно, заголовочный файл должен объявить<strong> extern "c" {} </strong>, это говорит компилятору<strong> создать совместимый/старый/simple/easy c abi для интерфейсных функций </strong>если компилятор является компилятором c, а не компилятором c++.</p>