Transform constexpr expressions into template non-type parameter

1.2k Views Asked by At

In the following example I want to transform some constexpr values into template non-type parameters. Here I have some constexpr values a1 and a2. I can use there (also constexpr) data-member d as non-type template parameters for the template B.

But if I try to take place this sort of transformation in some function transform this clearly doen't work anymore, since the parameter tuple isn't considered constexpr.

#include <cstdint>
#include <tuple>

template<auto N>
struct A {
    inline static constexpr auto size = N;
    const uint8_t d[N] {};
};
template<auto N, auto... II>
struct B {};

constexpr auto a1 = A<2>{1, 2};
constexpr auto a2 = A<3>{2, 3, 4};
constexpr auto t1 = std::tuple(a1, a2);

constexpr auto b1 = B<a1.size, a1.d[0], a1.d[1]>{};
constexpr auto b2 = B<a2.size, a2.d[0], a2.d[1], a2.d[2]>{};

template<typename T>
constexpr auto transform(const T& tuple) {
    constexpr auto x1 = std::get<0>(tuple); // NOK
    constexpr auto b1 = B<x1.size, x1.d[0], x1.d[1]>{}; // NOK 
    return std::tuple(b1);
}

constexpr auto t2 = transform(t1);

int main() {
}

The question is then: how can I make this sort of transformation. My idea is to write a meta-function with tuple t1 as parameter. But I can't get it right ...

1

There are 1 best solutions below

5
On BEST ANSWER

In C++17 you can pass a constexpr lambda returning your argument:

constexpr auto t2 = transform([&] { return t1; });

Inside the function you can "call" the lambda and assign to a constexpr:

template<class Tuple_>
constexpr auto transform(Tuple_ tuple_) {
  constexpr auto tuple = tuple_();
  // ...

Some research (thanks @wimalopaan) shows that this idiom has been described

http://coliru.stacked-crooked.com/a/073103acf18f80b6

#include <cstdint>
#include <iostream>
#include <tuple>

// not sure how robust this macro is...
#define CONSTEXPR_ARG(arg) [&] { return arg; }

template<auto N>
struct A {
    inline static constexpr auto size = N;
    const uint8_t d[N] {};
};
template<auto N, auto... II>
struct B {};

constexpr auto a1 = A<2>{1, 2};
constexpr auto a2 = A<3>{2, 3, 4};
constexpr auto t1 = std::tuple(a1, a2);

constexpr auto b1 = B<a1.size, a1.d[0], a1.d[1]>{};
constexpr auto b2 = B<a2.size, a2.d[0], a2.d[1], a2.d[2]>{};

template<class Tuple_>
constexpr auto transform(Tuple_ tuple_) {
  constexpr auto tuple = tuple_();
  constexpr auto x1 = std::get<0>(tuple); // NOK
  constexpr auto b1 = B<x1.size, x1.d[0], x1.d[1]>{}; // NOK
  return std::tuple(b1);
}

constexpr auto t2 = transform(CONSTEXPR_ARG(t1));

template<class T>
void inspect(const T& t) {
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

int main() {
  inspect(t2);
}

Output:

void inspect(const T&) [with T = std::tuple<B<2, 1, 2> >]