Parameter pack used for default template parameter - c++

I'd like to do something like this:
template<int... myints>
struct A
{}
template<int... myints, class traits = A<myints...>>
struct B
{}
I know that parameter packs should appear only at the end, but I remember there are some exceptions. Do you think that is there a way to make this code (or something similar) work?
Thanks

One way:
wrap the types into tuples.
use specialisation to convert type lists to types
#include <tuple>
#include <utility>
template<int...myints> struct A {};
namespace detail {
template<class Sequence>
struct make_A_from;
template<class T, T...ts>
struct make_A_from<std::integer_sequence<T, ts...>> {
using type = A<ts...>;
};
}
template<class Sequence> using make_A_from = typename detail::make_A_from<Sequence>::type;
template<class Sequence, class TraitsTuple = make_A_from<Sequence> >
struct B;
template<typename Int, Int...myints, class Traits>
struct B<std::integer_sequence<Int, myints...>, Traits>
{
using ints_tuple = std::tuple<std::integral_constant<Int, myints>...>;
using traits_type = Traits;
};
namespace detail {
template<typename Int, Int...is>
struct make_B
{
using type = B<std::integer_sequence<Int, is...>>;
};
}
template<int...is> using make_B = B<std::integer_sequence<int, is...>>;
int main()
{
make_B<1,3,4,5> b;
B<std::integer_sequence<int, 5,6,7,8>> b2;
B<std::integer_sequence<int, 5,6,7,8>, class CustomTrait> b3;
}

Related

Can you "hop" between "linked classes" in C++ metaprogramming?

Suppose you have something like this:
template<class D>
class HasDef {
public:
typedef D Def;
};
class A : public HasDef<class B> {};
class B : public HasDef<class C> {};
class C {};
So it is like a "metaprogramming linked list", with type links, via the included typedef Def. Now I want to make a template "Leaf" that, when applied to A, follows the links to yield C:
void f() {
Leaf<A>::type v; // has type C
}
Is it even possible at all to do this? I've tried some methods with std::compare and similar, but none are valid code: everything seems to run into issues with either that C has no Def typedef, or else that the type Leaf<> itself is incomplete when the inner recursive "call" is made so it (or its internal type type) cannot be referenced.
FWIW, the reason I want this is for making a "hierarchical state machine" where that Def represents the default state for each state in the hierarchy, and something a bit more elaborate that this seems to provide a fairly neat and clean "user interface syntax" for it.
I don't really like f(...) in modern code, thus my version uses void_t from C++17:
#include <type_traits>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T, typename=void>
struct DefPresent : std::false_type{};
template <typename T>
struct DefPresent<T, std::void_t<typename T::Def>> : std::true_type{};
template<typename T, bool deeper = DefPresent<T>::value>
struct Leaf
{
using Type = typename Leaf<typename T::Def>::Type;
};
template<typename T>
struct Leaf<T, false >
{
typedef T Type;
};
static_assert(std::is_same<typename Leaf<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf<A>::Type, C>::value, "A");
https://godbolt.org/z/5h5rfe81o
EDIT: just for completenes, 2 C++20 variants utilizing concepts. Tested on GCC 10
#include <type_traits>
#include <concepts>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T>
concept DefPresent = requires(T a)
{
typename T::Def;
};
template<typename T>
struct Leaf
{
using Type = T;
};
template<typename T>
requires DefPresent<T>
struct Leaf<T>
{
using Type = Leaf<typename T::Def>::Type;
};
static_assert(std::is_same_v<typename Leaf<C>::Type, C>, "C");
static_assert(std::is_same_v<typename Leaf<B>::Type, C>, "B");
static_assert(std::is_same_v<typename Leaf<A>::Type, C>, "A");
template<typename T>
struct Leaf2
{
template <typename M>
static M test(M&&);
template <DefPresent M>
static auto test(M&&) -> typename Leaf2<typename M::Def>::Type;
using Type = decltype(test(std::declval<T>()));
};
static_assert(std::is_same<typename Leaf2<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf2<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf2<A>::Type, C>::value, "A");
https://godbolt.org/z/vcqEaPrja
You can implement this with SFINAE to separate types that have the typedef from ones that don't when trying to follow them to their leaf. I used the trick from this SO answer here.
The first implementation for Leaf follows the typedef recursively, the second one just defines the type itself as there is no typedef to follow.
Also note I changed your classes to struct for default-public inheritance. Also I changed the order of their definitions for this to compile.
Compiler explorer
#include <type_traits>
namespace detail
{
template<typename T> struct contains_def {
template<typename U> static char (&test(typename U::Def const*))[1];
template<typename U> static char (&test(...))[2];
static const bool value = (sizeof(test<T>(0)) == 1);
};
template<typename T, bool has = contains_def<T>::value>
struct Leaf {
using type = typename Leaf<typename T::Def>::type;
};
template<typename T>
struct Leaf<T, false> {
using type = T;
};
}
template <typename T>
using Leaf = detail::Leaf<T>; // expose Leaf to the global scope
template <typename T>
using Leaf_t = typename Leaf<T>::type; // for convenience
template<typename T>
struct AddDef {
using Def = T;
};
struct C {};
struct B : AddDef<C> {};
struct A : AddDef<B> {};
static_assert(std::is_same_v<Leaf_t<A>, C>);
static_assert(std::is_same_v<Leaf_t<B>, C>);
static_assert(std::is_same_v<Leaf_t<C>, C>);
You could make it a type trait:
#include <utility> // declval
template<class L>
struct Leaf {
template<class M>
static L test(const M&); // match L's without Def
template<class M> // match L's with Def
static auto test(M&&) -> typename Leaf<typename M::Def>::type;
// find matching test() overload, prefer test(M&&):
using type = decltype( test(std::declval<L>()) );
};
template<class L> // bonus helper types
using Leaf_t = typename Leaf<L>::type;
Demo and template resolution # cppinsights

