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 };
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.
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";
}
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:
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.
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.
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.
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 .
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...};
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;
}
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.
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.
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.