Meta-types and Meta-functions¶
Template meta-programming has become an indispensible part of modern C++. In C++11, new features such as Variadic template and Template alias makes meta-programming much more efficient and convenient than before. CLUE++ provides a set of tools to facilitate meta programming, which take full advantage of these new C++ features.
For those who are not familiar with C++ meta-programming, Andrzej has a great blog that provides an excellent introduction of this topic.
Important Note: all meta-programming facilities in CLUE++ are within the
namespace clue::meta.
Basic types¶
A set of types to support meta-programming:
// Note: all names below are within the namespace clue::meta
using std::integral_constant;
// Indicator of a C++ type
template<typename T>
struct type_ {
using type = T;
};
// Extract an encapsulated type
template<typename A>
using get_type = typename A::type;
// Indicator of a nil type (nothing)
struct nil_{};
// Static boolean value
template<bool V>
using bool_ = integral_constant<bool, V>;
using true_ = bool_<true>;
using false_ = bool_<false>;
// Static integral values
template<char V> using char_ = integral_constant<char, V>;
template<int V> using int_ = integral_constant<int, V>;
template<long V> using long_ = integral_constant<long, V>;
template<short V> using short_ = integral_constant<short, V>;
template<unsigned char V> using uchar_ = integral_constant<unsigned char, V>;
template<unsigned int V> using uint_ = integral_constant<unsigned int, V>;
template<unsigned long V> using ulong_ = integral_constant<unsigned long, V>;
template<unsigned short V> using ushort_ = integral_constant<unsigned short, V>;
// Static size value
template<size_t V> using size_ = integral_constant<size_t, V>;
// Extract the value type of a static value.
template<class A>
using value_type_of = typename A::value_type;
Sometimes, it is useful to combine two types. For this purpose, we provide a
pair_ type to express a pair of types, as well as meta-functions first
and second to retrieve them.
// Note: all names below are within the namespace clue::meta
template<typename T1, typename T2>
struct pair_ {
using first_type = T1;
using second_type = T2;
};
template<typename T1, typename T2>
struct first<pair_<T1, T2>> {
using type = T1;
};
template<typename T1, typename T2>
struct second<pair_<T1, T2>> {
using type = T2;
};
template<class A> using first_t = typename first<A>::type;
template<class A> using second_t = typename second<A>::type;
Note
The meta-functions first and second are also specialized for other
meta data structures, such as the meta sequence.
Static Index Sequence¶
The library provides useful facilities to construct static index sequence, which is useful for splatting elements of a tuples as arguments.
// index_seq can be used to represent a static sequence of indexes
template<size_t... Inds>
struct index_seq{};
// make_index_seq<N> constructs index_seq<0, ..., N-1>
make_index_seq<0>; // -> index_seq<>
make_index_seq<1>; // -> index_seq<1>
make_index_seq<4>; // -> index_seq<0, 1, 2, 3>
The following example shows how one can leverage make_index_seq to splat
tuple arguments.
// suppose you have a function join can accepts arbitrary number of arguments
template<class... Args>
void join(const Args&... args) { /* ... */ }
// the join_tup function can splat elements of a tuple
template<class... Args, size_t... I>
void join_tup_impl(const std::tuple<Args...>& tup, clue::meta::index_seq<I...>) {
join(std::get<I>(tup)...);
}
template<class... Args>
void join_tup(const std::tuple<Args...>& tup) {
join_tup_impl(tup, clue::meta::make_index_seq<sizeof...(Args)>{});
}
join_tup(std::make_tuple("abc", "xyz", 123));
Basic functions¶
The library also has a series of meta-functions to work with types or static values.
Arithmetic functions¶
// Note: all names below are within the namespace clue::meta
template<typename A>
using negate = integral_constant<value_type_of<A>, -A::value>;
template<typename A>
using next = integral_constant<value_type_of<A>, A::value+1>;
template<typename A>
using prev = integral_constant<value_type_of<A>, A::value-1>;
template<typename A, typename B>
using plus = integral_constant<value_type_of<A>, A::value + B::value>;
template<typename A, typename B>
using minus = integral_constant<value_type_of<A>, A::value - B::value>;
template<typename A, typename B>
using mul = integral_constant<value_type_of<A>, A::value * B::value>;
template<typename A, typename B>
using div = integral_constant<value_type_of<A>, A::value / B::value>;
template<typename A, typename B>
using mod = integral_constant<value_type_of<A>, A::value % B::value>;
// aliases, to cover the names in <functional>
template<typename A, typename B> using multiplies = mul<A, B>;
template<typename A, typename B> using divides = div<A, B>;
template<typename A, typename B> using modulo = mod<A, B>;
Comparison functions¶
// Note: all names below are within the namespace clue::meta
template<typename A, typename B> using eq = bool_<(A::value == B::value)>;
template<typename A, typename B> using ne = bool_<(A::value != B::value)>;
template<typename A, typename B> using gt = bool_<(A::value > B::value)>;
template<typename A, typename B> using ge = bool_<(A::value >= B::value)>;
template<typename A, typename B> using lt = bool_<(A::value < B::value)>;
template<typename A, typename B> using le = bool_<(A::value <= B::value)>;
// aliases, to cover the names in <functional>
template<typename A, typename B> using equal_to = eq<A, B>;
template<typename A, typename B> using not_equal_to = ne<A, B>;
template<typename A, typename B> using greater = gt<A, B>;
template<typename A, typename B> using greater_equal = ge<A, B>;
template<typename A, typename B> using less = lt<A, B>;
template<typename A, typename B> using less_equal = le<A, B>;
Logical functions¶
-
class
not_¶ The member constant
not_<A>::valueis equal to!A::value.
-
class
and_¶ The member constant
and_<A, B>::valueistrueiff bothA::valueandB::valueis true.
-
class
or_¶ The member constant
or_<A, B>::valueistrueiff eitherA::valueorB::valueis true.
Note
The meta-functions and_<A, B> and or_<A, B> implement the
short-circuit behavior. In particular, when A::value == false,
and_<A, B>::value is set to false without examining the internals
of B. Likewise, when A::value == true, or_<A, B>::value is set
to true without examining the internals of B.
Select¶
C++11 provides std::conditional for static dispatch based on a condition.
However, using this type in practice, especially in the cases with multiple
branches, is very cumbersome. Below is an example that uses std::conditional
to map a numeric value to a signed value type.
#include <type_traits>
template<typename T>
using signed_type =
typename std::conditional<
std::is_integral<T>::value,
typename std::conditional<std::is_unsigned<T>::value,
typename std::make_signed<T>::type,
T
>::type,
typename std::conditional<std::is_floating_point<T>::value,
T,
nil_t
>::type
>::type;
With the meta-function select and the helper alias select_t, this can be
expressed in a much more elegant and concise way:
#include <clue/meta.hpp>
using namespace clue;
template<typename T>
using signed_type =
meta::select_t<
std::is_unsigned<T>, std::make_signed<T>,
std::is_signed<T>, meta::type_<T>,
std::is_floating_point<T>, meta::type_<T>,
meta::type_<nil_t> >;
Specifically, meta::select is a variadic class template, described as follows:
select<C1, A1, R>has a member typedeftypewhich is equal toA1::typewhenC1::valueis true, orR::typeotherwise.- This meta-function can accept arbitrary odd number of arguments. Generally,
select<C1, A1, C2, A2, ..., Cm, Am, R>has a member typedeftypewhich is equal toA1::typewhenC1::valueis true, otherwise, it is equal toA2::typeifC2::valueis true, and so on. If no conditions are met, it is set toR::type.
A helper alias select_t is provided to further simplify the use:
template<typename... Args>
using select_t = typename select<Args...>::type;
Note
The meta-function select implements a short-circuit behavior. It
examines the conditions sequentially, and once it finds a condition that is
true, it extracts the next type, and will not continue to examine
following conditions.
Variadic Reduction¶
A set of variadic meta-functions are provided to perform reduction over static values.
-
class
meta::sum¶ meta::sum<args...>has a member constantvaluethat equals the sum of argument’s member values.
-
class
meta::prod¶ meta::prod<args...>has a member constantvaluethat equals the product of argument’s member values.
-
class
meta::maximum¶ meta::maximum<args...>has a member constantvaluethat equals the maximum of argument’s member values.
-
class
meta::minimum¶ meta::minimum<args...>has a member constantvaluethat equals the minimum of argument’s member values.
-
class
meta::all¶ meta::all<args...>has a member constantvalue, which equalstrueif all argument’s member values aretrue, orfalseotherwise.Note: all<>::value == true.
-
class
meta::any¶ meta::any<args...>has a member constantvalue, which equalstrueif any of the argument’s member value istrue, orfalseotherwise.Note: any<>::value == false.
-
class
meta::count_true¶ meta::count_true<args...>has a member constantvalue, which equals the number of arguments whose member value istrue.
-
class
meta::count_false¶ meta::count_false<args...>has a member constantvalue, which equals the number of arguments whose member value isfalse.
-
class
meta::all_same¶ meta::all_same<args...>has a member constantvalue, which indicates whether all argument types are the same.
Note
The meta-functions all and any both implement the
short-circuit behaviors. They won’t look further once the
resultant value can be determined.