Макроопределения для заголовков, куда поместить их?

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

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

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

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

13
задан Matt Joiner 2 August 2010 в 03:43
поделиться

8 ответов

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

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

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

3
ответ дан 2 December 2019 в 01:30
поделиться

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

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

1
ответ дан 2 December 2019 в 01:30
поделиться

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

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

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

3
ответ дан 2 December 2019 в 01:30
поделиться

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

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

Они определенно раздражают, чтобы успевать за ними.

1
ответ дан 2 December 2019 в 01:30
поделиться

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

Связанные части информации должны располагаться рядом, чтобы их было легче идентифицировать. Классическим примером является то, что в языке 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"

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


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

#define BITCOUNT 8
#include "bitio.h"

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

0
ответ дан 2 December 2019 в 01:30
поделиться

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

Например,

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

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


`#include "bool.h"`
// blah
0
ответ дан 2 December 2019 в 01:30
поделиться

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

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

0
ответ дан 2 December 2019 в 01:30
поделиться

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

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

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

__РАСШИРЕНИЯ__ Включите общие расширения в Solaris.

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

_ТАНДЕМ_ИСТОЧНИК Включите расширения для платформы HP NonStop.

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

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

_POSIX_1_ИСТОЧНИК Включите дополнительные функции Posix для Minix.

_МИНИКС Определите платформу Minix. Этот конкретный макрос препроцессора устарела и может быть удалена в будущем выпуске Автоконф.

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

— Макрос: AC_SYS_LARGEFILE

Устроить для 6 смещения файлов, известные как поддержка больших файлов. На некоторых хостах необходимо использовать специальные опции компилятора для создания программ, которые могут обращаться к большим файлам. Добавьте любые такие параметры к выходной переменной 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, чтобы сделать их прототипы доступными при их использовании, и включена поддержка больших файлов.

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

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

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

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

1
ответ дан 2 December 2019 в 01:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: