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

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

До OS X v10.4, было два механизма для управления видимостью символа. Первый метод должен был объявить отдельные символы как частные к библиотеке, но внешние к текущему файлу с помощью __private_extern__ ключевое слово. Это ключевое слово могло использоваться в тех же местах, Вы будете использовать любого static или extern ключевые слова. Второй метод должен был использовать список экспорта.

Список экспорта является файлом, содержащим имена символов, которые Вы явно хотите скрыть или показать. Несмотря на то, что имена символа в C легко определяются (путем предварительного ожидания символа подчеркивания к имени), решения, что имена символа в C++ намного более сложны. Из-за классов и пространств имен, компиляторы должны включать больше информации для идентификации каждого символа уникально, и таким образом, компиляторы создают то, что известно как скорректированное имя для каждого символа. Это скорректированное имя является часто зависимым от компилятора, трудным вывести, и трудный найти в большом списке символов определенный Вашей библиотекой.

К счастью GCC 4.0 обеспечивает некоторые новые способы изменить видимость символов. Следующие разделы описывают эти новые методы вместе с причинами, почему это могло бы быть важно для Вас.

Используя GCC 4.0 для отмечания видимости символа

Начало с OS X v10.4, сокрытие имен символа C++ намного проще. Компилятор GCC 4.0 поддерживает новые опции для сокрытия или показа символов и также поддерживает новую прагму и атрибуты компилятора для изменения видимости символов в Вашем коде.

Флаги компилятора

GCC 4.0 поддерживает новый флаг для установки видимости по умолчанию символов в файле. -fvisibility=параметр компилятора vis позволяет Вам установить видимость для символов в текущей компиляции. Значение для этого флага может быть также default или hidden. Когда установлено в default, символы, не явно отмеченные, как скрытый, сделаны видимыми. Когда установлено в hidden, скрыты символы, не явно отмеченные как видимые. Если Вы не указываете -fvisibility флаг во время компиляции, компилятор принимает default видимость.

Компилятор также поддерживает -fvisibility-inlines-hidden флаг для того, чтобы вынуждать все подставляемые функции быть скрытым. Вы могли бы использовать этот флаг в ситуациях, где Вы хотите использовать видимость по умолчанию для большинства элементов, но все еще хотеть скрыть все подставляемые функции. Для получения дополнительной информации, почему это могло бы быть необходимо для подставляемых функций, посмотрите Видимость Подставляемых функций.

Атрибуты видимости

При компиляции кода с GCC 4.0 можно отметить отдельные символы как значение по умолчанию или скрытое использование атрибута видимости:

__attribute__((visibility("default"))) void MyFunction1() {}
__attribute__((visibility("hidden"))) void MyFunction2() {}

Атрибуты видимости переопределяют значение, указанное с -fvisibility флаг во время компиляции. Таким образом, добавление default атрибут видимости заставляет символ экспортироваться во всех случаях, тогда как добавление hidden атрибут видимости скрывает его.

Атрибуты видимости могут быть применены к функциям, переменным, шаблонам и классам C++. Если класс отмечен, как скрытый, все его функции членства, статические задействованные переменные, и сгенерированные компилятором метаданные, такие как таблицы виртуальной функции и информация о RTTI, также скрыты.

Чтобы продемонстрировать, как эти атрибуты работают во время компиляции, смотрите на следующие объявления:

int a(int n) {return n;}
 
__attribute__((visibility("hidden"))) int b(int n) {return n;}
 
__attribute__((visibility("default"))) int c(int n) {return n;}
 
class X
{
    public:
        virtual ~X();
};
 
class __attribute__((visibility("hidden"))) Y
{
    public:
        virtual ~Y();
};
 
class __attribute__((visibility("default"))) Z
{
    public:
        virtual ~Z();
};
 
X::~X() { }
Y::~Y() { }
Z::~Z() { }

Компиляция этого кода с -fvisibility=default флаг вызвал бы символы для функций a и c и классы X и Z экспортироваться библиотекой. Компиляция этого кода с -fvisibility=hidden флаг вызвал бы символы для функции c и класс Z экспортироваться.

Используя атрибут видимости к символам метки, поскольку видимый или скрытый лучшая практика, чем использование __private_extern__ ключевое слово для сокрытия отдельных символов. Используя __private_extern__ ключевое слово проявляет подход представления всех символов по умолчанию и затем выборочно сокрытия, которые являются частными. В крупной совместно используемой библиотеке обратный подход обычно лучше. Таким образом обычно лучше скрыть все символы, и затем выборочно представлять тех Вы хотите, чтобы клиенты использовали.

