Программно создайте статические массивы во время компиляции в 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. Массив на данный момент будет только состоять из типов POD
  4. Можно также предположить, что размер массива будет известен заранее в статическое время компиляции совместимый способ.
  5. Решения должны быть в C++ (никакой сценарий, никакие макросы, никакие стр или основанные на генераторе кода решения)

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

63
задан Hippicoder 6 June 2010 в 08:05
поделиться

9 ответов

Самое близкое, что вы можете получить, - это использование функций C ++ 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";
}
78
ответ дан 24 November 2019 в 16:25
поделиться

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

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

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

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

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

0
ответ дан 24 November 2019 в 16:25
поделиться

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

В любом случае, сборку на C++ можно представить как 4 этапа:

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

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

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

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

Особый интерес здесь представляют:

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

6
ответ дан 24 November 2019 в 16:25
поделиться

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

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

0
ответ дан 24 November 2019 в 16:25
поделиться

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

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

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

2
ответ дан 24 November 2019 в 16:25
поделиться

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

2
ответ дан 24 November 2019 в 16:25
поделиться

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

#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
ответ дан 24 November 2019 в 16:25
поделиться

the 1't question. Вы можете сделать это так.

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
ответ дан 24 November 2019 в 16:25
поделиться

из boost,

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

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

0
ответ дан 24 November 2019 в 16:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: