Программно создавать статические массивы во время компиляции в C ++ - PullRequest
63 голосов
/ 05 июня 2010

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

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 ++ (без скрипта, без макросов, без пп или решения на основе генератора кода, пожалуйста)

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

Ответы [ 10 ]

77 голосов
/ 05 июня 2010

Самое близкое, что вы можете получить, - это использовать функции 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";
}
7 голосов
/ 05 июня 2010

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

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

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

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

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

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

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

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

5 голосов
/ 05 июня 2010

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

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

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

2 голосов
/ 21 августа 2011

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

uint32_t tab[sizeof(A)]= {A::value...};
2 голосов
/ 05 июня 2010

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

2 голосов
/ 05 июня 2010

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

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

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

1 голос
/ 05 июня 2010

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;
}
0 голосов
/ 05 июня 2010

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

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

0 голосов
/ 05 июня 2010

от наддува,

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

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

0 голосов
/ 05 июня 2010

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

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

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

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

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

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