Макро определения заголовков, где их разместить? - PullRequest
13 голосов
/ 02 августа 2010

При определении макросов, на которые опираются заголовки, например, _FILE_OFFSET_BITS, FUSE_USE_VERSION, _GNU_SOURCE, где лучше всего их разместить?

Некоторые возможности, которые я рассмотрел, включают

  1. Вверху любых исходных файлов, которые основаны на определениях, предоставляемых заголовками, включенными в этот файл
  2. Непосредственно перед включением для соответствующих заголовков (заголовков)
  3. Определить на уровне CPPFLAGS через компилятор? (например, -D_FILE_OFFSET_BITS=64) для:
    1. Весь репозиторий
    2. Весь проект
    3. Только источники, которые в этом нуждаются
  4. В заголовках проекта, которые также должны включать те соответствующие заголовки, к которым применяются макросы
  5. В каком-то другом месте, о котором я не задумывался, но бесконечно лучше

Примечание. Обоснование применимости сборки, автоинструментов и других систем сборки является одним из факторов, повлиявших на мое решение.

Ответы [ 8 ]

3 голосов
/ 11 августа 2010

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

Если вы используете предварительно скомпилированные заголовки и имеете предварительно скомпилированный заголовок, который должен поэтомусначала включите его в каждый исходный файл (например, stdafx.h для проектов MSVC), а затем вставьте их туда.

Для макросов, которые влияют на автономные библиотеки (сторонние или написанные вами),Я хотел бы создать заголовок оболочки, который определяет макросы, а затем включает в себя заголовок библиотеки.Все случаи использования библиотеки из вашего проекта должны включать ваш заголовок оболочки, а не включать заголовок библиотеки напрямую.Это позволяет избежать ненужного определения макросов и дает понять, что они относятся к этой библиотеке.Если между библиотеками есть зависимости, вы можете сделать макросы глобальными (в системе сборки или предварительно скомпилированном заголовке), чтобы быть в безопасности.

3 голосов
/ 10 августа 2010

Ну, это зависит.

Большинство, я бы определил через командную строку - в Makefile или в любой другой системе сборки, которую вы используете.

Что касается _FILE_OFFSET_BITS Я бы на самом деле не определял это явно, а скорее использовал бы getconf LFS_CFLAGS и getconf LFS_LDFLAGS.

1 голос
/ 24 августа 2010

Для _GNU_SOURCE и, в частности, для автоинструментов, вы можете использовать AC_USE_SYSTEM_EXTENSIONS (здесь приведена полная ссылка на руководство по автоконфигурации):

- Макрос: AC_USE_SYSTEM_EXTENSIONS
Этот макрос был введен в Autoconf 2.60. Если возможно, включите расширения на C или Posix на хостах, которые обычно отключают расширения, как правило, из-за пространства имен соответствия стандартам проблемы. Это следует вызывать перед любыми макросами, которые запускают C компилятор. Следующие макросы препроцессора определены где соответствующим образом:

_GNU_SOURCE Включить расширения в GNU / Linux.

__EXTENSIONS__ Включить общие расширения в Solaris.

_POSIX_PTHREAD_SEMANTICS Включить потоковые расширения в Solaris.

_TANDEM_SOURCE Включить расширения для платформы HP NonStop.

_ALL_SOURCE Включить расширения для AIX 3 и Interix.

_POSIX_SOURCE Включите функции Posix для Minix.

_POSIX_1_SOURCE Включите дополнительные функции Posix для Minix.

_MINIX Определить платформу Minix. Этот конкретный макрос препроцессора устарела и может быть удалена в будущем выпуске Autoconf.

Для _FILE_OFFSET_BITS вам необходимо позвонить AC_SYS_LARGEFILE и AC_FUNC_FSEEKO:

- Макрос: AC_SYS_LARGEFILE

Организация 64-битных смещений файлов, известных как поддержка больших файлов. На некоторых хостах необходимо использовать специальные параметры компилятора для создания программ, которые могут обращаться к большим файлам. Добавьте любые такие параметры в выходную переменную CC. Определите _FILE_OFFSET_BITS и _LARGE_FILES, если необходимо.

Поддержка больших файлов может быть отключена путем настройки опции --disable-largefile.

