Определения макросов для заголовков, куда их поместить?
при определении макросов, на которые полагаются заголовки, например _FILE_OFFSET_BITS
, FUSE_USE_VERSION
, _GNU_SOURCE
среди других, где лучшее место, чтобы положить их?
некоторые возможности, которые я рассмотрел, включают
- в верхней части любых исходных файлов, которые полагаются на определения, предоставляемые заголовками, включенными в этот файл
- непосредственно перед включением для соответствующего заголовка(ов)
- определить в
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
функции для замены своих коллег Cfseek
и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