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 };
  1. rozwiązania wykorzystujące C++0x to Witamy
  2. tablica może być dość duża, niewiele hundred elements long
  3. tablica na razie będzie składać się tylko z Typy POD
  4. można też przyjąć wielkość tablica będzie znana wcześniej, w czasie kompilacji statycznej sposób.
  5. 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.

Author: Hippicoder, 2010-06-05

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";
}
 76
Author: Georg Fritzsche,
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.

 6
Author: Matthieu M.,
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.

 4
Author: Michael Anderson,
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.

 2
Author: CB Bailey,
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 .

 2
Author: danielkza,
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...};
 2
Author: kwesolowski,
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;
}
 1
Author: Max,
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.
 0
Author: valdo,
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.

 0
Author: Michael Dorgan,
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.

 0
Author: Rui Curado,
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