Видимость встроенных функций шаблона - PullRequest
0 голосов
/ 06 сентября 2018

Что является рациональным в том, что компиляция

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:
        *;
};

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

1 Ответ

0 голосов
/ 06 декабря 2018

Два аспекта перед ответом:

1) Если вы хотите, чтобы ваша встроенная или шаблонная функция использовалась за пределами вашего модуля, они должны идти в файле .h {pp} - для шаблонов они будут определены в каждом модуле, который их использует;

2) Если вы используете только определенные функции шаблона на текущем модуле компиляции, вы можете объявить их статическими - нет необходимости объявлять статические встроенные функции, если вы не используете указатели на функции для ссылки на них.

Если вы заявили, что не хотите, чтобы компилятор генерировал для вас дополнительную информацию, это означает, что вы не хотите отлаживать информацию. Скомпилируйте с флагами -Os, -O2 или -O3, и все будет в порядке.

...