2009-09-04 2 views
25

Я написал функцию C++, которую мне нужно вызвать из программы на C. Чтобы сделать его вызываемым с C, я указал extern "C" на функцию декларация. Затем я скомпилировал код C++, но компилятор (Dignus Systems/C++) создал функцию mangled name для этой функции. Так что, по-видимому, он не почитал extern "C".Является ли extern «C» обязательным только для объявления функции?

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

Технически extern "C" необходимо указывать только в объявлении функции. Это правильно? (У этого примера есть C++ FAQ Lite.) Должны ли вы также указать его на определение функции?

Вот пример, чтобы продемонстрировать это:

/* ---------- */ 
/* "foo.h" */ 
/* ---------- */ 

#ifdef __cplusplus 
extern "C" { 
#endif 

/* Function declaration */ 
void foo(int); 

#ifdef __cplusplus 
} 
#endif 

/* ---------- */ 
/* "foo.cpp" */ 
/* ---------- */ 

#include "foo.h" 

/* Function definition */ 
extern "C"    // <---- Is this needed? 
void foo(int i) { 
    // do something... 
} 

Моя проблема может быть результатом неправильно кодирования что-то, или я, возможно, нашли ошибку компилятора. В любом случае я хотел бы обратиться к stackoverflow, чтобы убедиться, что я знаю, что технически является «правильным».

+4

Вы уверены, что на самом деле вы получаете mangling foo, если вы оставите extern «C» в foo.c в примере кода, который вы показываете? Или это было нечто, что произошло в другом более сложном коде? Я довольно часто видел эту проблему как признак забывания включить foo.h в foo.c. –

+0

@Brooks Moses: Это отличный момент. В моем фактическом коде, который немного сложнее, чем этот пример «foo», я включаю заголовок в исходный файл «cpp». Что заставило меня думать, что компилятор искал имя: «Если я не включаю« extern »C» в определение функции, тогда в списке компилятора отображается внешний символ «foo_FPFPCc_v». Когда включен «extern» C », в листинге отображается внешний символ« foo ». – bporter

+1

@bporter - может быть интересно поэкспериментировать с тем, что делает компилятор с упрощенным примером. Если он показывает то же поведение, вы можете отправить заметку поставщику. Если он не показывает то же самое, тогда вы должны отслеживать, что происходит в вашей реальной сборке, потому что это указывает на то, что вводится неправильный заголовок (или что-то еще делает объявление функции недоступным). –

ответ

29

Не нужно требовать от функции дефиниции функции «extern "C"» до тех пор, пока декларация имеет ее и уже видна в компиляции определения. В стандарте конкретно указано (7.5/5 Спецификации связи):

Функция может быть объявлена ​​без спецификации соединения после того, как была видна явная спецификация связи; ссылка, явно указанная в предыдущей декларации, не влияет на такое объявление функции.

Однако, я вообще поставил «extern "C"» на Defintion, а также, потому что это на самом деле функция с экстерном «C» связи. Многие люди ненавидят, когда ненужные, избыточные вещи находятся в объявлениях (например, кладут virtual на переопределение методов), но я не один из них.

+0

@Michael Burr - Спасибо за ваш ответ. Похоже, что «extern» C «'не требуется в определении, если оно указано в предыдущем объявлении. Как вы уже упоминали, я могу продолжить и указать его в обоих местах. (На самом деле, мне может понадобиться, если я не выясню, что моя проблема - это нечто иное, чем ошибка компилятора. Если это похоже на ошибку компилятора, я сообщу об этом поставщику, чтобы они могли изучить его.) – bporter

+0

Я думал, что я единственный, кто ставит 'virtual' на переопределение метода :-) – Mawg

0

Он должен быть обоим. Компилятор должен знать, использовать имя символа C и соглашения о вызовах при компиляции сайтов вызовов (которые могут видеть только объявление), а компилятор также должен знать, чтобы сгенерировать имя символа C и использовать соглашения о вызове C при компиляции само определение функции (которое может не видеть никаких других объявлений).

Теперь, если у вас есть объявление extern-C, которое видно из единицы перевода, в которой существует определение, вы можете уйти, оставив extern-C из определения, но я не Знайте это наверняка.

+3

Это неверно в случае, как описано, где foo.h включен в foo.c, а «get away with» вводит в заблуждение для использования поведения, требуемого стандартом. Если включить foo.h в foo.c - как всегда, всегда нужно делать так, чтобы компилятор мог проверить, что декларация на самом деле точна! - тогда нет необходимости (кроме, быть может, ясности читателям) - поставить extern «C» в определение внутри foo.c. –

