Зачем вам "extern C" для C ++ обратных вызовов к функциям C? - PullRequest
17 голосов
/ 07 апреля 2010

Я нахожу такие примеры в коде Boost.

namespace boost {
   namespace {
     extern "C" void *thread_proxy(void *f)
     {
       ....
     }

   } // anonymous
   void thread::thread_start(...)
   {
       ...
       pthread_create(something,0,&thread_proxy,something_else);
       ...
   }
} // boost

Зачем вам это нужно extern "C"?

Понятно, что функция thread_proxy является закрытой внутренней, и я не ожидаю, что она будет искажен как "thread_proxy", потому что на самом деле он мне вообще не нужен.

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

Почему extern "C" добавлено?


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

Это не дубликат! Я не говорю о калечащих и внешних связях. В этом коде очевидно, что внешняя связь нежелательна!

Ответ: Соглашения о вызовах функций C и C ++ не обязательно совпадают, поэтому вам нужно создать соглашение с соглашением о вызовах C. См. 7.5 (p4) стандарта C ++.

Ответы [ 6 ]

16 голосов
/ 07 апреля 2010

Ясно, что функция thread_proxy является закрытой внутренней, и я не ожидаю, что она будет искажена как "thread_proxy", потому что на самом деле она мне вообще не нужна.

Несмотря на это, он все еще будет искажен. (Если бы не было extern "C") Так работает компилятор. Я согласен, что вполне возможно, что компилятор может сказать: «это не обязательно должно быть искажено», но стандарт ничего не говорит об этом. Тем не менее, искажение не вступает в игру здесь, так как мы не пытаемся связать с функцией.

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

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

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

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

Ничего не загрязняет приведенный выше код. Он находится в безымянном пространстве имен и недоступен за пределами модуля перевода.

6 голосов
/ 07 апреля 2010

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

Стандарт оставляет это полностью открытым как определяемая реализацией семантика.

5 голосов
/ 07 апреля 2010

Вопрос верный - хотя функция передается в библиотеку C, эта библиотека C вообще не связана с кодом C ++. Ему присваивается только адрес функции, поэтому он совершенно не интересуется именем функции.

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

К сожалению, он также имеет побочный эффект создания символа внешнего линкера на глобальном уровне. Но это можно смягчить, используя вместо этого имя типа boost_detail_thread_proxy.

1 голос
/ 07 апреля 2010

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


Это все, что нужно сделать. Это не имеет абсолютно никакого отношения к искажению имен, пространствам имен или любым другим странным ответам здесь (как вы уже знали, когда спрашивали).

0 голосов
/ 08 февраля 2011

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

Функция thread_proxy, указанная выше, имеет внешнюю связь (то есть видна за пределами своей единицы перевода), поскольку пространства имен не влияют на функции extern "C" - даже анонимные пространства имен. Вместо этого, чтобы задать внутреннюю связь функции thread_proxy, необходимо объявить ее как статическую:

namespace boost {
    namespace {
        extern "C" {
            static void *thread_proxy(void *f)
            {
                ....
            }
        } // extern "C"
    } // anonymous
    ...
} // boost

[Edit] Обратите внимание, что Boost включил это изменение. Смотри https://svn.boost.org/trac/boost/ticket/5170.

0 голосов
/ 07 апреля 2010

Возможно, потому что вы взаимодействуете с простой библиотекой C - pthreads.

...