2013-03-21 16 views
5

Stworzyłem klasę, która wygląda jak tablica, ale zamiast przechowywać dane w samym programie, przesyła bajt z pliku (aby zmniejszyć wpływ pamięci RAM). Teraz mam to wszystko działa, ale programista musi określić klasę przy użyciu następujących:Generowanie parametrów szablonu w czasie kompilacji

#define CreateReadOnlyBlock(name, location, size, ...)   \ 
template<>              \ 
const unsigned int ReadOnlyBlock<location, size>::Data[]  \ 
    __asm__(".readonly__" #location "__" #name)    \ 
    = { __VA_ARGS__ };           \ 
ReadOnlyBlock<location, size> name; 

Przykład:

//A read only array of {0, 1, 2, 3} 
CreateReadOnlyBlock(readOnlyArray, 0, 4, 0, 1, 2, 3); 

Należy pamiętać, że jest to dla wbudowanego procesora, a dyrektywa asm przechodzi przez narzędzie w asemblerze, aby utworzyć plik tylko do odczytu.

Oto moje pytanie: jak mogę wyeliminować zmienne "lokalizacja" i "rozmiar"? Nienawidzę tego, że programista musi wpisywać je ręcznie, i wolałby, żeby generował je podczas kompilacji. Więc zamiast programatora konieczności wpisywania:

//A read only array at location 0 of {0, 1, 2, 3} 
CreateReadOnlyBlock(readOnlyArray1, 0, 4, 0, 1, 2, 3); 
//A read only array at location 4 of {4, 5, 6, 7} 
CreateReadOnlyBlock(readOnlyArray2, 4, 4, 4, 5, 6, 7); 

Mogli po prostu wpisać:

CreateReadOnlyBlock(readOnlyArray1, 0, 1, 2, 3); 
CreateReadOnlyBlock(readOnlyArray2, 4, 5, 6, 7); 

i odpowiedni stałe byłyby generowane. Zasadniczo szukam sposobu generowania i umieszczania tych stałych na podstawie poprzednich definicji w czasie kompilacji. C++ 11 jest uczciwą grą, po prostu nie jestem do końca obca (coś ze constexpr wydaje się być wiarygodne?). Również C-Preprocessor jest w porządku, jeśli nie czyni go brzydszym, niż już jest. czy to możliwe?

EDIT dla jasności:

W klasie ReadOnlyBlock nie jest to metoda:

template<const int _location, const int _size> class ReadOnlyBlock 
    { 
     ... 
     unsigned int operator[] (size_t index) 
     { 
      return LoadFromROM(index + _location); 
     } 
    } 

Jest nieodłącznym współzależność między zmiennej lokalizacji i pliku ROM, że nie mogę myśleć jak złamać. I do mają również pełną kontrolę nad łańcuchem narzędzi, ale potrzebuję sposobu na przekazanie narzędzia asemblera, jak skonstruować plik, a także wskazanie kodu C++, w którym znajdują się bloki.

Kolejny EDIT:

Plik i jego bloki mogą być dość duże, jak 1k słowy, tak dużo preprocesora magii może zwiń skoro. Również dziękuję wszystkim za pomoc do tej pory!

+0

Opcja '# location' w części "__asm__" naprawdę to zabija. Czy naprawdę tego potrzebujesz, czy byłbyś zadowolony z rozwiązania tylko reszty? –

+2

Rozmiar jest łatwy, ale lokalizacja wymaga kontekstu. Inicjowanie szablonów jest językiem funkcjonalnym, a wynik instancji może być różny w zależności od tego, co przekazujesz. Jeśli łączysz takie bloki lub tworzysz pamięć w jednym dużym szablonie, możesz to zrobić. Np. Utwórz "krotkę" tablic tylko do odczytu, każda z lokalizacją i rozmiarem, zaczynając od pewnej lokalizacji i spakowaną. – Yakk

+0

Więc może powinienem był podać przyczynę tego. Przeciążony operator [] w klasie ReadOnlyBlock wywołuje LoadFromROM (index + location); Narzędzie tworzy plik tylko do odczytu, który ma każdy blok w każdej określonej lokalizacji, a klasa wie, aby załadować z tego miejsca. Nie mogę wymyślić sposobu na wyeliminowanie tej współzależności, ale mam pełną kontrolę nad tym narzędziem, a także kod dostępu, więc zmiana go nie jest wykluczona. –

Odpowiedz

1

ja nadal nie widzę pełne rozwiązanie do generowania nazwy (to #location fragment), ale dla reszty, chyba można użyć coś jak to:

template< std::size_t Line > 
struct current_location : current_location< Line - 1 > {}; 

template<> 
struct current_location<0> : std::integral_constant< std::size_t, 0 > {}; 

#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) 

#define CreateReadOnlyBlock(name, ...)       \ 
template<>              \ 
const unsigned int ReadOnlyBlock<        \ 
    current_location<__LINE__-1>::value, NUMARGS(__VA_ARGS__) \ 
>::Data[]              \ 
    __asm__(".readonly__" #name)        \ 
    = { __VA_ARGS__ };           \ 
ReadOnlyBlock<current_location<__LINE__-1>::value,    \ 
       NUMARGS(__VA_ARGS__)> name;      \ 
template<>              \ 
struct current_location<__LINE__>        \ 
    : std::integral_constant<std::size_t,      \ 
     current_location<__LINE__-1>::value+NUMARGS(__VA_ARGS__)> \ 
{}; 
+0

To jest idealne! Dziękuję bardzo. Właściwie to nie jestem przekonany, że potrzebuję już #location, ponieważ wydaje się, że i tak dane te są blokowane w sekwencyjny blok. Trochę się obawiam, że kompilator może zmienić kolejność rzeczy, w którym potrzebowałbym identyfikatora lokalizacji. W tym scenariuszu chciałbym po prostu umieścić lokalizację jako pierwszy indeks w tablicy i uzyskać narzędzie do jej usunięcia. Nie ładne, ale hej to działa! Jeszcze raz, dziękuję! –

0

Być może chwytanie trochę, ale byłoby to możliwe przy szablonach variadic zamiast va_args? Zasadniczo, wszędzie tam, gdzie potrzebowałbyś "lokalizacji", użyj T1. Wszędzie, gdzie potrzebujesz "rozmiaru", użyj sizeof (TV) + 1. Nie masz odpowiedniego kompilatora, aby to przetestować, ale może to jest coś do rozważenia ...

+0

To naprawdę nie dane, które mnie teraz zabijają, ale dzięki za sugestię! Mogę go później przekonwertować na szablony variadic, jestem po prostu bardziej komfortowy z preprocesorem C i próbuję go uruchomić. –

0

To może pomóc. Napisałem kilka rzeczy do policzenia bloków można dodać DEF_BLOCK(size) przed deklaracją bloku.Możesz spróbować przepisać mój przykład przydzielić dane wewnątrz moich bloków

template<size_t ID> 
struct block_t 
{ 
    enum{location = 0}; 
}; 

#define LAST_BLOCK struct last_block_t{enum{id=__COUNTER__-1};}; 

#define SPEC2(ID, SIZE) template<> struct block_t<ID>{enum{location = block_t<ID-1>::location + SIZE, prev_block = ID-1, size = SIZE};} 
#define SPEC(ID, SIZE) SPEC2(ID, SIZE) 

#define DEF_BLOCK(SIZE) SPEC(__COUNTER__, SIZE) 

DEF_BLOCK(10); 
DEF_BLOCK(11); 
LAST_BLOCK; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::cout << block_t<last_block_t::id>::location << std::endl; 
    return 0; 
} 
0

Jeżeli zachodzi:

  • nie używać __COUNTER__ elswhere
  • Wszystkie tablice mają maksymalną długość 5
  • Wszystkie tablice są zdefiniowane w tym samym pliku

Następnie można to zrobić:

#include <iostream> 

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N 
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1) 

template <int... Args> 
struct arg_counter { 
    enum { count = sizeof...(Args) }; 
}; 

#define INC_COUNTER1 arg_counter<-1>::count 
#define INC_COUNTER2 arg_counter<-1, __COUNTER__>::count 
#define INC_COUNTER3 arg_counter<-1, __COUNTER__, __COUNTER__>::count 
#define INC_COUNTER4 arg_counter<-1, __COUNTER__, __COUNTER__, __COUNTER__>::count 
#define INC_COUNTER5 arg_counter<-1, __COUNTER__, __COUNTER__, __COUNTER__, __COUNTER__>::count 

#define INC_COUNTER_IMPL2(count, ...) INC_COUNTER ## count 
#define INC_COUNTER_IMPL(count, ...) INC_COUNTER_IMPL2(count, __VA_ARGS__) 
#define INC_COUNTER(...) INC_COUNTER_IMPL(VA_NARGS(__VA_ARGS__), __VA_ARGS__) 

// removed: __asm__(".readonly__" #location "__" #name) 
#define CreateReadOnlyBlockImpl(name, location, size, ...)  \ 
    template<>             \ 
    const unsigned int ReadOnlyBlock<location, size>::Data[]  \ 
    = { __VA_ARGS__ };           \ 
    ReadOnlyBlock<location, size> name; 


#define CreateReadOnlyBlock(name, ...)         \ 
    CreateReadOnlyBlockImpl(name, __COUNTER__, INC_COUNTER(__VA_ARGS__), __VA_ARGS__); 

template<int Location, int Size> struct ReadOnlyBlock 
{ 
    static const unsigned int Data[Size]; 
    int loc() const { return Location; } 
    int size() const { return Size; } 
}; 

CreateReadOnlyBlock(readOnlyArray1, 0, 1, 2, 3); 
CreateReadOnlyBlock(readOnlyArray2, 4, 5, 6, 7); 
CreateReadOnlyBlock(readOnlyArray3, 9); 
CreateReadOnlyBlock(readOnlyArray4, 1, 2, 3, 4, 5); 

int main() 
{ 
    std::cout << "@" << readOnlyArray1.loc() << ": " << readOnlyArray1.size() << '\n'; 
    std::cout << "@" << readOnlyArray2.loc() << ": " << readOnlyArray2.size() << '\n'; 
    std::cout << "@" << readOnlyArray3.loc() << ": " << readOnlyArray3.size() << '\n'; 
    std::cout << "@" << readOnlyArray4.loc() << ": " << readOnlyArray4.size() << '\n'; 
} 

On ideone this prints:

@0: 4 
@4: 4 
@8: 1 
@9: 5 
+0

Niestety, tablice są tak duże, jak 1k słów, ale bardzo podoba mi się to rozwiązanie. Powinienem to wyjaśnić, przepraszam. Zmodyfikowałem mój post. –

Powiązane problemy