Подтвердить что ты не робот

Зачем вам нужен "extern C" для обратных вызовов С++ для функций C?

Я нахожу такие примеры в коде 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-вызова. См. 7.5 (p4) стандарта С++.

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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


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

Ответ 5

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

Ответ 6

Так как 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

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