2015-01-11 4 views
2

Скажем, у меня есть макет папки, как, например:Можете ли вы использовать переменные среды в директивах C/C++?

. 
+-- Project 
    +-- src 
     +-- foo.h 
     +-- foo.cpp 
    +-- test 
     +-- test_foo.c 

И test_foo.c выглядит так:

#include "../src/foo.h" 
#include <stdio.h> 
#include <assert.h> 

int main() { 
    assert(foo() == true); 
    printf("Test complete"); 
    return 0; 
} 

Есть ли способ заменить строку #include "../src/foo.h" с переменной, которая указывает на директорию с исходниками? Например, скажем, в моей среде у меня есть переменная:

PROJECT_SRC="./Project/src/" 

И тогда я мог бы иметь директиву включить как таковую:

#include "PROJECT_SRC/foo.h" 

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

Альтернативой может быть такой инструмент, как CMake, который может это сделать. Или это считается плохой практикой?

+1

Обычно вы обрабатываете такие файлы в своем make-файле и параметр -I, чтобы добавить исправления, рассмотренные для поиска включенных файлов. И да, используйте make (не CMake). –

+0

Почему бы просто не использовать символическую ссылку? Отлично подходит для таких случаев. Отсутствие поддержки в Visual Studio (редактор не распознает, что два разных пути являются одним и тем же файлом) делает его менее практичным для файлов, которые редактируются в Windows, но (1) вы используете Linux, и (2) вы «Не будем редактировать файл по ссылке. –

+0

@ Cheersandhth.-Alf Aaahrg! Мой коллега раздражает всех остальных своей символической системой построения дерева ссылок. Есть намного лучшие способы сделать это с make (например, 'vpath' является одним). –

ответ

6

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

# C PreProcessor flags. This variable is used by make's implicit rules for 
# everything preprocessor-related. 
CPPFLAGS += -I$(PROJECT_PATH) 

и #include заголовки без пути в исходном файле. Это сделает make вызовом компилятора с -Iyour/project/path, который заставит компилятор искать заголовки в your/project/path. То есть, в Makefile вы можете иметь

PROJECT_PATH = foo/bar 
CPPFLAGS = -I$(PROJECT_PATH) 

и в источниках

#include "foobar.h" 

иметь эффект #include "foo/bar/foobar.h".

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

Итак, я не вижу причины, по которой вы хотели бы ссылаться на путь проекта непосредственно в директивах #include в коде; единственное изменение на стороне сборки - это только то, что вы должны пройти -DPROJECT_PATH=foo/bar/ вместо -IPROJECT_PATH=foo/bar/ и что конструкция более хрупкая, чем механизмы, которые на самом деле предназначены для такого рода вещей. Но если вы действительно хотите сделать это, то вот как:

Первая проблема, вы столкнетесь в том, что

#include "foo/bar/" "baz.h" // no dice. 

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

#define HEADER_STRING(s) #s 
#define HEADER_I(path, name) HEADER_STRING(path ## name) 
#define HEADER(path, name) HEADER_I(path, name) 

//        v-- important: no spaces allowed here! 
#include HEADER(PROJECT_PATH,foobar.h) 

Возможно начинать снизу вверх:

#define HEADER_STRING(s) #s 

делает строку из своего аргумента. То есть HEADER_STRING(foo/bar/baz.h) расширяется до "foo/bar/baz.h". Примечательно, что макро-параметры не расширены, поэтому HEADER_STRING(PROJECT_PATH) будет расширяться до "PROJECT_PATH", даже если задан макрос PROJECT_PATH. Это одна из самых распространенных проблем, с которыми вы сталкиваетесь, когда вы пытаетесь сделать что-нибудь сложное с препроцессором, и решение добавить еще один слой, в котором параметры могут быть расширены:

#define HEADER_STRING_I(s) #s 
#define HEADER_STRING(s) HEADER_STRING_I(s) 

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

HEADER_I тогда

#define HEADER_I(path, name) HEADER_STRING(path ## name) 

ленты его аргументы вместе и передает их HEADER_STRING. HEADER_I(foo,bar) - до HEADER_STRING(foobar). Из-за этой проблемы я уже упоминал выше, HEADER_I(PROJECT_PATH,foobar.h) расширяется до HEADER_STRING(PROJECT_PATHfoobar.h), который, в свою очередь, расширяется до "PROJECT_PATHfoobar.h", поэтому нам нужен другой слой для расширения PROJECT_PATH:

#define HEADER(path, name) HEADER_I(path, name) 

Это только добавляет место для path и name параметров в быть расширена. Наконец, с PROJECT_PATH#define г в foo/bar/, HEADER(PROJECT_PATH,foobar.h) расширяется до "foo/bar/foobar.h", и тогда мы можем сказать

#include HEADER(PROJECT_PATH,foobar.h) 

к #include "foo/bar/foobar.h". PROJECT_PATH можно установить в файле makefile и передать с помощью -DPROJECT_PATH=$(some_make_variable).

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

#include HEADER(PROJECT_PATH,foobar.h) 

в конечном счете, расширяется до "foo/bar/ foobar.h" (обратите внимание на пробел), который не работает.

+0

] Благодарим вас за указание на ошибку, включая исходный файл! Это определенно не правильно, и я исправил это выше. Однако, спасибо за подробный ответ. Я просто буду придерживаться инструментов, которые я думаю. – shiveagit

Смежные вопросы