2012-05-24 4 views
2

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

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

Приведем пример. У вас есть библиотека, которая делает некоторые вещи и в разных исходных файлах, вам может понадобиться copy_file и create_directory, чтобы вы реализовали их как служебные функции.

Чтобы убедиться, что пользователь вашей библиотеки случайно не получить ошибку сцепления из-за наличие функции с тем же именем, что я могу думать о следующих решениях:

  • страшному: Копировать вставить функции для каждого файла, который их использует, добавив static к их объявлению.
  • Не очень хороший способ: Напишите их как макросы. Мне нравятся макросы, но это просто не так.
  • Дайте им такое странное имя, что шансы пользователя с тем же именем будут достаточно малы. Это может сработать, но делает код очень уродливым.
  • Что я делаю сейчас: Напишите их как static функции во внутреннем файле utils.h и включите этот файл в исходные файлы.

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

Что я прибегал сделать что-то вроде этого:

utils.h:

... 
#ifdef USE_COPY_FILE 
static int copy_file(/* args */) 
{...} 
#endif 

#ifdef USE_CREATE_DIR 
static int create_dir(/* args */) 
{...} 
#endif 
... 

file1.c:

#define USE_COPY_FILE 
#define USE_CREATE_DIR 
#include "utils.h" 

/* use both functions */ 

file2.c

#define USE_COPY_FILE 
#include "utils.h 

/* use only copy_file */ 

Проблема с этим методом, однако, я что он начинает становиться уродливым, поскольку вводятся больше утилит. Представьте, что если у вас есть 10 таких функций, вам нужно иметь 7 ~ 8 строк определения до включения, если вам нужно 7 ~ 8 из этих функций!

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

В любом случае, он не выглядит элегантным.

Вопрос в том, как у вас есть функции, которые являются внутренними в вашей собственной библиотеке, используемые несколькими единицами перевода и избегают внешних связей?

+0

С инструментальной связкой GNU, по крайней мере, вы можете пометить символы как «местные». –

+0

@OliCharlesworth Я использую 'gcc'. Постарайтесь объяснить, как это сделать? (Желательно в ответе) – Shahbaz

+0

Я уже собирался, но потом заметил, что это дубликат: [Ограничение символов в статической библиотеке Linux] (http://stackoverflow.com/questions/393980/restricting-symbols-in-a-linux -static-library) –

ответ

3

Маркировка функций static inline вместо static заставит предупреждения уйти. Он ничего не сделает о раздувании кода вашего текущего решения - вы помещаете по крайней мере одну копию функции в каждый TU, который ее использует, и это все равно будет. Оли говорит в комментарии, что компоновщик может быть достаточно умным, чтобы объединить их. Я не говорю, что это не так, но не рассчитывайте на это :-)

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

Это в основном лучшее, что вы можете сделать портативно. В стандарте C нет способа определить символ, который является внешним от POV определенных TU (ваш), но не от POV других (ваших пользователей). Стандарт C действительно не заботится о том, какие библиотеки, или о том, что TU могут быть связаны несколькими шагами, или о различии между статическими и динамическими связями. Поэтому, если вы хотите, чтобы функции были фактически разделены между вашими TU, без какого-либо внешнего символа, который мог бы помешать пользователям библиотеки, тогда вам нужно сделать что-то конкретное для GCC и/или вашей статической библиотеки или библиотеки dll для удаления символов один раз библиотека создается, но до того, как пользователь свяжется с ней.

+0

Я смотрю в стандарте C99, и я вижу их предложение для предупреждений в Приложении I: _ Объект определен, но не используется_, но я не могу найти нигде, где можно было бы сказать, что 'static inline' должен _not_ генерировать предупреждение. Как я вижу это, неиспользуемая функция 'static inline' по-прежнему является« определенной функцией, которая не используется ». Ваш ответ основан исключительно на gcc, или это четко определенное поведение? – Shahbaz

+0

@Shahbaz: за исключением необходимой диагностики, предупреждения не определены. Это включает те, которые рекомендованы в стандарте, но не требуются. –

+0

Встроенные функции обычно определяются в файлах заголовков. Нет смысла предполагать, что они будут использоваться всякий раз, когда файл включен. Компиляторы не будут предупреждать о известной хорошей практике. Но для 'gcc' вы можете использовать' attribute ((unused)) '. Это означает, что функция должна быть, возможно, не использована, и подавляет предупреждение. – ugoren

2

Обычно вы можете связать свою библиотеку с глобальными функциями и локализовать их позже.

objcopy может принимать глобальные символы и делать их локальными, поэтому их нельзя связать. Он также может удалить символ (функция остается, разрешенные ссылки на нее остаются разрешенными, просто имя отсутствует).

objcopy -L symbol локализован symbol. Вы можете повторить -L несколько раз.
objcopy -G symbol сохраняет symbol глобальный, но локализует все остальные. Вы также можете повторить его, и он будет поддерживать глобальные все указанные вами.

И я только что нашел, что я повторяю ответ this question, на который ссылается Оли Чарлворт в своем комментарии.

+0

'objcopy' интересен, но я думаю, что буду придерживаться другого решения, поскольку он переносится – Shahbaz

+0

Ну, по-видимому, это не совсем портативно. Я должен был подумать об этом. – Shahbaz