Search code examples
c++c++20constexprc++-templates

C++ initialize static constexpr from non-type parameter pack


I would like to develop a compile-time evaluation for a static constexpr struct member. However, despite my efforts, I have been unable to get the code to function correctly.

The following code will explain what I want better.

struct s1 {
    int a;
};

template <class T>
struct var {
    const char* name;
    int T::*offset;
};

constexpr int sum(auto&&... n)
{
    return (n + ...);
}

template <var... Ts>
struct A {
    int arg1;

    static constexpr auto value = sum((int)Ts.offset...);
};

template <int... Ts>
struct B {
    int arg1;

    static constexpr auto value = sum(Ts...);
};

And this is how I'm reference it:

auto Works      = B<1, 2, 3>::value;  // works == 6
auto Help       = A<{"a", &s1::a}>::value;

My final goal is to call the 3rd party function constexpr auto unknown(auto&&... args) function instead of sum, therefore I will need to expand my args as follow:

template <var... Ts>
struct C {
    int arg1;

    static constexpr auto value = unknown(Ts.name, Ts.offset ...);
};

That way it will expand to:

static constexpr auto value = unknown("a", &s1::a, "b", &s1::b ...);

Solution

  • An easy way is to take each var in the Ts pack, create a std::tuple<const char*, int T::*>, then tuple_cat these 2-tuples together, which you can apply to your unknown function:

        // static constexpr auto value = unknown(Ts.name, Ts.offset ...);
        static constexpr auto value = std::apply(
            [](auto... args) { return unknown(args...); },
            std::tuple_cat(std::tuple(Ts.name, Ts.offset)...)
        );
    

    And since you can't directly store a pointer to a string literal in a non-type template parameter, store the name directly in var and this should compile:

    template <class T, std::size_t N>
    struct var {
        const char name[N];
        int T::*offset;
    };
    

    Example: https://godbolt.org/z/j1scWvhrx