Программно создавать статические массивы во время компиляции в С++

Можно определить статический массив во время компиляции следующим образом:

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

Вопрос 1. Можно ли использовать различные методы метапрограммирования для назначения этих значений "программно" во время компиляции?

Вопрос 2. Предполагая, что все значения в массиве должны быть одним и тем же числом, возможно ли выборочно назначать значения во время компиляции программным способом?

например:

const std::size_t size = 7;        
unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 };
  • Решения, использующие С++ 0x, приветствуются
  • Массив может быть довольно большим, мало сто элементов длиной
  • Теперь массив будет состоять только из Типы POD
  • Можно также предположить размер массив будет известен заранее, в статическом компиляторе образом.
  • Решения должны быть в С++ (нет script, нет макросов, нет pp или основанные на генераторе кода решения pls)

ОБНОВЛЕНИЕ: Решение Georg Fritzsche поразительно, ему нужна небольшая работа, чтобы собрать его на компиляторах msvc и intel, но тем не менее очень интересный подход к проблеме.

Ответ 1

Ближе всего вы можете использовать функции С++ 0x для инициализации локальных или членных массивов шаблонов из списка вариантов вариационного шаблона.
Это, конечно, ограничено максимальной глубиной создания шаблона и может быть измерено существенным различием в вашем случае.

Пример:

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

Использование для вашего случая 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";
}

Ответ 2

Ну, ваши требования настолько туманны, что им сложно что-то сделать... Основная проблема, конечно же: откуда берутся эти ценности?

В любом случае сборку на С++ можно рассматривать как 4 шага:

  • Шаги предварительной сборки: script генерация заголовка/источника из других форматов
  • Препроцессирование
  • Создание экземпляров шаблонов
  • Собственная компиляция

Если вы хотите исключить генерацию script, у вас останутся две альтернативы: программирование предварительной обработки и мета-шаблонов.

Я просто не знаю, как программировать мета-шаблоны, чтобы сделать трюк здесь, потому что, насколько я знаю, невозможно объединить два массива во время компиляции. Таким образом, мы остаемся со спасителем дня: Программирование препроцессора

Я бы предложил использовать полноценную библиотеку, чтобы помочь нам: Boost.Preprocessor.

Особый интерес здесь:

Теперь, если бы мы знали, где выбирать значения, мы могли бы привести более содержательные примеры.

Ответ 3

Как создать встроенную структуру с использованием шаблонов и выставить ее как массив правильного типа. Пример ниже работает для меня, но у меня есть чувство, что я либо наступаю, либо хожу очень близко к поведению undefined.

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

И, конечно же, вы можете утверждать, что массив не инициализируется во время компиляции (что, я думаю, невозможно), но значения, которые войдут в массив, вычисляются во время компиляции, и вы можете получить к ним доступ, как если бы вы массив... Я думаю, что как можно ближе.

Ответ 4

Вам действительно нужно сделать это во время компиляции? Это было бы намного проще сделать при статическом времени инициализации. Вы могли бы сделать что-то вроде этого.

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

Вы можете нажать списки вне структур, если хотите, но я подумал, что это было немного чище, как это.

Ответ 5

Что-то вроде Boost.Assignment может работать для стандартных контейнеров. Если вам действительно нужны массивы, вы можете использовать его Boost.Array.

Ответ 6

Иногда (не всегда) такой массив генерируется из массива типов. Например, если у вас уже есть вариационный список классов (например, шаблон) и вы хотите сохранить инкапсулированное значение uint32_t, вы можете использовать:

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

Ответ 7

вопрос. Вы можете сделать это так.

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

Ответ 8

Есть много вещей, которые вы можете сделать с мета-программированием. Но сначала я хотел бы спросить: почему вы хотите сделать это в своем случае? Я мог бы понять, нужно ли объявлять такой массив в разных местах, чтобы он требовал многократного переписывания одних и тех же вещей. Это ваш случай?

Говоря "define programatically", я предлагаю следующее:

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

В настоящее время мы определили все значения, которые вы хотели самым абстрактным образом. BTW, если эти значения на самом деле означают что-то для вас - вы можете добавить его в объявление:

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

Теперь дайте вдох жизни в вышеуказанное выражение.

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

Вы также можете обрабатывать ситуацию, когда большая часть ваших записей массива одинакова, с некоторым извращенным творчеством:)

Но вы всегда должны спрашивать себя: это действительно стоит? Потому что, как видите, вы превращаете код в загадку.

Ответ 9

от boost,

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

Будет генерировать список отсортированных чисел от 1 до 5 во время компиляции. Во-вторых, вы не указываете критерии, для которых значения будут изменены. Я уверен, что вы не можете undef после этого redef новый var как только список будет создан.

Ответ 10

Просто используйте генератор кода. Создайте один или несколько шаблонов, которые могут генерировать нужный вам код, используя таблицу или даже математические функции. Затем включите файл, который вы создали в своем приложении.

Серьезно, генератор кода значительно облегчит вашу жизнь.