Лучшие практики и семантика вложенных функций пространства имен и использование extern "C" - PullRequest
3 голосов
/ 15 ноября 2010

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

Вот как GCC обрабатывает внешний квалификатор "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 во время соединения?

Ответы [ 2 ]

5 голосов
/ 15 ноября 2010

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

2 голосов
/ 15 ноября 2010

Это для 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 не имеет понятия namespace s, если 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()
{
}

Это выдаст ошибку компоновщика, так как extern "C" -эд версия x::foo() и y::foo() потеряли свою идентификацию пространства имен, поэтому в итоге они получили одно и то же имя: 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();
};
...