I'm trying to implement compile-time configurable callback for sync/async call behaviour.
Here is the first approach to do that:
//emit type's
enum EEmitType
{
SYNC,
ASYNC,
};
//general
template<EEmitType et, typename... Args>
class callback_impl;
//implementation
template<EEmitType et, typename R, typename... Args>
class callback_impl<et, R(Args...)>
{ /*todo*/ };
//................................................................
//convert enum value to type or SFINAE
template <EEmitType et>
using callback_emit_type = std::integral_constant<EEmitType, et>;
//(for default SYNC)
template <typename T>
struct is_emit_type : std::false_type {};
//(for any other implementation)
template <EEmitType et>
struct is_emit_type<callback_emit_type<et>> : std::true_type {};
//................................................................
//metafunction
template <typename T>
using is_emit_type_t = typename is_emit_type<T>::type;
//................................................................
//for decl. like: callback<void()>
template <typename _unused, typename... Args>
struct construct_callback_impl
{
//alias on implementation
using type = callback_impl<SYNC, Args...>;
};
//for decl. like: callback<callback_emit_type<ASYNC>, void()>
template <typename EEmitType, typename... Args>
struct construct_callback_impl<typename
std::enable_if<is_emit_type_t<EEmitType>::value>::type, EEmitType, Args...>
{
//alias on implementation
using type = callback_impl<EEmitType::value, Args...>;
};
//................................................................
//user alias
template <typename... Args>
using callback = typename construct_callback_impl<Args...>::type;
Now USING:
callback<int(int)> ff_s; //<-- uses undefined class 'callback_impl<SYNC>'
callback<callback_emit_type<ASYNC>, int(int)> ff_a; //<--OK
Of course because first args is eating, and for success compilation it should be write down like:
callback<int(int), int(int)> ff_s
But of course it's not unacceptable.
OK, then I try extract EEmitType from Args...
//for decl. like: callback<void()>
template <typename... Args>
struct construct_callback_impl
{
//alias on implementation
using type = int; //temporary stub
};
//for decl. like: callback<callback_emit_type<ASYNC>, void()>
template <typename... Args>
struct construct_callback_impl<typename std::enable_if<is_emit_type_t< typename std::tuple_element_t<0, std::tuple<Args...> >::type >::value>::type, Args...>
{
//alias on implementation
using type = int; //temporary stub
};
BUT now I get the:
error C2338: tuple index out of bounds
note: see reference to class template instantiation 'std::tuple_element<0,std::tuple<>>' being compiled
Something like this should work
template <typename>
struct emit_type_value : std::integral_constant<EEmitType, SYNC> {};
template <EEmitType x>
struct emit_type_value<callback_emit_type<x>> : std::integral_constant<EEmitType, x> {};
//for decl. like: callback<void()>
template <typename Arg0, typename ... Args>
struct construct_callback_impl
{
//alias on implementation
using type = std::conditional_t<is_emit_type<Arg0>::value,
callback_impl<emit_type_value<Arg0>::value, Args...>,
callback_impl<SYNC, Arg0, Args...>>;
};
It would be somewhat terser if you just used false and true instead of SYNC and ASYNC.
Related
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;
I'm doing some metaprogramming and I have ran into the following problem:
I have a class that takes one template parameter T, T can be assumed to be a function with an arbitary signature. The class a member variable V, that should have the type std::tuple<> if T takes no arguments or the first argument is not a std::tuple. If the first argument is an std::tuple, V should instead have the same type as first argument.
Example:
void f() // Should resolve to std::tuple<>
void f(int) // Should resolve to std::tuple<>
void f(std::tuple<int, float>) // Should resolve to std::tuple<int, float>
void f(std::tuple<float>, int) // Should resolve to std::tuple<float>
I have been trying something similar to this, but with no success. As it fails when indexing the first arguement on the argument free function, without selecting any of the other alternatives in spite of those being available. I'm using MSVC 2019 16.8.4
#include <functional>
#include <concepts>
namespace detail
{
template<typename... ArgTs>
struct HasArgs : public std::conditional<(sizeof... (ArgTs) > 0), std::true_type, std::false_type>::type {};
}
//!
//! Provides argument function information
//! Based on: https://stackoverflow.com/a/9065203
//!
template<typename T>
class FunctionTraits;
template<typename R, typename... Args>
class FunctionTraits<R(Args...)>
{
public:
static const size_t arg_count = sizeof...(Args);
using HasArguments = detail::HasArgs<Args...>;
using ReturnType = R;
using ArgTypes = std::tuple<Args...>;
template <size_t i>
struct arg
{
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
namespace detail
{
template <typename T>
struct is_tuple : std::false_type {};
template <typename... Args>
struct is_tuple<std::tuple<Args...>>: std::true_type {};
}
template <typename T>
concept is_tuple = requires() { detail::is_tuple<T>::value; };
class TestMemberFunctions
{
public:
static int test_f1(std::tuple<int, float>, int)
{
return 0;
}
static int test_f2(int)
{
return 0;
}
static int test_f3()
{
return 0;
}
};
template <typename CreateT> requires (!FunctionTraits<CreateT>::HasArguments::value)
std::tuple<> TypeDeductionDummyFunction();
template <typename CreateT> requires FunctionTraits<CreateT>::HasArguments::value
auto TypeDeductionDummyFunction() -> std::conditional<is_tuple<typename FunctionTraits<CreateT>::template arg<0>::type>,
typename FunctionTraits<CreateT>::template arg<0>::type,
std::tuple<>>;
template <typename T>
class SampleClass
{
decltype(TypeDeductionDummyFunction<T>()) m_member;
};
SampleClass<decltype(TestMemberFunctions::test_f1)> c1;
SampleClass<decltype(TestMemberFunctions::test_f2)> c2;
SampleClass<decltype(TestMemberFunctions::test_f3)> c3;
Something along these lines, perhaps:
template <typename T> struct ExtractFirstTuple;
template <typename R>
struct ExtractFirstTuple<R()> {
using type = std::tuple<>;
};
template <typename R, typename... Ts, typename... Args>
struct ExtractFirstTuple<R(std::tuple<Ts...>, Args...)> {
using type = std::tuple<Ts...>;
};
template <typename R, typename First, typename... Args>
struct ExtractFirstTuple<R(First, Args...)> {
using type = std::tuple<>;
};
Demo
An attempt to build what you want from more primitive operations.
template<typename T, std::size_t N>
struct FunctionArgument {
static constexpr bool exists = false;
};
template<typename R, typename A0, typename... Args>
struct FunctionArgument<R(A0, Args...), 0>{
using type=A0;
static constexpr bool exists = true;
};
template<typename R, typename A0, typename... Args, std::size_t N>
struct FunctionArgument<R(A0, Args...), N>:
FunctionArgument<R(Args...), N-1>
{};
template<class Sig, std::size_t N>
using FuncArg_type = typename FunctionArgument<Sig, N>::type;
template<class Sig, std::size_t N>
constexpr bool FuncArg_exists = FunctionArgument<Sig, N>::exists;
template<class Sig, class Otherwise>
using FirstArgIfExists =
typename std::conditional_t<
FuncArg_exists<Sig,0>,
FunctionArgument<Sig, 0>,
std::type_identity<Otherwise>
>::type;
template<class T, class Otherwise>
struct TypeIfTuple {
using type=Otherwise;
};
template<class...Ts, class Otherwise>
struct TypeIfTuple<std::tuple<Ts...>, Otherwise> {
using type=std::tuple<Ts...>;
};
template<class T, class Otherwise>
using TypeIfTuple_t = typename TypeIfTuple<T,Otherwise>::type;
template<class Sig>
using TheTypeYouWant = TypeIfTuple_t<
FirstArgIfExists<Sig, std::tuple<>>,
std::tuple<>
>;
I have the following code:
template <template <class...> class Temp, class Specialization>
struct IsSpecialization : std::false_type {};
template <template <typename...> class Temp1,
template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
: std::is_same<Temp1<Ts...>, Temp2<Ts...>> {};
struct ExprKindMerge {};
struct ExprKindSequence {};
template <class Tag, class... Args>
struct Expr {
std::tuple<Args...> tup;
constexpr std::tuple<Args...> toStdTuple() const {
return this->tup;
}
constexpr std::size_t size() const noexcept {
return std::tuple_size<decltype(tup)>{};
}
};
template <class...Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;
template <class...Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;
And this function, which sometimes receives a SequenceExpr<Something> as the template parameter:
template <class FullExpr>
auto f(FullExpr expr) {
///**************THIS RETURNS FALSE I EXPECT TRUE
///Type of full expr is Expr<ExprKindSequence, ...> which is
/// the expansion of SequenceExpr<...>
if constexpr (IsSpecialization<SequenceExpr, FullExpr>::value)
//...
}
I want to be able to detect if FullExpr is a specialization of SequenceExpr but it fails for some unknown reason.
Why it fails is easy. For a SequenceExpr<ExtraArgs...>, Temp2 is deduced to be Expr and Ts... is deduced to be ExprKindSequence, ExtraArgs.... Then Temp1<Ts...> is Expr<ExprKindSequence, ExprKindSequence, ExtraArgs...> and is obviously not the same as Temp2<Ts...>.
I know of no fully generic way to do this. After all, such a hypothetical template would presumably need to return true for IsSpecialization<std::remove_reference_t, int>...
If we limit it to alias templates that use their parameters in deducible contexts (which is the case in your example), then one possible way is to ask the question "Can I deduce the Ts... in Temp<Ts...> from Specialization?":
namespace detail {
template<class> class type {};
template<template<class...> class Temp, class...Ts>
void try_deduce(type<Temp<Ts...>>);
}
template <template <class...> class, class, class = void>
struct IsSpecialization : std::false_type {};
template <template <typename...> class Temp, class Specialization>
struct IsSpecialization<Temp, Specialization,
decltype(detail::try_deduce<Temp>(detail::type<Specialization>()))>
: std::true_type {};
static_assert(IsSpecialization<SequenceExpr, SequenceExpr<int>>()());
static_assert(IsSpecialization<Expr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<MergeExpr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<SequenceExpr, MergeExpr<int>>()());
If your interested only in detecting specializations of SequenceExpr, the best I can imagine is to add a specialization of IsSpecialization defined as follows
template <typename... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
: std::true_type
{ };
Obviously this isn't a general solution that I don't think it's possible.
Take in count that if you call f with a (by example) Expr<ExprKindSequence, int, long> value
f(Expr<ExprKindSequence, int, long>{}); // result true !!!
the specialization of IsSpecialization above match and you get true; I don't know if is what do you want.
The following is a full working example
#include <tuple>
#include <iostream>
#include <type_traits>
template <template <typename...> typename Temp, typename Specialization>
struct IsSpecialization : std::false_type
{ };
template <template <typename...> class Temp1,
template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
: std::is_same<Temp1<Ts...>, Temp2<Ts...>>
{ };
struct ExprKindMerge {};
struct ExprKindSequence {};
template <typename Tag, typename... Args>
struct Expr
{
std::tuple<Args...> tup;
constexpr std::tuple<Args...> toStdTuple () const
{ return this->tup; }
constexpr std::size_t size () const noexcept
{ return std::tuple_size<decltype(tup)>{}; }
};
template <typename ... Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;
template <typename ... Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;
template <typename ... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
: std::true_type
{ };
template <class FE>
auto f (FE expr)
{ std::cout << IsSpecialization<SequenceExpr, FE>::value << std::endl; }
int main ()
{
f(SequenceExpr<int, long>{}); // print 1
f(Expr<ExprKindSequence, int, long>{}); // print 1 (?)
f(Expr<int, long>{}); // print 0
f(int{}); // print 0
}
Please consider the following code snippet:
template<typename T, class Tuple>
class vector
{
using size_type = typename Tuple::size_type;
template<typename... Elements,
typename = decltype(std::declval<Tuple>().reserve(size_type()))>
typename = decltype(std::declval<Tuple>().push_back(T())),
vector(Elements&&... elements)
{ /* ... */ }
};
I want to define a nested struct supports_reserve_push_back which is derived from std::true_type whenever the constructor above would be enabled (and which is derived from std::false_type in the other case).
How can I do this?
I've modified the code to make it build. And implemented the trait you requested, to the best of my understanding.
#include <iostream>
#include <type_traits>
#include <vector>
#include <map>
namespace example {
template<typename...>
using void_t = void;
template<typename T, class Tuple>
struct vector {
using size_type = typename Tuple::size_type;
using tuple_type = Tuple;
using elem_type = T;
template<typename... Elements>
vector(Elements&&... elements)
{ /* ... */ }
};
template <class T, typename = void>
struct supports_reserve_push_back : std::false_type {};
template <class Vec>
struct supports_reserve_push_back<Vec, void_t<
decltype(std::declval<typename Vec::tuple_type>().reserve(typename Vec::size_type())),
decltype(std::declval<typename Vec::tuple_type>().push_back(typename Vec::elem_type())) >
>
: std::true_type {};
}
int main() {
std::cout
<< example::supports_reserve_push_back<example::vector<int, std::vector<int>>>::value
<< '\n'
<< example::supports_reserve_push_back<example::vector<int, std::map<int, int>>>::value;
return 0;
}
A few thing to note:
The way you wrote the c'tor originally caused a hard error when instantiating the class in the negative case. That's why I removed the chcck from the c'tor.
I'd suggest you define the type traits first, and use them to enable your c'tors.
namespace details{
template<template<class...>class Z,class,class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z,class...Ts>
struct can_apply<Z,std::void_t<Z<Ts...>,Ts...>:
std::true_type{};
}
template<template<class...>class Z,class... Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
Wrap up your decltypes into template usings and do some && and done.
There is also a std experimental similar to above.
template<class T, class U>
using push_back_r = decltype(std::declval<T>().push_back(std::declval<U>()));
template<class T>
using reserve_r = decltype(std::declval<T>().reserve(1));
template<class T, class U>
constexpr can_apply<push_back_r,T,U> has_push_back={};
template<class T>
constexpr can_apply<reserve_r,T> has_reserve={};
template<bool b>using bool_t=std::integral_constant<bool,b>;
template<class T,class U>
constexpr bool_t<has_push_back<T,U>&&has_reserve<T>>
efficiently_fillable_with = {};
Then efficiently_fillable_with<T,U> is true type iff you can reserve space with T and then push Us into it. The r/l value category of T and U is preserved: if you want to know about filling an non-cinstant lvalue of T with rvalue Us:
efficiently_fillable_with<T&,U>
If you want to fill with U const& instead of rvalues, pass U const&.
#include <type_traits>
#include <utility>
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_t<>, Operation, Args...>;
template <typename T, typename Sz>
using has_reserve = decltype(std::declval<T>().reserve(std::declval<Sz>()));
template <typename T, typename U>
using has_push_back = decltype(std::declval<T>().push_back(std::declval<U>()));
template <typename Tuple, typename T, typename size_type>
constexpr bool supports_reserve_push_back = detect<has_reserve, Tuple, size_type>{} && detect<has_push_back, Tuple, T>{};
Test:
template <typename T, class Tuple>
class vector
{
public:
using size_type = typename Tuple::size_type;
template <typename... Elements, typename U = Tuple,
std::enable_if_t<supports_reserve_push_back<U&, T, size_type>, int> = 0>
vector(Elements&&... elements)
{
}
template <typename... Elements, typename U = Tuple,
std::enable_if_t<!supports_reserve_push_back<U&, T, size_type>, int> = 0>
vector(Elements&&... elements)
{
}
};
DEMO
I would try the following approach:
Have your vector template class inherit from a superclass, like this:
template<typename T, class Tuple> class vector
: public supports_reserve_push_back_impl<
vector_has_default_constructor<T, Tuple>::value() > {
// ...
}
Now, define a vector_has_default_constructor template class that takes the same template parameters:
template<typename T, class Tuple> class vector_has_default_constructor {
public:
// ...
};
In vector_has_default_constructor:
Define a constexpr bool value() method with the same exact signature as the vector's constructor. This constexpr method returns true.
Define an overload constexpr bool value() with a ... signature, which should have lower priority in the overload resolution. This constexpr returns false.
Now, this situation is reduced to defining two trivial specializations, supports_reserve_push_back_impl<true> and supports_reserve_push_back_impl<false>.
supports_reserve_push_back_impl<true> contains your desired supports_reserve_push_back value, and is inherited by your vector.
supports_reserve_push_back_impl<false> is empty.
Is there a way to automatically select between multiple non-template functions based on a template parameter?
Example:
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
// possible map between types and functions?
return bind(aggregate, typeConvert[T])(); ??
// or
return aggregate.APPROPRIATE_TYPE_CONVERSION();
}
The solution would be nice to throw a compiler error if there is no good conversion available, i.e.
get<double>(aggregate); // compile error
I do not want to use template specialization, i.e
template<>
int get(Aggregate& aggregate)
{
return aggregate.asInt();
}
because it leads to code duplication when your get() function has more then one line of code
The pedestrian way is to define each possible option separately:
template <typename T> T get(Aggregate &); // leave undefined
template <> uint32_t get(Aggregate & a) { return a.asInt(); }
// ...
Absent any more systematic structure that encodes which function serves which conversion, I think this is the best you can do. It may be worth redefining Aggregate, though, to be more introspectible.
You may do something like (require C++11) : (https://ideone.com/UXrQFm)
template <typename T, typename... Ts> struct get_index;
template <typename T, typename... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_index<T, Tail, Ts...> :
std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
template <typename T, typename Tuple> struct get_index_in_tuple;
template <typename T, typename ... Ts>
struct get_index_in_tuple<T, std::tuple<Ts...>> : get_index<T, Ts...> {};
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
using types = std::tuple<uint32_t, std::string>;
auto funcs = std::make_tuple(&Aggregate::asInt, &Aggregate::asString);
return (aggregate.* (std::get<get_index_in_tuple<T, types>::value>(funcs)))();
}