Metafunction to extract argument parameter lists from multiple functions pointers - c++

For a single function it is possible to extract its parameter types like this:
template <class T>
struct Foo;
template <class Ret, class... Args>
struct Foo<Ret(*)(Args...)> { /* stuff */ };
Would it be possible to do the same for a series of function pointers? That is,
to be able to extract the arguments and then redeploy them in the same way? E.g. something like:
template <class.. T>
struct Foo;
template <class... Rets, class... Args>
struct Foo<Rets(*)(Args...)...> // I wish this worked
{
std::tuple<Rets(*)(Args...)...> fns; // Ditto
}

Late to the party, but this might be useful. First let's define some utilities:
template <class... T>
using void_t = void;
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
Then a metafunction to check whether a type is a function pointer:
template <class F>
struct is_function_pointer : std::integral_constant<bool, false> { };
template <class Ret, class... Args>
struct is_function_pointer<Ret (*)(Args...)> : std::integral_constant<bool, true> { };
Using these, let's write the class. The SFINAE toggle is placed first because the variadic pack takes up the right side:
template <class = void_t<>, class... T>
struct Foo_impl;
template <class... FuncPtrs>
struct Foo_impl<
std::enable_if_t<all_true<is_function_pointer<FuncPtrs>{}...>{}>,
FuncPtrs...
> {
std::tuple<FuncPtrs...> fns;
};
Finally, a typedef to hide the SFINAE toggle from the user:
template <class... T>
using Foo = Foo_impl<void_t<>, T...>;
VoilĂ ! Foo<void (*)(), int (*)(double)> goes to the specialization of Foo_impl which contains the adequate tuple, Foo<int, double> goes to the main template.
See it live on Coliru

Related

Expand template type

Is there a way to convert each type of std::tuple into specific subtypes?
I have following code
struct Foo1
{
struct A{};
struct B{};
};
struct Foo2
{
struct A{};
struct B{};
};
using myTypes = std::tuple<Foo1, Foo2>;
Is there a way to convert myTypes into following type?
std::tuple<Foo1::A, Foo1::B, Foo2::A, Foo2::B>;
Order of types doesn't matter, but would be nice to have it like above.
If A/B name are fixed, you might do
template <typename... Ts>
using AB_Types = std::tuple<typename Ts::A..., typename Ts::B...>;
So AB_Types<Foo1, Foo2> is std::tuple<Foo1::A, Foo2::A, Foo1::B, Foo2::B>.
Having expected order would also be possible:
template <typename... Ts>
using AB_Types_ordered =
decltype(std::tuple_cat(std::tuple<typename Ts::A, typename Ts::B>{}...));
and if source is a tuple, just add extra layer
template <typename>
struct AB_impl;
template <typename... Ts>
struct AB_impl<std::tuple<Ts...>>
{
using type = AB_Types<Ts...>; // AB_Types_ordered<Ts...>
};
template <typename T>
using AB_Types_from_tuple = typename AB_impl<T>::type;
An alternative solution based on the Boost.Mp11 library:
template<class T>
using add_AB = std::tuple<typename T::A, typename T::B>;
template <typename Tuple>
using AB_Types_from_tuple =
boost::mp11::mp_flatten<boost::mp11::mp_transform<add_AB, Tuple>>;
static_assert(std::is_same_v<
AB_Types_from_tuple<myTypes>,
std::tuple<Foo1::A, Foo1::B, Foo2::A, Foo2::B>
>);

Is there a way to pass all type template parameters in an old class template into a new class template?

