Чтение файла в строку во время компиляции
Я хотел бы написать что-то в файле (назовите его foo.cpp
) и включить его как строку в мою программу во время компиляции, аналогично тому, как это делает #include
.
Сейчас я использую этот препроцессор C #define
:
#define toString(src) #src
чтобы преобразовать связку кода в строку, используемую как в этом примере:
const char* str = toString(
int x;
void main(){}
);
Вы можете прочитать о макроснижении там, если хотите.
Я хотел бы переместить этот код во внешний файл, который будет "связан" во время компиляции.
Я не хочу, чтобы файл должен был быть распространен вместе с программой, что было бы, если бы я прочитал его во время выполнения.
Я попытался использовать директиву #include
, как показано ниже, но компилятор отказался:
const char* str = toString(
#include "foo.cpp"
);
g++
кажется полностью запутанным, но clang++
дал мне эту ошибку:
error: embedding a #include directive within macro arguments is not supported
Кто-нибудь знает, если/как это можно сделать?
Примечание. Я использую это, чтобы написать мои шейдеры GLSL, хотя я сомневаюсь, что эта информация может быть использована.
PS: Прежде чем вы скажете мне, что это дубликат этого вопроса, поместив мой код в гигантскую строку в свой собственный файл или используя внешний инструмент (например. xxd
), чтобы сбрасывать его шестнадцатеричное представление, для меня не являются "решениями", поскольку они не лучше (т.е. проще/чище), чем мой текущий метод.
Обновление, через несколько лет:
Я просто понял, что мне никогда не приходилось отвечать на этот вопрос, поскольку он был закрыт как дубликат.
Я нашел ответ, который я искал, когда увидел этот коммит, сам по себе комментарий к этой статье и использовали его с тех пор.
В двух словах небольшой файл сборки содержит нужные вам файлы и предоставляет их под заданным NAME
, причем три переменные NAME_begin
, NAME_end
и NAME_len
позволяют вам получить доступ к их содержимому с вашего C код.
Таким образом, у вас есть обычный файл, содержащий ничего, кроме нужного вам кода, и он автоматически считывается во время компиляции, вместо того, чтобы читать его во время выполнения или переходить через xxd
обручи.
Ответы
Ответ 1
Я не совсем уверен, чего вы пытаетесь выполнить, но утилита командной строки Linux xxd
может быть тем, что вы ищете:
xxd -i [filename]
создаст заголовочный файл стиля C, содержащий массив с содержимым вашего файла в полном двоичном кодировании и переменную с его длиной.
Пример:
xxd -i /proc/cpuinfo
создает файл с
unsigned char _proc_cpuinfo[] = {
0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x09, 0x3a, 0x20,
0x30, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x09,
...
};
unsigned int _proc_cpuinfo_len = 654390;
Вы можете включить результирующий заголовок в свой код и получить доступ к массиву и длине файла через эти переменные.
Ответ 2
Вы говорите, что вам не нравится xxd
, потому что он оставляет файл нечитаемым. Справедливо. Было бы просто написать собственную утилиту, которая кодирует данные в другом формате, так как вы специально хотите, чтобы строки вводились и выводились. Вы можете использовать конкатенацию строковых литералов, чтобы сделать это легко читаемым.
const char* str =
#include "foo.h"
;
foo.h:
" int x;" "\n"
" void main(){}" "\n"
Я кратко попытался использовать С++ 11 необработанные строковые литералы, которые позволили бы использовать файл без каких-либо переформатирования, но это не Работа. #include
считается частью строки, а не включает файл по желанию.
Ответ 3
Простейшая вещь в этом случае - написать небольшую
препроцессор, который читает ваш файл и выводит его на упаковку
каждая строка в кавычках. Я бы, наверное, сделал это на Python, но это
довольно прямо в С++. Предположим, что вы
получили inputFile
, outputFile
и variableName
из
(возможно, argv
, но вы можете получить
последние два из входного имени файла:
void
wrapFile( std::istream& inputFile,
std::ostream& outputFile,
std::string const& variableName )
{
outputFile << "extern char const " << variableName << "[] =\n";
std::string line;
while ( std::getline( inputFile, line ) ) {
outputFile << " \"" << line << "\\n\"\n";
}
std::outputFile << ";" << std::endl;
}
В зависимости от того, что содержится в файлах, которые вы включаете, вы можете
должны калечить line
, прежде чем выводить его, чтобы избежать вещей
например "
или \
.
Если вы хотите получить фантазию, вы можете добавить некоторые тесты для вставки
точка с запятой на последней завернутой линии, а не строка
его, но это не обязательно.
Это приведет к завершенной строке '\0'
. Учитывая
Вероятная длина строк, может быть предпочтительнее добавить
вторая переменная с длиной:
std::outputFile << "extern int const "
<< variableName
<< "_len = sizeof(" << variableName << ") - 1;\n";
(Не забывайте -1, так как вы не хотите считать
завершение '\0'
, которое компилятор добавит к вашей строке
literal.) Если вы включаете сгенерированный файл, где он будет
вам это не обязательно понадобится, поскольку std::begin
и
std::end
предоставит необходимую информацию (но опять же,
не забудьте использовать std::end( variableName ) - 1
, чтобы игнорировать
'\n'
).
Если вы используете make
, довольно легко сделать свой сгенерированный
файл зависит от обертываемого файла и исполняемого файла
который делает обертывание (которое, в свою очередь, зависит от источника
выше и т.д.). С помощью Visual Studios вам необходимо создать
отдельный проект для кода упаковки, если вы напишете его на С++
(одна из причин, по которой я буду использовать Python для этого), и вы, скорее всего,
имеют некоторые проблемы с управлением зависимостями; Визуальные студии
на самом деле не предназначена для профессиональной работы (где крупные блоки
кода регулярно генерируются с использованием таких методов).