Передовой опыт и семантика вложенных функций пространства имен и использование extern C

Я создаю библиотеку C++ с интерфейсом C-ABI.

Вот как GCC обрабатывает квалификатор extern "C" в отношении искажения:

namespace x {

    extern "C" int monkey(int x) {
        return 1;
    }

    int chimpanzee(int x) {
        return 1;
    }
}

Соответствующий вывод nm:

00000000004005cd T _ZN1x10chimpanzeeEi
00000000004005bf T monkey

Вопрос: я хочу оставить функции, участвующие в C-ABI, внутри пространства имен для максимальной гибкости повторного использования. Важное примечание. После компиляции библиотеки я передам компоновщику файл карты (GCC) или файл определения модуля (MSVC).

  1. Является ли стандартное поведение искажения вывода - будут ли другие основные компиляторы (в частности, MSVC) также удалять искажения?
  2. есть ли у них подводные камни или передовой опыт в отношении размещения функций в пространстве имен, когда они участвуют во внешнем ABI?
  3. Будет ли это мешать экспорту декодированных функций C-ABI во время компоновки?

person Hassan Syed    schedule 15.11.2010    source источник


Ответы (2)


Это для MSVC.

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

Отвечая на ваши конкретные вопросы, прыгая вокруг:

1) Стандартное поведение в отношении изменения имени отсутствует. Что на самом деле говорит Стандарт, так это то, что реализации обеспечивают C-совместимую компоновку для конструкций extern "C":

7.5.3 [Спецификации связи]

Каждая реализация должна обеспечивать связь с функциями, написанными на языке программирования C, "C", и связь с функциями C++, "C++". [Пример:

complex sqrt(complex); // C + + linkage by default 
extern "C" { double sqrt(double); // C linkage } 

— конец примера]

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

3) Да, у вас могут быть проблемы со связью. Попробуй это:

main.h

#ifndef MAIN_API
#   define MAIN_API __declspec(dllexport)
#endif

namespace x
{
    extern "C" MAIN_API void foo();
};

namespace y
{
    extern "C" MAIN_API void foo();
};

main.cpp

#include <cstdlib>
#include <iostream>
using namespace std;
#define MAIN_API __declspec(dllexport)
#include "main.h"

void x::foo()
{
    cout << "x::foo()\n";
}

void y::foo()
{
    cout << "y::foo()\n";
}

int main()
{
}

Это вызовет ошибку компоновщика, потому что версии x::foo() и y::foo() с extern "C" потеряли свою идентификацию пространства имен, поэтому они имеют точно такое же имя: foo()

2) Лучшие практики в этом отношении. Если вы должны экспортировать C-ABI для функций в пространствах имен, вы должны быть осторожны, чтобы имена, которые вы в конечном итоге экспортируете, не совпадали. В какой-то степени это противоречит цели использования namespace в первую очередь. Но вы можете сделать что-то вроде этого:

#ifndef MAIN_API
#   define MAIN_API __declspec(dllexport)
#endif

namespace x
{
    extern "C" MAIN_API void x_foo();
};

namespace y
{
    extern "C" MAIN_API void y_foo();
};
person John Dibling    schedule 15.11.2010
comment
Спасибо, я рад, что подводные камни относительно просты и, как правило, основаны на здравом смысле — возможно, я буду использовать префикс в стиле c для функций, формирующих интерфейс C-ABI. - person Hassan Syed; 15.11.2010

То, что вы делаете, прекрасно и даст вам желаемый эффект. Из Язык программирования C++, 3-е издание, стр. 208: "Имя со связью C может быть объявлено в пространстве имен. Пространство имен повлияет на способ доступа к имени в программе C++, но не как его видит компоновщик. Типичным примером является printf() из std. … Даже при вызове с std::printf() это все тот же старый C printf()».

person Steve M    schedule 15.11.2010