I've created a simple template class called tuple_tag which is identical to std::tuple but only acts as a tag.
// tuple_tag
template <typename...> struct tuple_tag {};
// tuple_tag_element
template <size_t I, typename T>
struct tuple_tag_element;
template <size_t I, typename Head, typename... Tail>
struct tuple_tag_element<I, tuple_tag<Head, Tail...>>
: tuple_tag_element<I - 1, tuple_tag<Tail...>> {};
template <typename Head, typename... Tail>
struct tuple_tag_element<0, tuple_tag<Head, Tail...>>
: std::type_identity<Head> {};
// tuple_tag_element_t
template <size_t I, typename T>
using tuple_tag_element_t = tuple_tag_element<I, T>::type;
// tuple_tag_size
template <typename T>
struct tuple_tag_size;
template <typename T> requires (std::is_reference_v<T> || std::is_const_v<T>)
struct tuple_tag_size<T> : tuple_tag_size<std::remove_cvref_t<T>> {};
template <typename... Ts>
struct tuple_tag_size<tuple_tag<Ts...>>
: std::integral_constant<size_t, sizeof...(Ts)> {};
// tuple_tag_size_v
template <typename T>
inline constexpr size_t tuple_tag_size_v = tuple_tag_size<T>::value;
Here:
using new_type_1 = to_tuple_type<tuple_tag<int, double>>::type;
// new_type_1 = std::tuple<int, double>;
using new_type_2 = to_tuple_tag_type<std::tuple<int, double>>::type;
// new_type_2 = tuple_tag<int, double>;
Where to_tuple_type takes a type template parameter tuple_tag<...> which will be converted into type std::tuple<...>, and to_tuple_tag_type takes a type template parameter std::tuple<...> which will be converted into type tuple_tag<...>.
What I am trying to achieve here is to pass all type template parameters from tuple_tag into std::tuple and vice-versa.
This is my prototype for to_tuple_type where it fails:
template <typename TupleTag>
struct to_tuple_type {
using type = std::tuple<...>;
};
Where type alias will be expanded into:
using type = std::tuple<tuple_tag_element_t<Index, TupleTag>...>;
...
using type = std::tuple<
tuple_tag_element_t<0, TupleTag>,
tuple_tag_element_t<1, TupleTag>,
...,
tuple_tag_element_t<N - 1, TupleTag>
>;
Where N is equal to tuple_tag_size_v<TupleTag>.
What I could only think of is to use std::index_sequence but I don't know where do I introduce the pack.
3 steps. First, make a pack
using indexes=std::make_index_sequence<tuple_tag_size<TupleTag>;
then have a helper that expands the pack. I like this one:
template<auto x>
using constant_t=std::integral_constant<decltype(x),x>;
template<auto x>
constexpr constant_t<x> constant={};
template<std::size_t...Is>
constexpr auto all_indexes( std::index_sequence<Is...> ){
return [](auto f){
return f(constant<Is>...);
};
}
now we can
template<class T>
struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag={};
using type=typename decltype(all_indexes(indexes{})([](auto...Is){
return tag<std::tuple<tuple_tag_element_t<Is, TupleTag>...>;
}))::type;
if that has no tpyos.
There is a simple solution that applies partial template specialization:
// to_tuple_type
template <typename Tup>
struct to_tuple_type;
template <typename... Ts>
struct to_tuple_type<tuple_tag<Ts...>> : std::type_identity<std::tuple<Ts...>> {};
// to_tuple_type_t
template <typename Tup>
using to_tuple_type_t = to_tuple_type<Tup>::type;
// to_tuple_tag_type
template <typename Tup>
struct to_tuple_tag_type;
template <typename... Ts>
struct to_tuple_tag_type<std::tuple<Ts...>> : std::type_identity<tuple_tag<Ts...>> {};
// to_tuple_tag_type_t
template <typename Tup>
using to_tuple_tag_type_t = to_tuple_tag_type<Tup>::type;

Retrieve the template a type is instantiated from

How can I retrieve the template a type was originally instantiated from?
I'd like to do the following:
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = get_template<SomeType>::template type<T>;
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
I know I can achieve this through partial specialization, but this forces me to specialize get_template for every template I want to use it with:
template <typename T>
struct get_template;
template <typename T>
struct get_template<Foo<T>>
{
template <typename X>
using type = Foo<X>;
};
live example
Is there a way around this limitation?
You could do something like that, using a template template parameter (should work for templates with any number of type arguments):
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
Then to get the template:
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
As mentioned by #Yakk in the comment, the above only works for template that only have type arguments. You can specialize for template with specific pattern of type and non-type arguments, e.g.:
// Note: You need the first size_t to avoid ambiguity with the first specialization
template <template <class, size_t, size_t...> class Y, typename A, size_t... Sizes>
struct get_template<Y<A, Sizes...>> {
template <class U, size_t... OSizes>
using type = Y<U, OSizes...>;
};
...but you will not be able to specialize it for arbitrary templates.
DEMO (with Foo and std::pair):
#include <type_traits>
#include <map>
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
static_assert(std::is_same<SomeType, Template<Bar>>::value, "");
static_assert(std::is_same<Foo<int>, Template<int>>::value, "");
using PairIntInt = std::pair<int, int>;
using PairIntDouble = std::pair<int, double>;
template <typename U1, typename U2>
using HopeItIsPair =
typename get_template<PairIntDouble>::type<U1, U2>;
static_assert(std::is_same<PairIntDouble, HopeItIsPair<int, double>>::value, "");
static_assert(std::is_same<PairIntInt, HopeItIsPair<int, int>>::value, "");
Not sure I got the question. Would this work?
#include<type_traits>
#include<utility>
template<typename V, template<typename> class T, typename U>
auto get_template(T<U>) { return T<V>{}; }
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = decltype(get_template<T>(SomeType{}));
int main() {
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
}
You can generalize even more (SomeType could be a template parameter of Template, as an example), but it gives an idea of what the way is.

