Compile-time arguments and function wrappers
Passing values in C++ as a template arguments has some inflexibilities. For example, if template function has been wrapped into a functor class, there are no ways to pass some value as a template argument.
There is the following template function:
template <size_t count, typename T, size_t N>
vec<T, N> swap(const vec<T, N>& x);
which requires first template argument to be passed at every call, but we can’t wrap it to the functor:
struct fn_swap
{
template <typename... Args>
auto operator()(Args&&... args)
{
return swap< ??? >(std::forward<Args>(args)...);
}
};
As a first attempt, we can move template parameters to the wrapper itself:
template <size_t count>
struct fn_swap
{
template <typename... Args>
auto operator()(Args&&... args)
{
return swap<count>(std::forward<Args>(args)...);
}
};
Good enough until we want to create this wrapper in one place and pass count
in another.
Solution
All these can be easily worked around by introducing a new type cval_t
which can hold compile-time value and can be passed as a regular argument.
This is small fragment of cval_t
implementation from the CoMeta C++14 metaprogramming library:
template <typename T, T val>
struct cval_t
{
constexpr static T value = val;
constexpr cval_t() noexcept = default;
constexpr cval_t(const cval_t&) noexcept = default;
constexpr cval_t(cval_t&&) noexcept = default;
using value_type = T;
using type = cval_t;
constexpr operator value_type() const { return value; }
constexpr value_type operator()() const { return value; }
};
template <size_t val>
using csize_t = cval_t<size_t, val>;
template <size_t val>
constexpr csize_t<val> csize{};
In addition to
csize_t
, which is an alias forcval_t<size_t, value>
, there are aliases forint
,bool
anduint
types.
Now we can write the following prototype for the swap
function:
template <size_t count, typename T, size_t N>
vec<T, N> swap(csize_t<count>, const vec<T, N>& x);
and pass all arguments including those that must be known at compile-time to our wrapper:
fn_swap swap; // we don't have to specify count here
vec<T, 4> x;
x = swap(csize<2>, x); // pass number as a regular argument
What about a list of values?
For passing a list of compile-time values to arbitrary functions we can create a class similar to this:
template <typename T, T... values>
struct cvals_t
{
using type = cvals_t<T, values...>;
constexpr static size_t size() { return sizeof...(values); }
template <size_t index>
constexpr T operator[](csize_t<index>) { return get(csize<index>); }
template <size_t index>
constexpr static T get(csize_t<index> = csize_t<index>());
constexpr static T front() { return get(csize<0>); }
constexpr static T back() { return get(csize<size() - 1>); }
// to be able to iterate this list in for(T v: vals)
static const T* begin() { return array(); }
static const T* end() { return array() + size(); }
};
With this class, passing numeric and boolean constants to various functions is quite easy:
template <size_t... Indices, typename T, size_t N>
inline vec<T, N> permute(const vec<T, N>& x, elements_t<Indices...> = elements_t<Indices...>())
{
return shufflevector<N, internal::shuffle_index_permute<N, Indices...>>(x);
}
Note, that both ways of passing arguments are perfectly possible:
vec<T, 3> v = permute(pack(1, 2, 3), elements<2, 1, 0>);
and
vec<T, 3> v = permute<2, 1, 0>(pack(1, 2, 3));
And we don’t have to write two different prototypes to make both ways work.
Compile-time math
In CoMeta there are operator overloads defined for cval_t
and cvals_t
types
and all their specializations.
This means that all regular calculations
which can be applied to number constants, can be applied to compile-time values too.
Few examples:
constexpr auto x = csizes<10, 20, 30, 40, 50>;
constexpr auto a = x[csize<1>]; // get second value, a = csize<20>
constexpr auto b = x[csizes<0, 1>]; // get first two values
constexpr auto c = x[csizes<4, 3, 2, 1, 0>]; // reverse x
constexpr auto d = x / csize<10>; // d = csizes<1, 2, 3, 4, 5>
All these techniques are widely used in the KFR C++ DSP framework for template expressions.