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

Видимость символов и пространство имен

Я экспериментирую с видимостью символа С++ в Linux и gcc. Кажется, что предпочтительный способ - использовать -fvisibility = hidden и экспортировать используемые символы один за другим в соответствии с вики-страницей видимости gcc (http://gcc.gnu.org/wiki/Visibility). Моя проблема в том, что многие библиотеки не справляются с этим, они забывают явно экспортировать символы, что является серьезной проблемой. После нескольких исправленных ошибок даже некоторые части повышения могут все еще быть затронуты. Конечно, эти ошибки должны быть исправлены, но до этого я хотел бы использовать "безопасный" способ скрыть как можно больше символов.

Я придумал решение: я помещаю все символы в пространство имен, и на этом я использую атрибут hide hide и экспортирую публичный интерфейс, таким образом меня могут затронуть только мои символы.

Проблема заключается в том, что я получил предупреждающее сообщение, когда компилирую что-то против этой библиотеки для каждого класса, который я не экспортировал, и использую в приложении как поле класса.

namespace MyDSO __attribute__ ((visibility ("hidden"))) {
  struct Foo {
    void bar() __attribute__ ((visibility ("default"))) {}
  };
}

struct Bar {
  MyDSO::Foo foo;
};

int main() {}

Предупреждающее сообщение может быть воспроизведено в этом маленьком примере, но, конечно, пространство имен должно быть в библиотеке другого класса в приложении.

$ gcc-4.7.1 namespace.cpp -o namespace
namespace.cpp:7:8: warning: ‘Bar’ declared with greater visibility than the type of its field ‘Bar::foo’ [-Wattributes]

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

Что предлагает это предупреждение? Это серьезная проблема? В каких ситуациях это может вызвать проблемы? Как скрыть пространство имен отличается от fvisibility = hidden?

4b9b3361

Ответ 1

Прежде чем ответить на ваш конкретный вопрос, я должен упомянуть для других, что применение атрибутов видимости символов для пространства имен является специфичной для GCC. MSVC поддерживает только dllexport по классам, функциям и переменным, и если вы хотите, чтобы ваш код был портативным, вы должны соответствовать MSVC. Как указано в моем оригинальном руководстве по визуализации символов GCC (тот, который вы связали с веб-сайтом GCC), механизмы dllexport на основе макросов на основе MSVC можно легко повторно использовать для достижения чего-то подобного в GCC, поэтому перенос в MSVC позволит вам управлять видимостью символов "бесплатно" ".

Что касается вашей конкретной проблемы, GCC правильно предупреждает вас. Если внешний пользователь попытался использовать панель открытого типа, почти наверняка нужно использовать все внутри Bar, включая Bar:: foo. По той же причине все частные функции участника, несмотря на то, что они являются частными, должны быть видимыми. Многие удивляются этому, полагая, что частные символы функции участника по определению недоступны для всех, но они забывают, что только потому, что у программиста нет доступа, это не значит, что компилятор не нужен. Другими словами, частные функции-члены являются частными для вас, но не компилятором. Если они появляются в файле заголовка, это обычно означает, что компилятор нуждается в доступе даже в анонимном пространстве имен (которые только анонимны для программистов, а не для компиляторов, которые, как правило, используют хэш содержимого как "реальное" имя пространства имен).

Скрытие пространства имен имеет очень разные последствия для -fvisibility = hidden. Это связано с тем, что GCC выводит много символов выше и выше тех, которые относятся к определенному типу, например. для vtables, для type_info и т.д. -fvisibility = скрытые скрытые материалы, которые вы не можете скрыть каким-либо способом, описанным в компиляторе, и совершенно необходимо загружать два двоичных файла в один и тот же процесс с помощью встречных символов, например. два общих объекта, построенных с использованием разных версий Boost.

Я ценю ваши попытки исправить проблемы, вызванные нарушением видимого символа в ELF, и последствиями для сломанных бинарных файлов С++ и значительной потерей производительности программистов. Однако вы не можете их исправить - они являются ошибками в самом ELF, который был разработан для C, а не С++. Если бы это было утешение, я несколько раз писал об этом на бумаге BlackBerry на этой теме, поскольку проблемы с видимостью символов ELF являются для нас такой же проблемой для BB10, как и для любой крупной корпорации со значительной кодовой базой на С++. Итак, возможно, вы можете увидеть некоторые решения, предлагаемые для С++ 17, особенно если реализация Doug Gregor С++ Modules делает хороший прогресс.

Ответ 2

Ваше использование атрибутов видимости кажется обратным для меня; Я думаю, что у вас будут лучшие результаты с использованием -fvisibility = hidden и добавлением видимости "по умолчанию" к пространству имен декларации библиотеки, поскольку интерфейс библиотеки предположительно имеет видимость по умолчанию или вы не можете использовать его из своего приложения. Если вы не хотите изменять заголовки библиотеки, вы можете использовать видимость #pragma GCC push/pop вокруг #includes.

Кроме того, как говорит Найл, выделение отдельных функций-членов по умолчанию не работает, весь тип Foo должен иметь видимость по умолчанию, если он является частью интерфейса библиотеки.