2

Я знаю, что есть более простые способы сделать это, но Я хотел бы инициализировать во время компиляции карту из развернутого индекса 2d массива в его общий формат.заполнить статические шаблонные массивы с метапрограммированием и вариативными шаблонами

Я хотел бы сделать это без необходимости создания объекта массива.

Ниже я определяю карту с array[][]->array[]. Теперь я задаюсь вопросом, как сделать обратное: без жесткого кодирования выбранной схемы отображения.

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

Заголовок:

template <int dim> 
class internal { 
    static unsigned int table[dim][dim]; 
    static unsigned int x_comp[dim*dim]; 
    static unsigned int y_comp[dim*dim]; 
}; 

Источник:

//1d case: 

template <> 
unsigned int 
internal<1>::table[1][1] = {{0}}; 

template <> 
unsigned int 
internal<1>::x_component[1] = {0}; 

template <> 
unsigned int 
internal<1>::y_component[1] = {0}; 

//2d case: 

template<> 
unsigned int 
internal<2>::table[2][2] = 
      {{0, 1}, 
      {2, 3} 
      }; 

// here goes some metaprogramming tricks to initialize 
// internal<2>::y_component[2*2] = ... 
// internal<2>::x_component[2*2] = ... 
// based on mapping above, i.e. table[2][2]; 
// that is: 
// x_table = { 0, 0, 1, 1 } 
// y_table = { 0, 1, 0, 1 } 
// 
// so that : 
// 
// index == table[i][j] 
// i  == x_comp[index] 
// j  == y_comp[index] 

EDIT1:

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

EDIT2: Я бы предпочел придерживаться определения произвольных массивов. Конечно, можно обойтись без, как в ответе ниже, используя целочисленное деление.

Эти массивы могут быть очень произвольно, например:

template<> 
unsigned int 
internal<2>::table[2][2] = 
      {{3, 0}, 
      {2, 1} 
      }; 
+0

Какие значения вы хотели бы быть в 'internal <2> :: x_component' и т. Д.? –

+0

@ArneMertz i изменен вопрос в конце, чтобы он дал понять – Denis

+0

Вы можете использовать функции C++ 11 как 'std :: array' и' constexpr'? Если вы хотите только иметь эти индексы, вам не понадобится весь массив. –

ответ

3

Использование массивов:

Учитывая таблицу с уникальными записями от 0 до тусклым^2-1, вы можете написать constexpr функции поиска для г и дж записи данной таблицы:

constexpr unsigned get_x_comp(unsigned index, unsigned i=0, unsigned j=0) 
{ return table[i][j] == index ? i : get_x_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); } 

constexpr unsigned get_y_comp(unsigned index, unsigned i=0, unsigned j=0) 
{ return table[i][j] == index ? j : get_y_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); } 

Они будут рекурсивно называть себя, итерации через стол и поиск index. Рекурсия закончится, когда данный индекс будет найден, и возвращается i/j этого индекса.

Объедините это с C++ 14 std::integer_sequence упоминается Джонатаном инициализировать массивы:

template<unsigned... I> 
constexpr auto make_x_comp(std::integer_sequence<unsigned, I...>) -> std::array<unsigned, sizeof...(I)> { return {get_x_comp(I)...}; } 

Использование метафункции вместо массивов:

В некоторых cicumstances, один может даже не нужны массивы. Я предполагаю, что вы хотите, чтобы table содержал последовательные индексы от 0 до dim^2-1. Если это так, table, x_comp и y_comp только простые функции compiletime со следующими атрибутами:

  • table(i,j) := i*dim + j
  • x_comp(index) := index/dim (целочисленное деление)
  • y_comp(index) := index % dim

В зависимости от того, если у вас есть Возможности C++ 11, реализация будет отличаться, но оба раза без массивов.

Примечание: следующие реализации предполагают, что числа, хранящиеся в table, являются последовательными от 0 до dim^2-1. Если это не случая, вам придется свернуть свою собственную функцию для table наших правил и использовать вышеописанный get_x_comp и get_y_comp implementatio

C++ 11:

template <unsigned dim> //use unsigned to avoid negative numbers! 
struct internal { 
    static constexpr unsigned table(unsigned i, unsigned j) { return i*dim+j; } 
    static constexpr unsigned x_comp(unsigned index) { return index/dim; } 
    static constexpr unsigned y_comp(unsigned index) { return index%dim; } 
}; 

Вы можете вызывать эти функции как обычные функции в любом месте, особенно в любом месте, где вам нужны константы compiletime.Пример: int a[internal<5>::table(2,4)];

C++ 03:

template <unsigned dim> //use unsigned to avoid negative numbers! 
struct internal { 
    template<unsigned i, unsigned j> 
    struct table{ static const unsigned value = i*dim+j; }; 
    template<unsigned index> 
    struct x_comp{ static const unsigned value = index/dim; }; 
    template<unsigned index> 
    struct y_comp{ static const unsigned value = index%dim; }; 
}; 

