-
Notifications
You must be signed in to change notification settings - Fork 82
Metafunctions
We distinguish
- value metafunctions are functions or other constructs that return a value at compile time
- type metafunctions are constructs that return a type
There are different ways to implement value metafunctions in C++:
-
struct
templates withenum
definitions -
struct
templates withstatic const
orstatic constexpr
data members - free
constexpr
functions (only meta if evaluated inconstexpr
context) - global
constexpr
variable templates
In SeqAn3 we use the style of the STL which is:
- a (possibly constrained)
struct
template withstatic constexpr
value
member; and - a shortcut of the same name, suffixed with
_v
as aconstexpr
variable template
Example:
template <typename alphabet_type>
requires detail::internal_alphabet_concept<alphabet_type>
struct alphabet_size
{
static constexpr underlying_integral_t<alphabet_type> value = alphabet_type::value_size;
};
template <typename alphabet_type>
constexpr underlying_integral_t<alphabet_type> alphabet_size_v = alphabet_size<alphabet_type>::value;
Note that internally, you may of course use constexpr
functions or other forms of metaprogramming, but the public interfaces shall be as specified here.
There are different ways to implement type metafunctions in C++:
-
struct
templates withtypedef
orusing
declarations - global templatized
using
declarations - calling
decltype()
on (constexpr
) functions
In SeqAn3 we use the style of the STL which is:
- a (possibly constrained)
struct
template with a localtype
alias; and - a global shortcut of the same name, suffixed with
_t
as a templatized using declaration
Example:
template <typename alphabet_type>
requires detail::internal_alphabet_concept<alphabet_type>
struct underlying_integral
{
using type = typename alphabet_type::integral_type;
};
template <typename alphabet_type>
using underlying_integral_t = typename underlying_integral<alphabet_type>::type;
There are different ways to specialize type metafunctions:
- βpartial template specialization
- stronger constraints
The first case is especially handy for template subclassing, but it has the drawback that it does not work if regular inheritance is used (which is now more often the case because we rely on concepts in other places):
template <typename type>
struct is_foo : std::false_type
{};
template <typename ...>
struct is_foo<foo_impl<...>> : std::true_type
{};
//is_foo<foo_impl<int>> == true_type
template <typename t>
struct my_type : foo_impl<t>
{
//...
};
//is_foo<my_type<int>> == false_type
The desired behaviour can be achieved with a template template
and constraints:
template <typename type>
struct is_foo : std::false_type
{};
template <template <typename...> type, typename ...types>
requires std::is_base_of_v<foo_impl<types...>, type<types...>>
struct is_foo<type<types...>> : std::true_type
{};
//is_foo<foo_impl<int>> == true_type
template <typename t>
struct my_type : foo_impl<t>
{
//...
};
//is_foo<my_type<int>> == true_type