Reputation: 1595
I am writing a library where object parameters that are inherently static are built into the type using non-type template parameters. Doing this offers massive performance optimization over an implementation where these values are runtime values (small benchmarks measured at 10x, expected 5-7x). However, I find that C++ does not support this idiom very well. I am trying to refactor the library to make it easier to use by developers that aren't very familiar with templates or metaprogramming.
I would like to offer constexpr
functions that simplify the extraction of static constexpr
member variables (that are used to propogate the static values through the types), so that users do some basic metaprogramming without understanding the heavy machinery behind the scenes. For example,
#include <type_traits>
template <int A, int B>
class Example {
public:
static constexpr auto C = A + B;
private:
int b {0};
};
template <int A, int B>
constexpr auto getC(Example<A, B> e) { return decltype(e)::C; }
/**** USER CODE ****/
int main()
{
Example<1, 2> e;
Example<2, getC(e)> f;
}
This does not work, e
being non-constexpr can be used in a constexpr context, even if you aren't using the value (strangely if Example
had no runtime members, it would work). One could write decltype(e)::C
instead, but if it's a reference they would have to std::remove_reference<decltype(C)>::type::C
and of course they would have to know to do this, and know enough to get around common problems like the remove_reference
call. And at that point we are off in the weeds for the typical programmer this library is meant for.
constexpr
functions don't work, macros don't work with the type system so they are also insufficient, how else could I accomplish this?
Upvotes: 0
Views: 138
Reputation: 1595
I chose a combination of Nathan's answer, and an answer that was deleted, for the implementation of getC
.
template <typename T, typename = std::void_t<decltype(std::remove_reference_t<T>::C)>>
constexpr int getC_() { return std::remove_reference_t<T>::C; }
#define getC(e) (getC_<decltype(e)>())
The function does the heavy lifting and gives a good error message if you give it an invalid type thanks to the guard. And the macro does the messy (to a beginner) invocation in a way that can never fail on it's own.
getC(1); // error: 'C' is not a member of 'std::remove_reference<int>::type' {aka 'int'}
Upvotes: 1
Reputation: 180415
You can use a macro to wrap
std::remove_reference<decltype(C)>::type::C
That would look like
#define getC(obj) std::remove_reference_t<decltype(obj)>::C
Upvotes: 1