Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write constexpr function which operate on forwarded tuple of reference?

Tags:

c++

c++17

I have written a constexpr function which calculate the sum of the sizes of the elements of a tuple.

When called directly, the function call compiles with both tuple of values, and tuple of references.

When called through a templated function, it still compiles with tuple of values, but fail with tuple of references.

I can work around my problem using tuple of pointers instead of tuple of reference, but the API of the thing I write (a templated set of functions to ease writing SPI and I²C driver for microcontrollers) will be less clean.

Thanks for any help.

Ps: I am using gcc8.2 using c++17 standard.

Alexandre

#include <tuple>

template <typename> struct is_tuple_t: std::false_type {};
template <typename ...T> struct is_tuple_t<std::tuple<T...>> : std::true_type {};

template<typename Type>
constexpr bool is_tuple(const Type&) {
    if constexpr (is_tuple_t<Type>::value) 
        return true;
    else
        return false;
}

template<class F, class...Ts, std::size_t...Is>
constexpr void for_each_in_tuple(const std::tuple<Ts...> & tupl, F func,
             std::index_sequence<Is...>){
    using expander = int[];
    (void)expander { 0, ((void)func(std::get<Is>(tupl)), 0)... };
}

template<class F, class...Ts>
constexpr void for_each_in_tuple(const std::tuple<Ts...> & tupl, F func){
    for_each_in_tuple(tupl, func, std::make_index_sequence<sizeof...(Ts)>());
}

template <typename T>
constexpr size_t size_of_tuple(const T &tup) {
    static_assert(is_tuple(tup) == true, "error size_of_tuple argument must be a tuple");
    size_t s=0;
    for_each_in_tuple(tup, [&s](auto &&x) {
        s += sizeof(x);
    });
    return s;
}

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
    constexpr size_t st = size_of_tuple(tup);
    return st;
}

int main()
{
    uint16_t var;

    constexpr size_t s1 = size_of_tuple(std::make_tuple(1)) ;          // OK
    constexpr size_t s2 = size_of_tuple(std::forward_as_tuple(var)) ;  // OK

    constexpr size_t f1 = foo(std::make_tuple(1)) ;          // OK
    constexpr size_t f2 = foo(std::forward_as_tuple(var)) ;  // FAIL
}
like image 924
user3897188 Avatar asked Oct 27 '22 20:10

user3897188


1 Answers

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  constexpr size_t st = size_of_tuple(tup);
  return st;
}

In this function, tup is not a constant expression, so it can't be used in the initializer of a constexpr variable.

[expr.const]¶2

An expression e is a core constant expression unless the evaluation of e [...] would evaluate one of the following expressions:

  • [...]
  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • it is initialized with a constant expression or
    • its lifetime began within the evaluation of e

Either one of these should work instead:

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  size_t st = size_of_tuple(tup);
  return st;
}

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  return size_of_tuple(tup);
}

Note that Clang still rejects your code, for other violations of the same rule.


Ultimately, your size_of_tuple and is_tuple are both flawed, as they can't be used in constant expressions if their argument is a reference. If you want to use this function-like syntax, you need something like type_c from Boost.Hana:

template <typename T>
class type {};
template <typename T>
constexpr type<T> type_c{};

template <typename T>
constexpr bool is_tuple(type<T>) {
    return is_tuple_t<T>::value;
}

template <typename T>
constexpr size_t size_of_tuple(type<T> tup) {
    static_assert(is_tuple(tup), "size_of_tuple argument must be a tuple");
    //...
}

template<typename Tb>
constexpr size_t foo(const Tb&& tup)
{
  size_t st = size_of_tuple(type_c<std::remove_cvref_t<decltype(tup)>>);
  return st;
}

uint16_t var = 42;
constexpr size_t f2 = foo(std::forward_as_tuple(var));
like image 150
Oktalist Avatar answered Nov 15 '22 06:11

Oktalist