2014-10-02 15 views
21

Zastanawiam się, czy w C++ 11/14 jest możliwe odczytanie plików w czasie kompilacji. Na przykład poniższy kod będzie kompilowany tylko wtedy, gdy uda się go odczytać.Czy można odczytać plik podczas kompilacji?

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

Czy uważasz, że to może być możliwe?

Wiem, że mogłem to zrobić za pomocą niestandardowego narzędzia podczas budowania aplikacji.

+6

To zależy od tego, co odmieniasz przez * obciążenie *. Jedyne znane mi rozwiązanie to '# include', a to oznacza, że ​​zawartość pliku musi być zrozumiana przez kompilator. –

+6

Najprawdopodobniej jest to zewnętrzna deklaracja zmiennej, która zostanie dopasowana do rzeczywistych danych przez linker. Połącz to z narzędziem, które osadza arbitralny zasób binarny w pliku obiektowym z wyeksportowanym symbolem do wyboru i jesteś złoty. (Mogłabym zasugerować takie narzędzie, ale nie wspomniałeś o tym, którego toolchain używasz, a narzędzia są inne dla ELF vs CV vs PE vs a.out formatów plików obiektów) Zwróć uwagę, że konwersję zasobu na kod C/C++ jako stała tablica zainicjowana za pomocą szesnastkowych literałów prawdopodobnie będzie bardzo powolna, więc przejdź bezpośrednio do pliku obiektu. –

+0

Nie mogę uwierzyć, że twoja funkcja 'load' jest' constexpr'. Tak więc pełne wyrażenie nie może być constexpr. Nie mam pojęcia, jak zewnętrzna zawartość pliku może być ciągiem constexpr.Jedynym sposobem jest użycie narzędzia, które proste ładuje plik i generuje kod c/C++. – Klaus

Odpowiedz

16

Opierając się na idei teivaz jest, zastanawiam się, czy zwykłe "stringize po rozszerzeniu" sztuczka zadziała:

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

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


Nadal, pójdzie na konwencjonalną deklarację extern const char[] rozwiązaną na zawartość przez linker. W artykule "Embedding a File in an Executable, aka Hello World, Version 5967" ma przykład:

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

Naturalnie należy zmienić --output i --binary-architecture polecenia, aby dopasować swoją platformę. Nazwa pliku z pliku obiektu kończy się na nazwie symbolu, więc możesz go użyć tak:

#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++); 
} 
+0

Ale jak wskazuje interjay, może to przerwać ze względu na przecinki w GLSL –

+0

Sprawdziłem to. shader_sourc e będzie zawierało '#include" ~/.foo.glsl "' – teivaz

+0

@teivaz: Nawet z dwuwarstwowym wywołaniem makra? –

-1
#define STR(x) #x 

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

i foo.glsl należy załączyć jego zawartość w STR ( ... )

UPD. To będzie prawidłowo obsługiwać przecinki

#define STRINGIFY(...) #__VA_ARGS__ 
#define STR(...) STRINGIFY(__VA_ARGS__) 
+4

Deklaracje preprocesorów, takie jak '# include', muszą być pierwszymi nie-białymi znakami na linii :( –

+0

Ale zobacz http://stackoverflow.com/a/5566624/103167. będzie w porządku, aby wstawić znak nowego wiersza między nawiasami i '# include', o ile format danych jest tolerancyjny dla tych dodatkowych znaków pojawiających się w ciągu znaków (GLSL jest tolerancyjny) –

+0

Hmm, twoja edycja naprawiła wywołanie preprocesora, ale zepsuła się –

0

Zrobiłem coś takiego. Sprawdź, czy da ci to, czego chcesz.

Dodaj opcję wiersza poleceń do programu, który sprawdza istnienie i ważność pliku wejściowego.
Ta opcja powinna wyjść z programu z kodem błędu, jeśli plik nie istnieje lub jest nieważny.

W pliku make, dodaj połączenie do programu (przy użyciu tej opcji wiersza poleceń), jako ostateczny krok kompilacji.

Teraz po zbudowaniu programu pojawi się błąd, jeśli odpowiednie pliki nie są dostępne lub nie są prawidłowe.

+0

Chce uniknąć programów zewnętrznych. – Deduplicator

Powiązane problemy