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


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

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 };
  1. решения с использованием C++0x приветствуются
  2. массив может быть очень большой, несколько сто элементов длиной
  3. массив будет состоять только из Стручковые типы
  4. можно также принять размер массив будет известен заранее, в статическом времени компиляции совместимый манера.
  5. решения должны быть в C++ (без скрипта, без макросов, без pp или решения на основе генератора кода пожалуйста)

обновление: решение Георга Фрицше удивительно, нужно немного поработать, чтобы получить его компиляцию на компиляторах msvc и intel, но тем не менее очень интересный подход к проблеме.

10 60

10 ответов:

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

пример:

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 case:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1 не вопрос. Ты можешь сделать это вот так.

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

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

говоря "определить программно" я предлагаю следующее:

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

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

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

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

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

от boost,

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

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

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

серьезно, генератор кода сделает вашу жизнь намного проще.