Typetrait to get value type of contiguous-memory containers

I need a typetrait that would return value type for the following "contiguous-memory" types (to which operator[] can be applied):
std::vector<T, Args...>, std::array<T, N>, T[], T[N], and T* (with possibly CV qualifiers like T* const). The trait should return T for all above types.
Is there a more concise implementation in C++17, than the tedious one below? Like somehow gathering all pointer cases in one specialization and appliyng std::remove_ptr, same for T[N] with std::remove_extent.
template<class T> struct remove_array_like;
template<class T> struct remove_array_like<T*> { using type = T; };
template<class T> struct remove_array_like<T* const> { using type = T; };
template<class T> struct remove_array_like<T* volatile> { using type = T; };
template<class T> struct remove_array_like<T* const volatile> { using type = T; };
template<class T> struct remove_array_like<T[]> { using type = T; };
template<class T, std::size_t N>
struct remove_array_like<T[N]> { using type = T; };
template<class T, std::size_t N>
struct remove_array_like<std::array<T, N>> { using type = T; };
template<class T, class... Args>
struct remove_array_like<std::vector<T, Args...>> { using type = T; };
I need a typetrait that would return value type for the following "contiguous-memory" types (to which operator[] can be applied):
To "extract" the required type, it seems to me that you can simply use the operator[] itself, removing references, volatiles and consts. See the following helper struct
template <typename T>
struct contained_type
{ using type = std::remove_cv_t<
std::remove_reference_t<
decltype(std::declval<T>()[0])>>; };
You can write your contained_type struct using SFINAE, so with an additional defaulted template parameter
template <typename, typename = void>
struct remove_array_like;
It seems to me that you needs only three specializations.
One for contigous-memory containers (so for containers with a data() method that return a pointer to the start of the contigous contained memory), that is for std::vector, std::array, std::string and other strings types
template <typename T>
struct remove_array_like<T, std::void_t<decltype(std::declval<T>().data())>>
: public contained_type<T>
{ };
one for pointers
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_pointer_v<T>>>
: public contained_type<T>
{ };
and one for arrays
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_array_v<T>>>
: public contained_type<T>
{ };
The following is a full compiling C++17 example
#include <set>
#include <array>
#include <deque>
#include <vector>
#include <string>
#include <type_traits>
template <typename T>
struct contained_type
{ using type = std::remove_cv_t<
std::remove_reference_t<
decltype(std::declval<T>()[0])>>; };
template <typename, typename = void>
struct remove_array_like;
template <typename T>
struct remove_array_like<T, std::void_t<decltype(std::declval<T>().data())>>
: public contained_type<T>
{ };
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_pointer_v<T>>>
: public contained_type<T>
{ };
template <typename T>
struct remove_array_like<T, std::enable_if_t<std::is_array_v<T>>>
: public contained_type<T>
{ };
int main ()
{
using T1 = remove_array_like<std::vector<int>>::type;
//using T2 = remove_array_like<std::deque<int>>::type; // error! no data(), no contigous
using T3 = remove_array_like<std::array<int, 1u>>::type;
using T4 = remove_array_like<std::string>::type;
using T5 = remove_array_like<int * volatile>::type;
using T6 = remove_array_like<int const [1u]>::type;
using T7 = remove_array_like<int volatile []>::type;
// using T8 = remove_array_like<std::set<int>>::type; // error!
// using T9 = remove_array_like<int>::type; // error!
static_assert( std::is_same_v<T1, int> );
static_assert( std::is_same_v<T3, int> );
static_assert( std::is_same_v<T4, char> );
static_assert( std::is_same_v<T5, int> );
static_assert( std::is_same_v<T6, int> );
static_assert( std::is_same_v<T7, int> );
}
Starting from C++20 you can use std::remove_cvref, so contained_type can be simplified as follows
template <typename T>
struct contained_type
{ using type = std::remove_cvref_t<
decltype(std::declval<T>()[0])>; };

