Search code examples
c++arraystemplatesc++17template-argument-deduction

how to deduce the size of a multidimensional array in a curly brace expression of a function call with templates


I'm currently writing a typesafe compiletime-enabled tensor class, which boils down to

template<typename T, int... Sizes>
struct tensor {
  T elements[(Sizes * ...)];
  auto ptr() {return elements;}
  auto size() {return (Sizes * ...);}

  using value_type = T;
};

I wanted to write a method that allows to create a tensor with arrays as an expression, like that:

make_tensor({1,2,3,4}) or make_tensor({{1,2},{3,4}}), giving a vector and a matrix. my make_tensor function currently looks like this:

template<template<typename, int...> class Fill, typename Type, int... Sizes>
struct array_to_index {
  using type = Fill<Type, Sizes...>;
};

template<template<typename, int...> class Fill, typename Type, int Size, int... Sizes>
struct array_to_index<Fill, Type[Size], Sizes...> {
  using type = typename array_to_index<Fill, Type, Sizes..., Size>::type;
};

template<template<typename, int...> class Fill, typename Type>
using array_to_index_t = typename array_to_index<Fill, Type>::type;

template<typename Type, int Size>
auto make_tensor(const Type (&arr)[Size]) {
  using tensor_t = array_to_index_t<tensor, Type[Size]>;
  tensor_t tensor;
  using ptr_t = const typename tensor_t::value_type *;

  for(int i = 0; i < tensor.size(); ++i)
    tensor.elements[i] = reinterpret_cast<ptr_t>(arr)[i];

  return tensor;
}

I can call this with a multidimensional array if I assign it first, but the nice syntax doesn't work:

int main() {
  //auto matrix = make_tensor({{0,1},{2,3},{4,5}}); // doesn't work
  int arr[][2] = {{0,1},{2,3},{4,5}};
  auto other = make_tensor(arr); // works
  assert(other.elements[3] == 3);
}

With what I gather from this Failed to deduce bounds from initializer for multi-dimensional arrays question, my approach won't work. Is there any other way this deduction is possible (e.g with initializer lists)?


Solution

  • You cannot deduce multi dimensional array bounds from nested {{}}.

    You could deduce it by adding some tokens.

    using namespace std;
    auto arr = array{ array{1,2}, array{3,4} };
    
    for (auto row : arr) {
        for (auto e : row) {
            std::cout << e << ",";
        }
        std::cout << "\n";
    }
    

    Live example.

    The array deduction guide tears apart the {} expression following and deduces the type of the array template.

    So:

    make_tensor(a{a{1,2},a{3,4}})
    

    is possible, or even

    tensor{ tensor{1,2}, tensor{1,2} }
    

    as make functions are a bit passé.