Можно ли прочитать файл во время компиляции?

Мне интересно, возможно ли в C ++ 11/14 действительно читать файлы во время компиляции. Например, следующий код будет компилироваться только в том случае, если он сможет успешно прочитать файл.

constexpr std::string shader_source = load("~/foo.glsl");

Как вы думаете, такое возможно?

Я знаю, что могу сделать это с помощью специального инструмента при создании приложения.


person Maik Klein    schedule 02.10.2014    source источник
comment
Это зависит от того, что вы отучаете от нагрузки. Единственное известное мне решение - #include, и это означает, что компилятор должен понимать содержимое файла.   -  person Didier Trosset    schedule 02.10.2014
comment
Что вам, вероятно, нужно, так это объявление внешней переменной, которая будет сопоставлена ​​с фактическими данными компоновщиком. Объедините это с инструментом, который встраивает произвольный двоичный ресурс в объектный файл с экспортированным символом по вашему выбору, и вы в золотом цвете. (Я мог бы предложить такой инструмент, но вы не упомянули, какую цепочку инструментов вы используете, и инструменты различаются для форматов файлов ELF, CV, PE и a.out). Обратите внимание, что преобразование ресурса в код C / C ++ как постоянный массив, инициализированный шестнадцатеричными литералами, скорее всего, будет очень медленным, поэтому сразу переходите к объектному файлу.   -  person Ben Voigt    schedule 02.10.2014
comment
Я не могу поверить, что ваша load функция - это constexpr. Таким образом, полное выражение не может быть constexpr. Я понятия не имею, как содержимое внешнего файла может быть строкой constexpr. Единственный способ - использовать инструмент, который просто загружает файл и генерирует код c / c ++.   -  person Klaus    schedule 02.10.2014
comment
Учитывая, что C ++ не предоставляет способа сделать это, вы получаете ответы, предполагая, что конкретный компилятор / компоновщик - вы можете указать свои фактические потребности в ОС / переносимости.   -  person Tony Delroy    schedule 02.10.2014
comment
да, исходный файл c ++ можно прочитать   -  person weima    schedule 03.10.2014


Ответы (3)


Основываясь на идее Тейваза, я задаюсь вопросом, сработает ли обычный трюк с "растягиванием после раскрытия":

#define STRINGIZE(...) #__VA_ARGS__
#define EXPAND_AND_STRINGIZE(...) STRINGIZE(__VA_ARGS__)

constexpr std::string shader_source = EXPAND_AND_STRINGIZE(
#include "~/.foo.glsl"
);


Тем не менее, я бы выбрал обычное объявление extern const char[], разрешенное компоновщиком для содержимого. Статья «Встраивание файла в исполняемый файл, также известный как Hello World, версия 5967 " содержит пример:

# objcopy --input binary \
          --output elf32-i386 \
          --binary-architecture i386 data.txt data.o

Естественно, вам следует изменить команды --output и --binary-architecture в соответствии с вашей платформой. Имя файла из объектного файла заканчивается именем символа, поэтому вы можете использовать его так:

#include <stdio.h>

/* here "data" comes from the filename data.o */
extern "C" char _binary_data_txt_start;
extern "C" char _binary_data_txt_end;

main()
{
    char*  p = &_binary_data_txt_start;

    while ( p != &_binary_data_txt_end ) putchar(*p++);
}
person Ben Voigt    schedule 02.10.2014
comment
Но, как указывает Interjay, это может сломаться из-за запятых в GLSL. - person Ben Voigt; 02.10.2014
comment
Я это проверил. shader_source будет содержать #include "~/.foo.glsl" - person Teivaz; 02.10.2014
comment
@teivaz: Даже при двухуровневом вызове макроса? - person Ben Voigt; 02.10.2014
comment
Выход - заключить содержимое foo.glsl в EXPAND_AND_STRINGIZE( ... ) - person Teivaz; 02.10.2014
comment
@teivaz: Это вообще не решение, потому что это уже недействительный код GLSL. OTOH для GLSL, в частности, вы можете поиграть #ifndef трюками, поскольку я считаю, что он поддерживает препроцессор C. - person Ben Voigt; 02.10.2014
comment
Вычеркнутый раздел не может работать из-за C ++ 11 16.3 / 11: если в списке аргументов есть последовательности токенов предварительной обработки, которые в противном случае действовали бы как директивы предварительной обработки, поведение не определено. - person Angew is no longer proud of SO; 02.10.2014
comment
@Angew: Кажется, это решает вопрос довольно убедительно. - person Ben Voigt; 02.10.2014

#define STR(x) #x

const char* a =
{ 
#include "foo.glsl" 
};

и foo.glsl должен заключать свое содержимое в STR (...)

UPD. Это будет правильно обрабатывать запятые

#define STRINGIFY(...) #__VA_ARGS__
#define STR(...) STRINGIFY(__VA_ARGS__)
person Teivaz    schedule 02.10.2014
comment
Объявления препроцессора, такие как #include, должны быть первым непробельным символом в строке :( - person Ben Voigt; 02.10.2014
comment
Но см. stackoverflow.com/a/5566624/103167. Можно поместить новую строку между круглыми скобками и #include, если формат данных допускает появление этих дополнительных символов новой строки в строке (GLSL допускает). - person Ben Voigt; 02.10.2014
comment
Хм, ваша правка исправила вызов препроцессора, но нарушила построение строк. - person Ben Voigt; 02.10.2014
comment
@NeilKirk: Вероятно, потому что он должен быть голым исходным кодом GLSL для совместимости с отладчиками шейдеров. - person Ben Voigt; 02.10.2014
comment
Такое использование макроса STR не сработает, если файл содержит запятые. Я думаю, что это также изменило бы пробелы в файле. - person interjay; 02.10.2014
comment
блин, это умно - person CoffeeTableEspresso; 06.08.2019
comment
Работает ли это для файлов произвольных типов, рассматриваемых как строка? Я столкнулся с некоторыми проблемами при попытке загрузить файл сертификата / ключа SSL таким образом - компилятору не нравится содержимое файла. Я надеялся, что это будет просто чистая строка, поскольку это в основном файлы .txt. - person netpoetica; 04.03.2021

Я сделал что-то подобное. Посмотрите, даст ли это вам то, что вы хотите.

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

В вашем файле make добавьте вызов программы (используя этот параметр командной строки) в качестве последнего шага сборки.

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

person Louis Newstrom    schedule 02.10.2014
comment
Он хочет избежать внешних программ. - person Deduplicator; 02.10.2014