Specializing struct template using enable_if

I'm trying to create a template class, that will implement a callback with a different signature, depending on if it is instantiated with one type, or two.
struct NoIntermediate
{
};
template<typename R, typename I>
struct ParserCallbackSwitch
{
using type = std::function<bool(const std::string &, R&, I&)>;
}
template<typename R, typename I = NoIntermediate>
class OtherClass
{
public:
typedef ParserCallbackSwitch<R, I>::type ParserType;
}
Now I want to add code so that if I is not specified when instantiating 'OtherClass', that ParserCallbackSwitch would be:
template<typename R, typename I>
struct ParserCallbackSwitch
{
using type = std::function<bool(const std::string &, R&)>;
}
Notice that in this case ParserCallbackSwitch::type is a function with only two parameters.
I wanted to be able to do the following:
OtherClass<int, float> p; // p::ParserType = std::function<bool(std::string &, int &, float &);
OtherClass<int> q; // q::ParserType = std::function<bool(std::string &, int &);
I can't figure out how to partially specify ParserCallbackSwitch for the case when I is of type NoIntermediate (i.e. I is not specified)
SOLUTION: Based on response below. Here is the code I finally ended up using.
struct NoIntermediate {};
template<typename R, typename I = NoIntermediate>
struct ParserCallbackSwitch
{
using type = std::function<bool(const std::string &, R&, I&)>;
};
template<typename R>
struct ParserCallbackSwitch<R, NoIntermediate>
{
using type = std::function<bool(const std::string &, R&)>;
};
template<typename R, typename I = NoIntermediate>
class OtherClass
{
public:
typedef ParserCallbackSwitch<R, I>::type ParserType;
}
So! You're not properly specializing a template. You're defining two unrelated class templates that happen to have the same name.
There are multiple ways to do what you propose. This one gives the least specialized template a parameter pack.
#include <functional>
#include <type_traits>
template<typename... S>
struct OtherClass;
template<typename R, typename I>
struct OtherClass<R, I> {
using ParserType = std::function<bool(std::string&, R&, I&)>;
};
template<typename R>
struct OtherClass<R> {
using ParserType = std::function<bool(std::string&, R&)>;
};
int main(void) {
static_assert(std::is_same<OtherClass<int, float>::ParserType,
std::function<bool(std::string&, int&,
float&)>>(),
"Something wrong here.");
static_assert(std::is_same<OtherClass<int>::ParserType,
std::function<bool(std::string&, int&)>>(),
"Hmmmmmm.");
return 0;
}
Your idea of using a default type in a parameter also works, but your syntax was slightly off. Here's how that would look.
#include <functional>
#include <type_traits>
template<typename R, typename I = void>
struct OtherClass {
using ParserType = std::function<bool(std::string&, R&, I&)>;
};
template<typename R>
struct OtherClass<R, void> {
using ParserType = std::function<bool(std::string&, R&)>;
};
int main(void) {
static_assert(std::is_same<OtherClass<int, float>::ParserType,
std::function<bool(std::string&, int&,
float&)>>(),
"Something wrong here.");
static_assert(std::is_same<OtherClass<int>::ParserType,
std::function<bool(std::string&, int&)>>(),
"Hmmmmmm.");
return 0;
}

