2013-03-22 5 views
11

Я экспериментирую с видимостью символов C++ в 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 = скрытый, но я никогда не получал подобные предупреждения с помощью последнего. Я вижу, что когда я передаю -fvisibility = hidden в приложение, класс в приложении также будет скрыт, поэтому я не получу предупреждение. Но когда я не передаю этот параметр, ни один из символов в заголовках не будет скрыт от компилятора, поэтому я не получу предупреждение снова.

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

ответ

16

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

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

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

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

+0

Спасибо за подробный ответ. Просто для любопытства, меня интересуют, какие символы нужен компилятору, который не может быть сгенерирован? Насколько я знаю в тривиальных классах, может генерироваться даже типinfo. При использовании vis = hidden вы не получите предупреждения, даже если вы скрываете символы, которые не должны быть скрыты, вы просто получаете неопределенную ошибку символа от компоновщика. Использование скрытого пространства имен gcc может обнаружить проблему. Возможно, есть законное использование, чтобы экспортировать только некоторые символы в классе, но gcc все равно выдает предупреждение. Модули C++ Дуга Грегора очень интересны, мне понравилась его презентация, спасибо, что поделились ею. – VargaD

+1

Короче говоря, typeinfo для любого класса с виртуальным всегда испускается, тогда как typeinfo для классов без него испускается только тогда, когда используется typeid() или что-то, что его использует (исключение catch, dynamic_cast <> и т. Д.). Кроме того, большинство компиляторов испускают две или более реализаций конструктора для каждого указанного конструктора программы, и в генерации деструкторов также есть какая-то магия. Короче говоря, -fvisibility = hidden скрывает лот, и ваш метод скроет только указанный программистом материал, а не волшебные внутренние элементы. Большая часть этого начинает упрощаться с C++-модулями, хотя и не решена. Niall –

0

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

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

+0

Спасибо за ваш ответ. Я не вижу _why_ весь класс должен быть экспортирован. У большинства классов нет даже vtable. Я много тестовых приложений даже пытался выбросить не экспортированное исключение, но все они работали очень хорошо. Я пытаюсь понять, почему его нужно экспортировать. Знаете ли вы, в каких обстоятельствах это не удается? Можете ли вы показать пример? – VargaD

+0

Основная проблема заключается в том, что в C++ классы имеют связь; если класс имеет скрытую видимость, это означает, что вы не собираетесь использовать его вне своего собственного DSO. И поэтому предоставление функции-члена большей видимости, чем ее класс, не имеет смысла, так как вам нужно использовать класс для использования функции-члена. Почему вы не хотите экспортировать сам класс? –

+0

Моя проблема заключается в том, что экспорт всего класса не поддерживается, символы легко экспортируются. В C, использующем -fvisibility-hidden, вы просто экспортируете символ, который хотите экспортировать, но на C++ вы должны экспортировать весь класс и скрыть все символы, которые вы не хотите экспортировать. Я вижу, что классы, имеющие vtable, должны быть экспортированы. Я много раз читал, что в C++ видимость символов может вызвать много проблем, и я не должен скрывать классы, которые экспортировали методы, но я не понимаю, почему. Я старался создавать ошибки времени выполнения из-за видимости, но я не мог. – VargaD

Смежные вопросы