Traits class as a template template parameter

I have traits classes sprinkled about my code which follow the same basic idiom:
template<class Frame, typename = void>
struct frame_traits
{
typedef void base_frame_type;
};
template<class Frame>
struct frame_traits<Frame, typename std::void_t<
typename Frame::base_frame_type>::type>
{
typedef typename Frame::base_frame_type base_frame_type;
};
and I have a bunch of trait checkers which use them, which also follow a similar idiom:
template <typename T>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename frame_traits<T>::base_frame_type, void>::value>::type {};
however, it turns out that has_base_frame_type has become useful to multiple concepts in my code, and I'd like to generalize it further so that I can pass the traits class as an additional parameter:
template <typename T, template<typename> class Traits = frame_traits>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename Traits<T>::base_frame_type, void>::value>::type {};
This doesn't work though, since templates with default arguments cannot be used as template template parameters.
I know I could work around the problem if I always use a traits class in the template instantiation (and modify the trait checker to accept it), namely
has_base_frame_type<frame_traits<MyClass>>::value
but I don't want to do that, because it would be all too easy to forget and pass in a non-trait class. In fact, that's how I originally had the code written until I forgot the trait one too many times and refactored it.
Is there someway I can modify my trait class idiom to work around the template template parameter problem?
Framework:
#include <type_traits>
template <typename...>
using void_t = void;
template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};
template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};
template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void, Operation, Args...>;
Detectors:
template <class Frame>
using frame_traits = typename Frame::base_frame_type;
template <class Frame>
using other_frame_traits = typename Frame::other_frame_type;
Trait with a default detector:
template <typename T, template <typename...> class Traits = frame_traits>
using has_frame_type = detect<Traits, T>;
Test:
struct A
{
using base_frame_type = void;
};
struct B
{
using other_frame_type = void;
};
int main()
{
static_assert(has_frame_type<A>{}, "!"); // default
static_assert(!has_frame_type<B>{}, "!"); // default
static_assert(!has_frame_type<A, other_frame_traits>{}, "!"); // non-default
static_assert(has_frame_type<B, other_frame_traits>{}, "!"); // non-default
}
DEMO

Variadic templates of Variadic templates class in c++

given Variadic templates data structure as below ( code from Eli Bendersky's website):
template <class... Ts> struct tuple {};
template <class T, class... Ts>
struct tuple<T, Ts...> : tuple<Ts...> {
tuple(T t, Ts... ts) : tuple<Ts...>(ts...), tail(t) {}
T tail;
};
we are able to define something like
tuple<double, uint64_t, const char*> t1(12.2, 42, "big");
Then how to define a template structure that could accept following signature?
Foo<tuple<int,double>, tuple<double,int,long> ..., tuple<std::string>> foo;
I think it would be like this:
template<tuple<class... Ts>... Tuples>
struct VariadicTuples {};
but it cannot compile. is that because the class in the Variadic templates cannot be Variadic templates? If so, How to make this work?
You simply cannot write that, syntactically. What would Ts hold in that situation? It would be different for each type in Tuples so it wouldn't really be usable.
What you can do instead is take advantage of Columbo's bool_pack trick:
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
To simply static_assert that all the Tuples are, in fact, tuples and then have VariadicTuples be a simple variadic class template:
template <typename > struct is_a_tuple : std::false_type { };
template <typename... T> struct is_a_tuple<tuple<T...>> : std::true_type { };
template <typename... Tuples>
struct VariadicTuples {
static_assert(all_true<is_a_tuple<Tuples>::value...>::value, "!");
};