At many points in my codebase, I use the following pattern:
template <uint8_t dim>
class Example
{
static_assert((dim == 1 || dim == 2 || dim == 3), "Example objects must be defined in one, two, or three dimensions.");
// other stuff
};
and, for regular functions,
template <uint8_t dim>
void some_func()
{
if constexpr (dim < 2 || dim > 3)
throw std::invalid_argument("some_func() is valid for 2 or 3 dimensions only.")
// other stuff
}
I want to avoid having to check that the dimensionality is correct everywhere. Instead, I want something like
template <Dim<1,3> dim> // enforces 1 <= dim <= 3
class Example
// etc.
template <Dim dim> // by default enforces 1 <= dim <= 3
class AnotherExample
// etc.
template <Dim<2> dim> // enforces dim == 2
class AnotherAnotherExample
// etc.
template <Dim<2,3> dim> // enforces 2 <= dim <= 3
void some_func()
// etc/
Note that the allowed dimensions are different for different classes and functions, and the dim
template argument is not the same everywhere in the codebase.
An obvious solution would be something like
template <uint8_t dim_lower, uint8_t dim_upper>
class Dim
{
// something
}
but I don't see how I can declare an object of this Dim
in a way that it behaves like a uint8_t
. I'd have to instead have Dim
store a uint8_t
member and then overload the ()
operator or something, and then I can't simply do template <Dim dim>
. Maybe there's a way to use using
to get what I want, but I don't see how I can incorporate conditions in using
.
You used C++20 syntax in the question, so assuming you can use that, you could just constrain your functions
template<uint8_t x, uint8_t lo, uint8_t hi>
concept in_range = x >= lo && x < hi;
template<uint8_t dim>
requires in_range<dim, 0, 42>
void foo()
{
}
If you want a shorthand for common cases
template<uint8_t x>
concept dim3d = in_range<x, 0, 3>;
template<uint8_t dim>
requires dim3d<dim>
void bar()
{
}
If you only have C++17, there's no syntax sugar whatsoever, and you have to convert all of those into std::enable_if_t
template<uint8_t x, uint8_t lo, uint8_t hi>
inline constexpr bool in_range_v = x >= lo && x < hi;
template<uint8_t dim, std::enable_if_t<in_range_v<dim, 0, 42>, bool> = false>
void foo()
{
}