Programowo tworzyć tablice statyczne w czasie kompilacji w C++
Można zdefiniować statyczną tablicę w czasie kompilacji w następujący sposób:
const std::size_t size = 5;
unsigned int list[size] = { 1, 2, 3, 4, 5 };
Pytanie 1 - czy jest możliwe, używając różnych technik metaprogramowania, aby przypisać te wartości "programowo" w czasie kompilacji?
Pytanie 2 - zakładając, że wszystkie wartości w tablicy mają być tymi samymi barrami, czy możliwe jest selektywne przypisywanie wartości w czasie kompilacji w sposób programowy?
Eg:
const std::size_t size = 7;
unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 };
- rozwiązania wykorzystujące C++0x to Witamy
- tablica może być dość duża, niewiele hundred elements long
- tablica na razie będzie składać się tylko z Typy POD
- można też przyjąć wielkość tablica będzie znana wcześniej, w czasie kompilacji statycznej sposób.
- roztwory muszą być w C++ (bez skryptu, bez makr, bez pp lub rozwiązania oparte na generatorze kodu pls)
UPDATE: rozwiązanie Georga Fritzschego jest niesamowite, wymaga trochę pracy, aby je zdobyć kompilowanie na kompilatorach msvc i Intela, ale mimo to bardzo ciekawe podejście do problemu.
10 answers
Najbliżej jest użycie funkcji C++0x do inicjalizacji lokalnych lub członkowskich tablic szablonów z listy zmiennych argumentów szablonów.
Jest to oczywiście ograniczone maksymalną głębokością instancjacji szablonu i wymagałoby to zmierzenia znaczącej różnicy w Twoim przypadku.
Przykład:
template<unsigned... args> struct ArrayHolder {
static const unsigned data[sizeof...(args)];
};
template<unsigned... args>
const unsigned ArrayHolder<args...>::data[sizeof...(args)] = { args... };
template<size_t N, template<size_t> class F, unsigned... args>
struct generate_array_impl {
typedef typename generate_array_impl<N-1, F, F<N>::value, args...>::result result;
};
template<template<size_t> class F, unsigned... args>
struct generate_array_impl<0, F, args...> {
typedef ArrayHolder<F<0>::value, args...> result;
};
template<size_t N, template<size_t> class F>
struct generate_array {
typedef typename generate_array_impl<N-1, F>::result result;
};
Użycie dla Twojego 1..5
Przypadku:
template<size_t index> struct MetaFunc {
enum { value = index + 1 };
};
void test() {
const size_t count = 5;
typedef generate_array<count, MetaFunc>::result A;
for (size_t i=0; i<count; ++i)
std::cout << A::data[i] << "\n";
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-05 18:54:12
Cóż twoje wymagania są tak niejasne, że trudno cokolwiek z nimi zrobić... Głównym problemem jest oczywiście: skąd biorą się te wartości ?
W każdym razie kompilację w C++ można traktować jako 4 kroki:
- pre-build kroki: generowanie skryptu nagłówka / źródła z innych formatów
- przetwarzanie wstępne
- instancje szablonu
- kompilacja właściwa
Jeśli chcesz wykluczyć generowanie skryptu, pozostajemy z 2 alternatywami: Preprocessing i programowanie Meta-szablonów.
Po prostu nie znam sposobu na to, aby programowanie meta-szablonów wykonało tę sztuczkę, ponieważ z tego, co wiem, nie jest możliwe połączenie dwóch tablic w czasie kompilacji. W ten sposób zostajemy z Zbawicielem dnia: Programowanie Preprocesorowe
Sugerowałbym skorzystanie z pełnowartościowej biblioteki, aby nam pomóc: Boost.Preprocesor .
Of particular interest tutaj:
Teraz, gdybyśmy tylko wiedzieli, skąd wybrać wartości, moglibyśmy podać bardziej znaczące przykłady.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-05 10:12:28
Może zbudujemy zagnieżdżoną strukturę używając szablonów i stworzymy tablicę odpowiedniego typu. Poniższy przykład działa dla mnie, ale mam wrażenie, że albo stąpam, albo chodzę bardzo blisko nieokreślonego zachowania.
#include <iostream>
template<int N>
struct NestedStruct
{
NestedStruct<N-1> contained;
int i;
NestedStruct<N>() : i(N) {}
};
template<>
struct NestedStruct<0>
{
int i;
NestedStruct<0>() : i(0) {}
};
int main()
{
NestedStruct<10> f;
int *array = reinterpret_cast<int*>(&f);
for(unsigned int i=0;i<10;++i)
{
std::cout<<array[i]<<std::endl;
}
}
I oczywiście można argumentować, że tablica nie jest inicjalizowana w czasie kompilacji (co moim zdaniem jest niemożliwe), ale wartości, które wejdą do tablicy są obliczane w czasie kompilacji i możesz uzyskać do nich dostęp tak, jak do zwykłej tablicy... Myślę, że tak blisko jak tylko możesz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-05 04:23:58
Czy naprawdę musisz to robić w czasie kompilatora? Byłoby to znacznie łatwiejsze do zrobienia w czasie inicjalizacji statycznej. Mógłbyś zrobić coś takiego.
#include <cstddef>
#include <algorithm>
template<std::size_t n>
struct Sequence
{
int list[n];
Sequence()
{
for (std::size_t m = 0; m != n; ++m)
{
list[m] = m + 1;
}
}
};
const Sequence<5> seq1;
struct MostlyZero
{
int list[5];
MostlyZero()
{
std::fill_n(list, 5, 0); // Not actually necessary if our only
// are static as static objects are
// always zero-initialized before any
// other initialization
list[2] = 2;
list[3] = 3;
}
};
const MostlyZero mz1;
#include <iostream>
#include <ostream>
int main()
{
for (std::size_t n = 0; n != 5; ++n)
{
std::cout << seq1.list[n] << ", " << mz1.list[n] << '\n';
}
}
Możesz wypchnąć listy poza struktury, jeśli chcesz, ale myślałem, że to trochę czystsze, jak to.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 23:11:10
Coś w rodzaju Boost.Assignment może działać dla standardowych kontenerów. Jeśli naprawdę potrzebujesz użyć tablic, możesz użyć go wzdłuż Boost.Array .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 23:21:57
Czasami (nie zawsze) taka tablica jest generowana z tablicy typów. Na przykład, jeśli masz już zmienną listę klas (jak szablon) i chcesz zapisać zamkniętą wartość uint32_t, możesz użyć:
uint32_t tab[sizeof(A)]= {A::value...};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-20 21:03:16
Pytanie 1. Możesz to zrobić w ten sposób.
template <int num, int cur>
struct ConsequentListInternal {
enum {value = cur};
ConsequentListInternal<num-1,cur+1> next_elem;
};
template <int cur>
struct ConsequentListInternal<0, cur> {
enum {value = cur};
};
template <int v>
struct ConsequentList {
ConsequentListInternal<v, 0> list;
};
int main() {
ConsequentList<15> list;
return 0;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 23:40:21
Jest wiele rzeczy, które możesz zrobić z meta-programowaniem. Ale najpierw chciałbym zapytać: dlaczego chcesz to zrobić w Twoim przypadku? Zrozumiałbym, gdybyś musiał zadeklarować taką tablicę w różnych miejscach, aby wymagała wielokrotnego przepisywania tych samych rzeczy. To twoja sprawa?
Mówiąc "Definiuj programowo" sugeruję co następuje:
#define MyArr(macro, sep) \
macro(0) sep \
macro(0) sep \
macro(2) sep \
macro(3) sep \
macro(0) sep \
macro(0) sep \
macro(0)
Do tej pory zdefiniowaliśmy wszystkie wartości, które chciałeś w najbardziej abstrakcyjny sposób. BTW jeśli te wartości faktycznie oznaczają coś dla ciebie-możesz to dodać do deklaracji:
#define MyArr(macro, sep) \
macro(0, Something1) sep \
macro(0, Something2) sep \
// ...
Teraz tchnijmy życie w powyższą deklarację.
#define NOP
#define COMMA ,
#define Macro_Count(num, descr) 1
#define Macro_Value(num, descr) num
const std::size_t size = MyArr(Macro_Count, +);
unsigned int list[size] = { MyArr(Macro_Value, COMMA) };
Możesz również poradzić sobie z sytuacją, w której większość twoich wpisów jest taka sama, z jakąś zboczoną kreatywnością:)
Ale zawsze powinieneś zadać sobie pytanie: czy to naprawdę jest tego warte? Ponieważ, jak widzisz, zamieniasz kod w puzzle.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 23:15:00
Z boost,
boost::mpl::range_c<int,1,5>
Wygeneruje listę posortowanych liczb od 1 do 5 w czasie kompilacji. Po drugie, nie wymieniasz żadnych kryteriów, dla których wartości zostałyby zmienione. Jestem prawie pewien, że nie można undef następnie redef nowego var po utworzeniu listy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-05 06:12:23
Wystarczy użyć generatora kodów. Zbuduj jeden lub więcej szablonów, które mogą wygenerować żądany kod, używając tabeli lub nawet funkcji matematycznych. Następnie dołącz plik wygenerowany w aplikacji.
Poważnie, generator kodu znacznie ułatwi Ci życie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-07 10:16:30