web-dev-qa-db-fra.com

Créez par programme des tableaux statiques lors de la compilation en C++

On peut définir un tableau statique à la compilation de la manière suivante:

const std::size_t size = 5;    
unsigned int list[size] = { 1, 2, 3, 4, 5 };

Question 1 - Est-il possible, en utilisant différents types de techniques de métaprogrammation, d'attribuer ces valeurs "par programme" au moment de la compilation?

Question 2 - En supposant que toutes les valeurs du tableau soient identiques, est-il possible d'affecter sélectivement des valeurs au moment de la compilation de manière programmée?

par exemple:

const std::size_t size = 7;        
unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 };
  1. Les solutions utilisant C++ 0x sont les bienvenues
  2. Le tableau peut être assez grand, quelques centaines d'éléments de long
  3. Le tableau pour le moment ne comprendra que des types POD
  4. On peut aussi supposer que la taille du tableau est connue à l’avance, d’une manière statique, au moment de la compilation.
  5. Les solutions doivent être en C++(pas de script, pas de macros, pas de pp Ni de solutions basées sur un générateur de code)

MISE À JOUR: La solution de Georg Fritzsche est incroyable, elle nécessite un peu de travail pour la compilation sur les compilateurs msvc et intel, mais néanmoins une approche très intéressante du problème.

62
Hippicoder

Le plus proche que vous puissiez obtenir consiste à utiliser des fonctionnalités C++ 0x pour initialiser des tableaux de modèles locaux ou membres à partir d’une liste d’arguments de modèles variadique.
Ceci est bien sûr limité par la profondeur maximale d'instanciation du modèle et par le fait que cela fasse une différence notable dans votre cas devrait être mesuré.

Exemple:

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;
};

Utilisation pour votre cas 1..5:

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
Georg Fritzsche

Eh bien, vos exigences sont si vagues qu'il est difficile de faire quoi que ce soit à leur propos ... Le problème principal est bien entendu: d'où proviennent ces valeurs?

Quoi qu'il en soit, une construction en C++ peut être considérée comme 4 étapes:

  • Étapes préalables à la création: génération de script d'en-tête/source à partir d'autres formats
  • Prétraitement
  • Instanciations de modèles
  • Compilation proprement dite

Si vous souhaitez exclure la génération de script, il vous reste alors 2 alternatives: le prétraitement et la programmation de méta-modèles.

Je ne connais absolument aucun moyen pour la programmation par méta-modèles de faire l'affaire, car, autant que je sache, il n'est pas possible de concaténer deux tableaux au moment de la compilation. Nous nous retrouvons donc avec le sauveur du jour: Programmation préprocesseur

Je suggère d'utiliser une bibliothèque à part entière pour nous aider: Boost.Preprocessor .

D'intérêt particulier ici:

Maintenant, si seulement nous savions où choisir les valeurs, nous pourrions donner des exemples plus significatifs.

6
Matthieu M.

Pourquoi ne pas construire une structure imbriquée à l'aide de modèles et la transformer en un tableau du type approprié? L'exemple ci-dessous fonctionne pour moi, mais j'ai le sentiment de marcher ou de marcher très près d'un comportement indéfini.

#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;
  }
}

Et bien sûr, vous pouvez affirmer que le tableau n'est pas initialisé au moment de la compilation (ce qui, à mon avis, est impossible), mais que les valeurs qui y entreront sont calculées au moment de la compilation, et vous pouvez y accéder comme vous le feriez d'un tableau normal. Je pense que c'est aussi proche que possible.

4
Michael Anderson

Avez-vous vraiment besoin de le faire au moment de la compilation? Ce serait beaucoup plus facile à faire au moment de l'initialisation statique. Vous pouvez faire quelque chose comme ça.

#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';
    }
}

Vous pouvez pousser les listes en dehors des structures si vous voulez, mais je pensais que c'était un peu plus propre comme ça.

2
CB Bailey

Quelque chose comme Boost.Assignment pourrait fonctionner pour des conteneurs standard. Si vous avez vraiment besoin d'utiliser des tableaux, vous pouvez l'utiliser le long de Boost.Array .

2
danielkza

Parfois (pas toujours), ce tableau est généré à partir du tableau de types . Par exemple, si vous avez déjà une liste de classes variadic (comme template) et que vous voulez stocker la valeur uint32_t encapsulée, vous pouvez utiliser:

uint32_t tab[sizeof(A)]= {A::value...};
2
kwesolowski

la 1't question. Vous pouvez le faire comme ça.

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
Max

Il suffit d'utiliser un générateur de code. Construisez un ou plusieurs modèles pouvant générer le code souhaité, à l'aide d'un tableau ou même de fonctions mathématiques. Ensuite, incluez le fichier que vous avez généré dans votre application.

Sérieusement, un générateur de code vous faciliterait la vie.

0
Rui Curado

Il y a beaucoup de choses que vous pouvez faire avec la méta-programmation… .. Mais je voudrais d'abord demander: pourquoi voudriez-vous faire cela dans votre cas? Je pourrais comprendre si vous aviez besoin de déclarer un tel tableau à différents endroits, de manière à demander de réécrire les mêmes choses plusieurs fois. Est-ce votre cas?

En disant "définir par programme", je suggère ce qui suit:

#define MyArr(macro, sep) \
    macro(0) sep \
    macro(0) sep \
    macro(2) sep \
    macro(3) sep \
    macro(0) sep \
    macro(0) sep \
    macro(0)

Nous avons maintenant défini toutes les valeurs que vous vouliez de la manière la plus abstraite. BTW si ces valeurs signifient réellement quelque chose pour vous - vous pouvez l'ajouter à la déclaration:

#define MyArr(macro, sep) \
    macro(0, Something1) sep \
    macro(0, Something2) sep \
    // ...

Inspirons maintenant la déclaration ci-dessus.

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

Vous pouvez également gérer la situation où la plupart des entrées de votre tableau sont identiques, avec une certaine créativité perverse :)

Mais vous devriez toujours vous demander: est-ce vraiment utile? Comme vous le voyez, vous transformez le code en puzzle.

0
valdo

de boost, 

boost::mpl::range_c<int,1,5>

Générera une liste de nombres triés de 1 à 5 lors de la compilation. Pour le second, vous ne mentionnez aucun critère pour lequel des valeurs seraient modifiées. Je suis sûr que vous ne pouvez pas redéfinir une nouvelle variable une fois la liste créée.

0
Michael Dorgan