Если вы используете этот макрос, убедитесь, что ваша программа работает, даже если off_t шире, чем long int, так как это часто встречается, когда включена поддержка больших файлов. Например, неправильно печатать произвольное off_t значение X с printf("%ld", (long int) X).

LFS представила функции fseeko и ftello, чтобы заменить их аналоги C fseek и ftell, которые не используют off_t. Старайтесь использовать AC_FUNC_FSEEKO, чтобы сделать их прототипы доступными при их использовании, и поддержка больших файлов включена.

Если вы используете autoheader для генерации config.h, вы можете определить другие макросы, которые вас интересуют, используя AC_DEFINE или AC_DEFINE_UNQUOTED:

AC_DEFINE([FUSE_VERSION], [28], [FUSE Version.])

Определение будет передано в командную строку или помещено в config.h, если вы используете autoheader. Реальное преимущество AC_DEFINE состоит в том, что он легко позволяет определения препроцессора в результате проверок конфигурации и отделяет системно-специфический раскол от важных деталей.

При записи файла .c сначала #include "config.h", затем заголовок интерфейса (например, foo.h для foo.c - это гарантирует, что заголовок не имеет отсутствующих зависимостей), а затем все остальные заголовки.

1 голос
/ 10 августа 2010

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

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

Они, конечно, раздражают, чтобы идти в ногу.

1 голос
/ 02 августа 2010

Я бы всегда помещал их в командную строку через CPPFLAGS для всего проекта.Если вы разместите их в другом месте, существует опасность, что вы можете забыть скопировать их в новый исходный файл или включить системный заголовок, прежде чем включать заголовок проекта, который их определяет, и это может привести к чрезвычайно неприятным ошибкам (например, декларация одного файла).устаревший 32-битный struct stat и передача его адреса функции в другом файле, который ожидает 64-битный struct stat).

Кстати, действительно смешно, что _FILE_OFFSET_BITS=64 по-прежнему не используется по умолчаниюна glibc.

0 голосов
/ 11 августа 2010

Глобальные, общие для проекта константы, которые являются конкретными для цели, лучше всего поместить в CCFLAGS в вашем make-файле. Константы, которые вы используете повсеместно, могут находиться в соответствующих заголовочных файлах, которые включены в любой файл, который их использует.

Например,

// bool.h - a boolean type for C
<code>#ifndef __BOOL_H__
</code>#define <strong>BOOL_H</strong>
typedef int bool_t
<code>#define TRUE 1</code>
<code>#define FALSE 0</code>
<code>#endif

Затем, в каком-то другом заголовке,


`#include "bool.h"`
// blah
0 голосов
/ 11 августа 2010

Использование файлов заголовков - это то, что я рекомендую, потому что оно позволяет вам создавать базу кода, созданную файлами make и другими системами сборки, а также проектами IDE, такими как Visual Studio. Это дает вам единую точку определения, которая может сопровождаться комментариями (я фанат doxygen , который позволяет вам генерировать документацию макроса ).

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

0 голосов
/ 11 августа 2010

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

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

void something (void) {
    // 600 lines of code here
    int x = fn(y);
    // more code here
}

намного лучше, чем:

void something (void) {
    int x;
    // 600 lines of code here
    x = fn(y);
    // more code here
}

поскольку в последнем случае вам не нужно искать тип x.


Например, если вам нужно несколько раз скомпилировать один исходный файл с разными значениями, у вас есть , чтобы сделать это с компилятором:

gcc -Dmydefine=7 -o binary7 source.c
gcc -Dmydefine=9 -o binary9 source.c

Однако, если каждая компиляция этого файла будет использовать 7, он может быть перемещен ближе к месту, где он используется:

source.c:
    #include <stdio.h>

    #define mydefine 7
    #include "header_that_uses_mydefine.h"

    #define mydefine 7
    #include "another_header_that_uses_mydefine.h"

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


И, если выВы уверены, что никогда не будет включать (например) bitio.h без первой установки BITCOUNT в 8, вы даже можете пойти так далеко, что создадите файл bitio8.h, содержащий только:

#define BITCOUNT 8
#include "bitio.h"

, а затем просто включите bitio8.h в свои исходные файлы.

...