2010-11-16 2 views
5

У меня есть класс, который содержит только статические элементы.Вызов указателей функций C++ из библиотек C

Я хотел бы зарегистрировать одну из своих функций-членов (VerifyClean в коде ниже) для вызова при выходе с использованием функции библиотеки «atexit».

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

class Example 
{ 
public: 
    static void Initialize(); 
    static void DoDirtyStuff {++dirtLevel;} 
    static void CleanUpStuff {--dirtLevel;} 
private: 
    static void VerifyClean(); 
    // DOESN'T COMPILE: extern "C" static void VerifyClean(); 
    static int dirtLevel; 
} 

int Example::dirtLevel; 

extern "C" void Example::VerifyClean() // DO I NEED extern "C" HERE? 
{ 
    assert(dirtLevel == 0); 
} 

void Example::Initialize() 
{ 
    dirtLevel = 0; 
    atexit(&VerifyClean); 
} 

Нужно ли мне действительно использовать экстерьер «С»?

Будет ли изменяться ответ, если я заменил «atexit» на не библиотечную функцию (реализованную на простом C)?

Если функция VerifyClean была общедоступной, и я решил назвать ее непосредственно из кода на C++, могли бы я получить ошибки ссылки или сбои во время выполнения? Я спрашиваю об этом, потому что в декларации не упоминается extern «C» вообще, поэтому обычный C++-код может неправильно обрабатывать вызов функции. Это работает нормально в моей системе MS Visual Studio 2005.

+7

Почему класс с только статическими функциями ? И FQA вряд ли является хорошим местом для изучения C++. –

+6

«FQA считается вредным» – jkerian

+2

C++ FQA - гораздо более надежный ресурс для обучения C++, чем FAQ. –

ответ

7

Возможно для использования различными соглашениями о вызовах для кода C и C++; однако на практике это почти никогда не происходит.

Если вы просто хотите, чтобы он работал и не заботился о поддержке неясных компиляторов, не беспокойтесь о extern "C". Это не обязательно в любом широко используемом компиляторе.

Если вы хотите быть абсолютно педантичный, или нужно поддерживать педантичный компилятор, написать обертку:

extern "C" static void ExampleVerifyClean() 
{ 
    Example::VerifyClean(); 
} 

void Example::Initialize() 
{ 
    dirtLevel = 0; 
    atexit(&ExampleVerifyClean); 
} 
+1

+1. Объясняет, что, вероятно, не стоит беспокоиться ('atexit (& VerifyClean)', вероятно, будет работать на практике), а затем дает сложное технически правильное решение. – aschepler

+0

Разве extern «C» также не требуется, если компилятор C++ исказил имя функции? Или большинство компиляторов C++ избегают манипулирования, когда им не нужно (например, когда нет перегрузки или функции не находятся внутри класса)? –

1

ошибки связи.

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

Внешняя линия С превращает с в более простой идентификатор.

редактировать:

Если все компилируется компилятором C++, это не будет проблемой. Но если у вас есть объектный файл, скомпилированный компилятором C и один компилируется компилятором C++, у вас появятся некоторые проблемы.

Кажется, я припоминаю библиотеки DLL, требующих Экстерн «C» спецификации, но эта память может быть 10 лет в этой точке


Хорошо.

я взбитый тестовый пример с функцией, которая имела подпись

INT Foo (с плавающей точкой, с плавающей точкой)

И скомпилирован под 3-й различные Gcc заклятий -

gcc test_c.c -S 
g++ test.cpp -S 

Эти два вызова произвели различных идентификаторов в сборке. C++ исказил имя в своем обычном подходе к модификации типов. (Конечно компиляторы могут сделать это по-разному)

Затем я обернул foo в Экстерн «C» и вызывается G ++ снова ...

g++ test.cpp -S 

Который затем Убрана подогнаны C++ имя, оставив равнину С непонятным именем.

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

+2

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

+1

+1 за то, что он был без причины зависён от какого-то придурковатого гормонального привода от спутника. Кстати, 'extern 'C" 'не отключает манипулирование именами, но это * обычно * приводит к значительно более простому искажению, просто префикс подчеркивания. cheers, –

+1

@John: вопрос имеет отношение к изменению имени, а также к вопросу о возможном вызове, о котором вы упоминаете. целью манипулирования именами является проверка типа времени слабой связи (вы можете рассмотреть возможность вызова условной части типа). некоторые компиляторы жалуются даже на код в том же компиляционном блоке, хотя, насколько я знаю, для всех существующих компиляторов предупреждение можно отключить. cheers, –

0

Без внешнего «C» ваше имя функции будет искажено компилятором, поэтому имя функции может оказаться отличным от того, что вы ожидаете. Вам нужно вызвать функцию, используя свое искаженное имя (например, использование GetProcAddress в Windows), или вы получите ошибку компоновщика. Разный компилятор искал его по-другому, поэтому лучше всего использовать ключевое слово extern.

+0

Измененное имя может даже не быть допустимым идентификатором C. – dan04

+0

Правда, но используя GetProcAddress (windows dll), мы можем вызвать функцию с искаженным именем, если мы знаем, что такое искаженное имя. – arifwn

0

вы могли бы использовать это:

class yourname 
{ 
    public: 
    ... 
    static void _cdecl AtExitCall(); 
}; 

int main() 
{ 
    ataexit(yourname::AtExitCall); 
}