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

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

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

  1. в верхней части любых исходных файлов, которые полагаются на определения, предоставляемые заголовками, включенными в этот файл
  2. непосредственно перед включением для соответствующего заголовка(ов)
  3. определить в

8 ответов


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

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

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


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

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

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


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

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


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

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

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


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

-- Macro:AC_USE_SYSTEM_EXTENSIONS
Этот макрос был представлен в Autoconf 2.60. Если возможно, включите расширения для C или Posix на хостах, которые обычно отключают расширения, обычно из-за пространства имен standards-conformance проблемы. Это должно быть вызвано перед любыми макросами, которые запускают 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 при необходимости.

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

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

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


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

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

#define BITCOUNT 8
#include "bitio.h"

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


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

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


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

например,

// 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