Declaring a function with types of arguments taken from a foreign parameter pack

Suppose I have some kind of type list
template<typename... Types> struct TypeList {};
Now in some other class I can generate such a TypeList in a variety of ways.
template<class T> struct MyClass {
using MyList = TypeList<T, typename Something<T>::type, SomethingElse>;
// ...
};
How can I declare a method with types of arguments extracted from this type list? For example, if I set MyList = TypeList<int, float, const char*>, I wish a method
void my_method(int, float, const char*)
to be declared.
You could derive from a base class that implements the method:
template <typename> struct MethodProvider;
template <typename ...Args>
struct MethodProvider<TypeList<Args...>>
{
void my_method(Args ...args);
};
template <typename T>
struct MyClassAux
{
using MyList = TypeList<T, typename Something<T>::type, SomethingElse>;
};
template <typename T>
struct MyClass
: private MyClassAux<T>
, private MethodProvider<typename MyClassAux<T>::MyList>
{
using typename MyClassAux<T>::MyList;
using MethodProvider<typename MyClassAux<T>::MyList>::my_method;
// ...
};
You can use static_assert and std::is_same to constraint the arguments of my_method
#include <iostream>
#include <string>
#include <vector>
#include <type_traits>
template<typename... Types> struct TypeList {};
template<class T> struct MyClass {
using MyList = TypeList<int, T>;
template<typename ...Args>
void my_method(Args ...args) {
static_assert(std::is_same<MyList, TypeList<Args...>>::value, "invalid arguments");
auto dummy = {(std::cout << args << "\n", 0)...};
(void)(dummy); // avoid variable warning
}
};
int main()
{
MyClass<float> c;
c.my_method(1, 2.3f);
// c.my_method(1, ""); // unmatched arguments won't compile
// c.my_method(1);
}
Online Demo

Get twice as wide type

basically, i'd like to (at compile time) get the twice-as-wide type from a stdint type. I can do it by hand like this
template <typename T>
class twice_as_wide{};
template<>
class twice_as_wide<uint8_t>
{
public:
typedef uint16_t type;
};
template<>
class twice_as_wide<int8_t>
{
public:
typedef int16_t type;
};
template<>
class twice_as_wide<uint16_t>
{
public:
typedef uint32_t type;
};
ect, I just want to make sure that this doesn't exist yet. I'm using visual studio 2010 C++0X (annoying, i know) and already have a boost dependency. Does anyone know of an existing implementation of this?
If you don't mind another boost dependency, then you could do this:
#include <type_traits>
#include <boost/integer.hpp>
template <typename T, bool is_unsigned = std::is_unsigned<T>::value>
struct twice_as_wide
{
typedef typename boost::uint_t< 2 * std::numeric_limits<T>::digits>::exact type;
};
template<typename T>
struct twice_as_wide<T, false>
{
typedef typename boost::int_t< 2 * (std::numeric_limits<T>::digits + 1)>::exact type;
};
template< typename T>
using twice_as_wide_t = typename twice_as_wide<T>::type;
I'd say, use Boost Integer. Demo that keeps signed-ness of the source type: Live on Coliru
#include <boost/integer.hpp>
#include <limits>
namespace helpers
{
// wrappers around Boost Integer http://www.boost.org/doc/libs/1_54_0/libs/integer/doc/html/boost_integer/integer.html#boost_integer.integer.sized
template <bool is_signed, int bin_digits> struct select_twice;
template <int bin_digits> struct select_twice<true, bin_digits> {
using type = typename boost::int_t<bin_digits + 1>::least;
};
template <int bin_digits> struct select_twice<false, bin_digits> {
using type = typename boost::uint_t<bin_digits>::least;
};
}
template <typename Int>
using select_twice = helpers::select_twice<std::numeric_limits<Int>::is_signed, std::numeric_limits<Int>::digits*2>;
template <typename Int>
using twice_t = typename select_twice<Int>::type;
int main()
{
static_assert(std::is_same<uint16_t, twice_t<uint8_t>>::value, "oops");
static_assert(std::is_same<uint32_t, twice_t<uint16_t>>::value, "oops");
static_assert(std::is_same<uint64_t, twice_t<uint32_t>>::value, "oops");
}