Что является рациональным в том, что компиляция
namespace ns __attribute__((visibility("default"))) {
template<typename T>
inline int func1(const T& x) {
return x;
}
inline int func2(int x) {
return x;
}
struct test {
template<typename T>
int func3(const T &x) { return x; }
int func4(int x) { return x; }
};
}
int __attribute__((visibility("default"))) client(int x) {
ns::test t;
const int y1 = ns::func1(x);
const int y2 = ns::func2(x);
const int y3 = t.func3(x);
const int y4 = t.func4(x);
return y1 + y2 + y3 + y4;
}
с
g++ -Wall -fPIC \
-fvisibility=hidden -fvisibility-inlines-hidden \
-shared -o libtest.so test.cpp
возвращает библиотеку, экспортирующую ns::test::func1<int>()
и ns::test::func3<int>()
, но не ns::func2()
или ns::test::func4()
? Обе функции шаблона определены inline
, а -fvisibility-inlines-hidden
указывает компилятору скрыть их & mdash; или, по крайней мере, их экземпляры, которые, надеюсь, тоже встроены.
Явное скрытие шаблонов функций func1
и func3
, т.е. с
template<typename T>
int __attribute__((visibility("hidden"))) func(const T &x) { return x; }
приводит к ожидаемому поведению. Отказ от видимости по умолчанию в определении пространства имен скрывает два экземпляра.
Фон: Мы стараемся свести к минимуму количество видимых символов в нашей библиотеке. Таким образом, мы используем упомянутые флаги и атрибуты компилятора. Конечно, это также необходимо для всех статических сторонних библиотек. Но, к сожалению, эти встроенные функции шаблона внутри включенных заголовочных файлов полностью находятся вне нашего контроля. Например, каждый экземпляр
namespace std {
template<typename _CharT, typename _Traits, typename _Alloc>
inline bool
operator==(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
const _CharT* __rhs)
{ return __lhs.compare(__rhs) == 0; }
}
из #include <string>
с радостью сгенерирует публично видимый символ внутри моей библиотеки. И самая раздражающая часть, она будет помещена в мою библиотеку без какой-либо информации о версии, так что нет GLIBCXX_3.x.z
различий.
Бонусный вопрос: Какое общее влияние дает использование сценария компоновщика, например
{
global:
_Z6clienti;
local:
*;
};
Насколько я понимаю, это действительно возможно, только если я не использую обработку исключений или динамическое приведение типов через границы моей библиотеки. Но что происходит с этими встроенными функциями? Такое ощущение, что вся эта скрытая вещь в любом случае нарушает одно правило определения, поэтому это не имеет большого значения.