Используя эти метафункции немного более неуклюжим, чем в C++ 11, но работает, как обычно, с шаблонной метафункцией. Тот же пример, что и выше: int a[internal<5>::table<2,4>::value];

Примечание: На этот раз вы можете поместить (мета-) функцию в заголовке, так как они не являются неинтегральными статическими переменными-членами больше. Также вам не нужно ограничивать шаблон небольшими размерами, поскольку все будет хорошо рассчитано для размеров менее sqrt(numeric_limits<unsigned>::max()).

+0

Спасибо Арне за ответ. Извините за то, что я сразу не сказал, что мне нужны эти массивы. В противном случае это правда, без массивных массивов можно легко провести целочисленное деление. – Denis

+0

@Denis вы можете использовать подход Джонатана для populationg 'table', я добавил meas для заполнения' x_comp' функциями constexpr. Смотрите мои правки. –

+0

Ницца. Первая часть этого ответа велика, но остальные предполагают последовательные номера в 'table', а редактирование ответа дает понять, что может быть не так (что делает мой ответ неправильным.) Для обработки общего случая решение должно используйте функции 'get_x_comp' и' get_y_comp' для поиска значений в 'table', которые я не смог выполнить. –

1

Edit: Я не понял правила для заполнения x_comp и y_comp, когда я писал, теперь, когда я вижу, что часть из вопрос этот ответ не имеет особого значения, потому что я неправильно предполагал, что table содержит только целые целые числа. Ответ остался здесь в любом случае, потому что ответ Арне (гораздо лучший) относится к нему.


Я хотел бы заменить массивы std::array и использовать C++ 14 integer_sequence утилиты:

template <int dim> 
struct internal { 
    static std::array<std::array<unsigned, dim>, dim> table; 
    static std::array<unsigned, dim*dim> x_comp; 
    static std::array<unsigned, dim*dim> y_comp; 
}; 

template<unsigned Origin, unsigned... I> 
constexpr std::array<unsigned, sizeof...(I)> 
make_1d_array_impl(std::integer_sequence<unsigned, I...>) 
{ 
    return { { I + Origin ... } }; 
} 

template<int N> 
constexpr std::array<unsigned, N*N> 
make_1d_array() 
{ 
    return make_1d_array_impl<0>(std::make_integer_sequence<unsigned, N*N>{}); 
} 


template<unsigned... I> 
constexpr std::array<std::array<unsigned, sizeof...(I)>, sizeof...(I)> 
make_2d_array_impl(std::integer_sequence<unsigned, I...> seq) 
{ 
    return { { make_1d_array_impl<I*sizeof...(I)>(seq) ... } }; 
} 

template<int N> 
constexpr std::array<std::array<unsigned, N>, N> 
make_2d_array() 
{ 
    return make_2d_array_impl(std::make_integer_sequence<unsigned, N>{}); 
} 

template<int dim> 
std::array<std::array<unsigned, dim>, dim> internal<dim>::table = make_2d_array<dim>(); 

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

Вы можете найти реализацию C++ 11 integer_sequence в https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h

+0

Я не совсем понимаю, как вы использовали определение 'internal <2> :: table [2] [2]'? – Denis

+0

Я не использовал 'internal <2>', я показал, как определить и заполнить 'internal :: table' _for any N_. Я не понимаю, какие значения вы хотите использовать в массивах x и y, но значения –

+0

приведены в разделе комментариев. Это фактически карта между двумя представлениями массива 2d - пара индексов (x, y) и один (развернутый) индекс. Эта 'internal <2> :: table [2] [2]' определяет параметр пары-> index; Вопрос заключается в том, как заполнить index-> ​​pair; – Denis

1

Я извиняюсь, если я не отвечая на вопрос, непосредственно (или вообще), но я не очень понимаю, что вы» повторно прошу. Я думаю, что вы говорите, что вы хотите инициализировать во время компиляции способ иметь массив размером N x M представлен в виде массива 1D?

Я включил код, который позволяет выделять не квадратные размеры. Я построил это в «легком» C++, поэтому, если вы просто попадаете в шаблоны, это не так сложно.

Можно ли сделать что-то подобное?

template <typename T, typename std::size_t N, typename std::size_t M = 1> 
class Array { 
    T* data; 
public: 
    Array<T, N, M>() : data(new T[N * M]) { 
     T temp = 0; 
     for (int i = 0; i < N; i++) { 
      for (int j = 0; j < M; j++) { 
       data[i * M + j] = temp++; 
      } 
     } 
    } 
    /* methods and stuff 
} 

Где M это номер столбца, так что вы будете использовать это как:

int main(void) { 

    Array<float, 10, 10> myArray; 

    return 0; 
} 

Не забудьте позвонить delete в деструкторе.

+0

, который требует экземпляра объекта этого класса. Это именно то, чего я не хочу делать. Спасибо за ответ, так или иначе – Denis

+0

@Denis Не возражаете, если я отредактирую ваш вопрос, чтобы это отразить? –

+0

уверен. жаль, что не было ясно с самого начала – Denis