Как найти все позиции нескольких подстрок в строке в C++

Я хочу использовать директиву #include в файлах GLSL, чтобы мне не приходилось переписывать один и тот же код для каждого создаваемого мной шейдера GLSL. Я попытался создать для этой цели свой собственный небольшой парсер, но не смог найти хороший способ найти все операторы #include в файле GLSL. Я хочу извлечь пути включенных файлов, прочитать их и записать их содержимое в реальный шейдер (не исходный файл GLSL, который использует директиву #include, а содержимое, считанное в std::string).

Я попытался найти шейдерные включения, используя следующий код:

std::string include = "#include";
std::string shaderSource = ReadFile("shader.glsl");
std::uint32_t offset = 0, position = 0;
while ((position = shaderSource.find(include, offset)) != std::string::npos)
{
    offset += include.size();
    // parse the shader
}

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

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

Dúthomhas 18.08.2024 21:51

@Dúthomhas Я не пишу в настоящий файл GLSL, так что, думаю, все будет в порядке. Хотя я не думаю, что понял вашу точку зрения.

Newbie programmer 18.08.2024 21:56

@Newbieprogrammer, какой у тебя вопрос? Похоже, вы уже знаете, как считать файл в строку и затем выполнить в нем поиск.

Remy Lebeau 18.08.2024 22:13

Почему нельзя просто использовать #include?

Eljay 18.08.2024 22:14

@Eljay У OpenGL нет файловой системы для шейдеров, так что нет, я не могу.

Newbie programmer 18.08.2024 22:26

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

Ted Lyngmo 18.08.2024 22:32

Я не совсем адекватно объяснил. Почему у вас не может быть исходных файлов с директивами #include, которые выполняют предварительную обработку (с использованием препроцессора C++ или любого другого препроцессора, который вы предпочитаете (например, раньше я использовал m4 и Python)) в файлы GLSL в качестве выходных данных.

Eljay 18.08.2024 22:33

@Eljay О, я понимаю, потому что у меня есть класс Shader, который принимает файловую систему::path, а не исходный код.

Newbie programmer 18.08.2024 22:44

@Ted Lyngmo Если подумать, совершенно очевидно: если длина пути включения больше длины «#include», то цикл while не прервется. Надеюсь, я ясно выразился.

Newbie programmer 18.08.2024 22:48

@Newbieprogrammer Извините, но это не прояснило ситуацию. Пожалуйста, обновите свой вопрос. Включите пример файла, который вы читаете shaderSource, или просто назначьте shaderSource содержимым такого файла. См. минимально воспроизводимый пример .

Ted Lyngmo 18.08.2024 22:51

Нет проблем, я обновлю свой вопрос.

Newbie programmer 18.08.2024 22:52

Скорее всего, вы хотите offset = position + include.size(). В противном случае вы будете снова и снова находить одну и ту же подстроку "#include". Допустим, подстрока находится в позиции 100. Сначала вы найдете ее при поиске, начиная со смещения 0; затем снова, начиная со смещения 8; затем снова, начиная со смещения 16; ... Тогда как, как только вы нашли его в позиции 100, вы хотите начать поиск со смещения 108 (или 100 + длина всей директивы).

Igor Tandetnik 18.08.2024 23:13

@Игорь Тандетник Спасибо! Попробую реализовать и посмотреть, получится или нет. Я ценю вашу помощь.

Newbie programmer 18.08.2024 23:15

Возможно актуально: Как использовать #include в поддержке glsl ARB_shading_language_include

Wyck 19.08.2024 00:02

@Wyck прав - вам нужно просто передать компилятору GLSL данные заголовочного файла, включить расширение include и позволить ему фактически заменить содержимое там, где это необходимо.

Jerry Coffin 19.08.2024 00:41
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
15
82
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

пример.glsl

#include "common.glsl"

void main()
{
    FragColor = vertexColor;
}

common.glsl

out vec4 FragColor;
in vec4 vertexColor;

Командная строка Visual Studio

cl /P common.glsl /Fipreprocessed.glsl /EP

preprocessed.glsl (сгенерирован)

out vec4 FragColor;
in vec4 vertexColor;


void main()
{
    FragColor = vertexColor;
}

Эквивалентная команда в gcc

gcc -E -P common.glsl -o preprocessed.glsl

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

Newbie programmer 19.08.2024 00:22
Ответ принят как подходящий

Примечание. Эта версия выполнена с использованием кода C++, как вы просили в своем комментарии к другому моему ответу.

Идея состоит в том, чтобы найти текст формата #include "filename" и рекурсивно заменить этот текст содержимым включенного файла.

Я позаботился об указании дополнительных каталогов включения. Но я также использовал несколько сокращений, жертвуя надежностью, особенно с регулярным выражением для сопоставления "" или <>.

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

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <regex>
#include <vector>
#include <filesystem>

// Read a file and return its contents as a string
std::string readFile(const std::string& fileName) {
    std::ifstream file(fileName);
    if (!file.is_open()) {
        throw std::runtime_error("Could not open file: " + fileName);
    }

    std::stringstream buffer;
    buffer << file.rdbuf();
    return buffer.str();
}

// Find the file in the current directory or in additional include directories
std::string findIncludeFile(const std::string& fileName, const std::vector<std::string>& includeDirs) {
    if (std::filesystem::exists(fileName)) {
        return fileName; // File found in the current directory
    }

    for (const auto& dir : includeDirs) {
        std::filesystem::path filePath = std::filesystem::path(dir) / fileName;
        if (std::filesystem::exists(filePath)) {
            return filePath.string(); // File found in one of the include directories
        }
    }

    throw std::runtime_error("File not found: " + fileName);
}

// Process `#include` directives
std::string processIncludes(const std::string& input, const std::vector<std::string>& includeDirs) {
    std::regex includeRegex(R"(#include\s*["<](.*?)[">])");
    std::smatch match;
    std::string output = input;
    std::string::const_iterator searchStart(output.cbegin());

    while (std::regex_search(searchStart, output.cend(), match, includeRegex)) {
        std::string includeFile = match[1].str();
        std::string fileContent;
        try {
            std::string filePath = findIncludeFile(includeFile, includeDirs);
            fileContent = readFile(filePath);
        }
        catch (const std::exception& e) {
            std::cerr << "Error: " << e.what() << std::endl;
            fileContent = "";
        }

        // Calculate the position in the full string for the replacement
        auto matchPos = match.position(0) + (searchStart - output.cbegin());
        output.replace(matchPos, match.length(0), fileContent);

        // Adjust the searchStart to continue at the inserted content (to allow nested includes)
        searchStart = output.cbegin() + matchPos;
    }

    return output;
}

int main() {
    // Example input string containing `#include` directives
    std::string input = R"(#include "example.glsl")";

    // List of additional include directories
    std::vector<std::string> includeDirs = { "./include", "./shaders" };

    try {
        std::string output = processIncludes(input, includeDirs);
        std::cout << "Processed Output:\n" << output << std::endl;
    }
    catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}

пример.glsl

#include "common.glsl"

void main()
{
    FragColor = vertexColor;
}

common.glsl

out vec4 FragColor;
in vec4 vertexColor;

Выход:

out vec4 FragColor;
in vec4 vertexColor;

void main()
{
    FragColor = vertexColor;
}

Спасибо, я ценю вашу помощь.

Newbie programmer 19.08.2024 10:34

Другие вопросы по теме