Для упрощения задачи маркировки символов для экспорта Вы могли бы также хотеть определить макрос с помощью default набор атрибута видимости, такой как в следующем примере:

#define EXPORT __attribute__((visibility("default")))
 
// Always export the following function.
EXPORT int MyFunction1();

Преимущество использования макроса состоит в том, что, если Ваш код также компилируется на других платформах, можно изменить макрос на надлежащие ключевые слова для компиляторов на других платформах.

Прагмы

Другой способ отметить символы как значение по умолчанию или скрытый с новой прагмой в GCC 4.0. Прагма видимости GCC имеет преимущество способности отметить блок функций быстро без потребности применить атрибут видимости к каждому. Использование этой прагмы следующие:

void f() { }
 
#pragma GCC visibility push(default)
void g() { }
void h() { }
#pragma GCC visibility pop

В этом примере, функциях g и h отмечены как значение по умолчанию и поэтому экспортируются независимо от -fvisibility флаг, в то время как функция f соответствует любому значению, установлен для -fvisibility флаг. Как имена push и pop предложите, эта прагма может быть вложена.

Причины ограничения видимости символа

Это - хорошая практика для экспорта как можно меньшего количества символов из динамических совместно используемых библиотек. Экспорт ограниченного набора символов улучшает модульный принцип программы и скрывает подробные данные реализации. Сокращение количества символов в Ваших библиотеках также уменьшает место Вашей библиотеки и сокращает объем работы, который должен быть сделан динамическим компоновщиком. С меньшим количеством символов, чтобы загрузиться и решить, динамический компоновщик в состоянии разбудить Вашу программу и работающий более быстро.

Причины того, чтобы сделать символы видимыми

Несмотря на то, что вероятно, что большинство символов C++ в Вашей совместно используемой библиотеке не должно быть видимо, существуют некоторые ситуации, куда действительно необходимо экспортировать их:

Видимость подставляемых функций

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

Несмотря на то, что можно применить атрибут видимости (см. Атрибуты Видимости) к подставляемым функциям в C++ так же, как Вы можете любой другой символ, обычно лучше скрыть все подставляемые функции. Некоторые сложные вопросы возникают при экспорте подставляемых функций из динамических совместно используемых библиотек. Поскольку существует несколько переменных, вовлеченных в решение компилятора испустить функцию или встроить его, можно столкнуться с ошибками при создании клиентов для различных сборок совместно используемой библиотеки.

Также важно помнить, что существуют тонкие различия между семантикой подставляемой функции для C и C++. В программах C только один исходный файл может обеспечить определение из строки для подставляемой функции. Это означает, что программисты C имеют точный контроль, где находятся копии из строки. Таким образом для динамической совместно используемой библиотеки на базе С, возможно экспортировать только одну копию подставляемой функции. Для C++ определение подставляемой функции должно быть включено в каждую единицу перевода, использующую функцию. Так, если компилятор действительно испускает копию из строки, может потенциально быть несколько копий функции, находящейся в различных единицах перевода.

В конце, если Вы хотите скрыть все подставляемые функции (но не обязательно весь Ваш другой код), можно использовать -fvisibility-inlines-hidden отметьте при компиляции кода. Если Вы уже передаете -fvisibility=hidden отметьте к компилятору, использованию -fvisibility-inlines-hidden флаг является ненужным.

Видимость символа и Objective C

Objective C является строгим надмножеством C, и Objective C ++ является строгим надмножеством C++. Это означает, что все обсуждение относительно видимости символа в C и C++ применяется к Objective C и Objective C ++ также. Можно использовать флаги компилятора, атрибуты видимости и прагму видимости для сокрытия C и кода C++ в файлах кода Objective C.

В 32-разрядном проекте OS X эти средства управления видимостью применяются только к C или подмножеству C++ Вашего кода. Они не применяются к классам Objective C и методам. Класс Objective C и имена сообщения связываются временем выполнения Objective C, не компоновщиком, таким образом, понятие видимости не применяется к ним. Нет никакого механизма для сокрытия класса Objective C или метода, определенного в динамической библиотеке от клиентов той библиотеки.

При создании для x86_64 OS X или для iOS, видимость символа действительно влияет на объективные-C классы. Сокрытие класса не является панацеей безопасности — инициативные разработчики могут получить доступ к любому классу с объективными-C вызовами во время выполнения — но если Вы непосредственно сошлетесь на класс, видимость которого скрыта в библиотеке, с которой Вы соединяетесь, то Вы получите ошибку компоновщика. Это означает, что, если данный класс предназначается, чтобы быть применимым за пределами библиотеки или исполнимой программы, это определяется в, необходимо гарантировать надлежащую видимость символа.