1

Редактировать:
Похоже, что я неправильно понял вопрос. В любом случае, я попробовал:


// foo.cpp 
/* Function definition */ 

#include "foo.h" 

void foo(int i) { 
//do stuff 
} 
void test(int i) 
{ 
// do stuff 
} 

// foo.h 
#ifdef __cplusplus 
extern "C" { 
#endif 

/* Function declaration */ 
void foo(int); 

#ifdef __cplusplus 
} 
#endif 

void test(int); 

Использование команды nm для просмотра символов из скомпилированного файла:


linuxuser$ nm foo.o 
00000006 T _Z4testi 
     U __gxx_personality_v0 
00000000 T foo 

Это ясно говорит о том, что имя функции объявлены как экстерном «C» не подогнаны и ключевое слово extern «C» не требуется при определении.
Если бы для каждого кода библиотеки C, написанного без extern, «C» было бы непригодным для использования в программах на C++.

+0

@Neeraj - Спасибо за ваш пример. Это то, что я ожидал увидеть и в моем случае. Похоже, мой компилятор управляет именем функции «foo», если я не укажу «extern» C «'в объявлении и определении. Я думаю, что вы и некоторые другие плакаты верны, что «extern» C »не требуется в определении, если оно указано в предыдущем объявлении. – bporter

0

extern "C" вокруг определения не требуется. Вы можете уйти, просто положив его на объявление. Одно примечание в вашем примере ...

#ifdef __cplusplus 
extern "C" { 
#endif 

/* Function declaration */ 
void foo(int); 

#ifdef __cplusplus 
} 
#endif 

Вашего код ищет макрос препроцессора «__cplusplus».

Хотя это обычно реализуется в зависимости от вашего компилятора, это может быть или не быть определено. В вашем примере вы также используете extern "C" вокруг объявления, но там вы не проверяете макрос «__cplusplus», поэтому я подозреваю, что он сработал, как только вы это сделали.

Смотрите комментарии ниже - Стандартный C++ требует __cplusplus макрос определяется препроцессором.

+0

@whitej - макрос «__cplusplus» определяется моим конкретным компилятором, но вы правы, что он не является «стандартным». Кроме того, я использую этот макрос в заголовке, потому что это то, где я объявляю функцию (и именно там большинство людей будут изучать интерфейс), и это позволяет мне включать заголовок как в файлы C, так и на C++. Компиляторы C не поддерживают «extern» C «', и поэтому этот макрос вставляет« extern »C«' только тогда, когда это уместно. Обратите внимание, что макрос не нужен в «foo.cpp», если указан «extern» C «', потому что« foo.cpp »всегда будет скомпилирован компилятором C++. – bporter

+4

Макрос «' __cplusplus »требуется по стандарту, который должен быть определен при компиляции модуля C++ - абсолютно никаких проблем с его использованием. –

+1

И в то время как «' __cplusplus »часто требуется в заголовках (поскольку они могут быть включены модулями C или C++), обычно это не нужно в файлах .c или .cpp, поскольку они обычно предназначены для компиляции в виде C или C++ , но не оба (есть исключения, конечно, но обычно нет). –

1

Только что столкнулся с такой ситуацией ... Неприятный опыт.

Следующая была объявлена ​​в одном из моих c файлов:

void unused_isr(void) {} 
void ADC_IRQHandler(void)  __attribute__ ((weak, alias("unused_isr"))); 

Далее где-то в cpp файле я определила:

void ADC_IRQHandler(void) {                     
    ... 
} 

И я забыл изменить вперед декларацию:

void ADC_IRQHandler(void); 

Мне потребовалось некоторое время, прежде чем я понял Я делал все правильно в отношении преобразования AD, но я не смог добавить «extern C» к определению!

extern "C" void ADC_IRQHandler(void) {                     
    ... 
} 

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

1

Я думаю, что это нужно разъяснить здесь, поскольку у меня была аналогичная проблема, и мне потребовалось некоторое время, чтобы понять это в моей голове, только Брукс Моисей коснулся этого правильно, и я думаю, что это нужно заявить более четко ...

Таким образом, заголовок может вас отбросить, все, что видит компилятор, это файл cpp, и если заголовок не включен в extern «C» обратно в ваш cpp (что я обычно вижу) то extern «C» должен находиться в файле cpp где-нибудь (либо в определении, либо в другом объявлении), поэтому компилятор CXX может знать, чтобы сделать его с C-связью, компилятор не заботится о заголовке, только компоновщик ,

Смежные вопросы