Consider the following code:
template <class F, class... Args, class = std::void_t<>>
struct is_invokable
: std::false_type {};
template <class F, class... Args>
struct is_invokable<F, Args..., std::void_t<std::invoke_result_t<F, Args...>>>
: std::true_type {};
The goal is to have a trait that is able to tell whether a callable of type F is invokable with arguments of type Args....
However, it fails to compile because:
error: parameter pack 'Args' must be at the end of the template parameter list
What is the (elegant) way to do this in C++17?
namespace details {
template <class F, class, class... Args>
struct is_invokable : std::false_type {};
template <class F, class... Args>
struct is_invokable<F, std::void_t<std::invoke_result_t<F, Args...>>, Args...>
: std::true_type {};
}
template <class F, class... Args>
using is_invokable=typename ::details::is_invokable<F, void, Args...>::type;
I propose an helper struct (is_invokable_h) and the use of std::tuple to wrap the Args...
Something like
#include <type_traits>
#include <utility>
template <typename, typename, typename = void>
struct is_invokable_h : std::false_type
{};
template <typename F, typename ... Args>
struct is_invokable_h<F, std::tuple<Args...>,
std::void_t<std::invoke_result_t<F, Args...>>>
: std::true_type
{};
template <typename F, typename ... Args>
struct is_invokable : is_invokable_h<F, std::tuple<Args...>>
{};
int foo (int)
{ return 0; }
int main()
{
static_assert( true == is_invokable<decltype(foo), int>{} );
static_assert( false == is_invokable<decltype(foo), int, int>{} );
}
Related
I am trying to create an IoC Container in C++ that resolves dependencies automatically.
For that I created a function with two variadic parameter packs that is declared like this:
template <class T, typename ... TDependencies, typename... TArgs>
void Register(std::function<std::shared_ptr<T> (std::shared_ptr<TDependencies> ...,
TArgs ...)> && pFactory)
Apparently, it seems the compiler is unable to match this when supplied with
Register<Foo, Bar>(std::function<std::shared_ptr<Foo>(std::shared_ptr<Bar>)>(
[](std::shared_ptr<Bar> bar){return std::make_shared<Foo>(bar);}));
The compile errors say
note: candidate: 'void Container::Register(std::function<std::shared_ptr<_Tp>
(std::shared_ptr<TDependencies>..., TArgs ...)>&&)
[with T = Foo; TDependencies = {Bar}; TArgs = {std::shared_ptr<Bar>}]'
Apparently, it matches std::shared_ptr<Bar> twice. How can I get the compiler not to match the shared_ptr in TArgs too?
Rather than trying to deduce TDependencies directly from the pFactory parameter type, I'd write a type trait to get the dependencies from the whole parameter pack instead. With boost::mp11:
template <class>
struct is_shared_ptr : std::false_type {};
template <class T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
namespace mp11 = ::boost::mp11;
template <class... Ts>
using register_traits = mp11::mp_partition<mp11::mp_list<Ts...>, is_shared_ptr>;
template <class T, class F, class... TDependencies, class... TArgs>
void RegisterImpl(F && pFactory,
mp11::mp_list<
mp11::mp_list<std::shared_ptr<TDependencies>...>,
mp11::mp_list<TArgs...>>);
template <class T, class... Ts>
void Register(std::function<std::shared_ptr<T> (Ts...)> && pFactory)
{
return RegisterImpl<T>(
std::forward<std::function<std::shared_ptr<T> (Ts...)>>(pFactory),
register_traits<Ts...>{});
}
And to call it:
Register(std::function{[] (std::shared_ptr<Bar> bar) {
return std::make_shared<Foo>(bar);
}});
Try it on godbolt.org.
If boost::mp11 is not an option, here's how you can implement your own partition template metafunction:
template <class...>
struct list {};
namespace detail {
template <class L, template <class...> class P, class T, class F, class = void>
struct partition;
template <class Next, class... Ls,
template <class...> class P, class T, class... Fs>
struct partition<list<Next, Ls...>, P, T, list<Fs...>,
std::enable_if_t<!P<Next>::value>> :
partition<list<Ls...>, P, T, list<Fs..., Next>> {};
template <class Next, class... Ls,
template <class...> class P, class... Ts, class F>
struct partition<list<Next, Ls...>, P, list<Ts...>, F,
std::enable_if_t<P<Next>::value>> :
partition<list<Ls...>, P, list<Ts..., Next>, F> {};
template <template <class...> class P, class T, class F>
struct partition<list<>, P, T, F> { using type = list<T, F>; };
} // namespace detail
template <class L, template <class...> class P>
using partition = typename detail::partition<L, P, list<>, list<>>::type;
template <class... Ts>
using register_traits = partition<list<Ts...>, is_shared_ptr>;
template <class T, class F, class... TDependencies, class... TArgs>
void RegisterImpl(F && pFactory,
list<list<std::shared_ptr<TDependencies>...>, list<TArgs...>>);
Try it on godbolt.org.
The rest of the code will remain the same.
I am actually thinking of something similar to the '*' operator in python like this:
args = [1,2,4]
f(*args)
Is there a similar solution in C++?
What I can come up with is as follows:
template <size_t num_args, typename FuncType>
struct unpack_caller;
template <typename FuncType>
struct unpack_caller<3>
{
void operator () (FuncType &f, std::vector<int> &args){
f(args[0], args[1], args[3])
}
};
Above I assume only int argument type.
The problem is that I feel it is a hassle to write all the specializations of unpack_caller for different value of num_args.
Any good solution to this? Thanks.
You can use a pack of indices:
template <size_t num_args>
struct unpack_caller
{
private:
template <typename FuncType, size_t... I>
void call(FuncType &f, std::vector<int> &args, indices<I...>){
f(args[I]...);
}
public:
template <typename FuncType>
void operator () (FuncType &f, std::vector<int> &args){
assert(args.size() == num_args); // just to be sure
call(f, args, BuildIndices<num_args>{});
}
};
There's no way to remove the need to specify the size in the template though, because the size of a vector is a runtime construct, and we need the size at compile-time.
Update to #Fernandes's answer.
Yes, there does be a way to remove the need to specifying num_args in template parameter. This is because num_args is determined by the function signature, not the vector. What should be checked at run-time is the size of the vector and the arity of the function.
See the following example.
#include <iostream>
#include <utility>
#include <vector>
#include <cassert>
namespace util {
template <typename ReturnType, typename... Args>
struct function_traits_defs {
static constexpr size_t arity = sizeof...(Args);
using result_type = ReturnType;
template <size_t i>
struct arg {
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename T>
struct function_traits_impl;
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename T, typename V = void>
struct function_traits
: function_traits_impl<T> {};
template <typename T>
struct function_traits<T, decltype((void)&T::operator())>
: function_traits_impl<decltype(&T::operator())> {};
template <size_t... Indices>
struct indices {
using next = indices<Indices..., sizeof...(Indices)>;
};
template <size_t N>
struct build_indices {
using type = typename build_indices<N - 1>::type::next;
};
template <>
struct build_indices<0> {
using type = indices<>;
};
template <size_t N>
using BuildIndices = typename build_indices<N>::type;
namespace details {
template <typename FuncType,
typename VecType,
size_t... I,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT do_call(FuncType& func,
VecType& args,
indices<I...> ) {
assert(args.size() >= Traits::arity);
return func(args[I]...);
}
} // namespace details
template <typename FuncType,
typename VecType,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT unpack_caller(FuncType& func,
VecType& args) {
return details::do_call(func, args, BuildIndices<Traits::arity>());
}
} // namespace util
int func(int a, int b, int c) {
return a + b + c;
}
int main() {
std::vector<int> args = {1, 2, 3};
int j = util::unpack_caller(func, args);
std::cout << j << std::endl;
return 0;
}
If you use c++14 there is update to #r-martinho-fernandes response using index-sequence:
template <size_t num_args>
struct unpack_caller
{
private:
template <typename FuncType, size_t... I>
void call(FuncType &f, std::vector<int> &args, std::index_sequence<I...>){
f(args[I]...);
}
public:
template <typename FuncType>
void operator () (FuncType &f, std::vector<int> &args){
assert(args.size() == num_args); // just to be sure
call(f, args, std::make_index_sequence<num_args>{});
}
};
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
}
In my program I have following code:
template<typename... Args>
class Outer {
template<typename Arg>
class Inner {
//some code
}
}
How can I restrict Arg to be one of the types in Args template pack?
#include <type_traits>
template <typename T, typename... Ts>
struct contains : std::false_type {};
template <typename T, typename F, typename... Ts>
struct contains<T, F, Ts...> : contains<T, Ts...> {};
template <typename T, typename... Ts>
struct contains<T, T, Ts...> : std::true_type {};
template <typename... Args>
struct Outer
{
template <typename Arg>
struct Inner
{
static_assert(contains<Arg, Args...>::value, "Arg must be from Args");
};
};
DEMO
In this code, I'm trying to generalize Test from using Arg to using Args.... The problem is the default template argument. What I have below compiles, except when I uncomment the commented-out line in main():
#include <iostream>
#include <type_traits>
struct A {
void foo(int) const {}
void foo(int, bool, char) const {}
};
template <typename...> struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename T, typename Arg, typename = void_t<T>>
struct Test : std::false_type {};
template <typename T, typename Arg>
struct Test<T, Arg, void_t<decltype(std::declval<T&>().foo(std::declval<Arg>()))>> :
std::true_type {};
// Trying to generalize Test with Args... instead of Arg
template <typename T, typename, typename... Args> struct Check;
template <typename T, typename... Args>
struct Check<T, void_t<T>, Args...> : std::false_type {};
template <typename T, typename... Args>
struct Check<T, void_t<decltype(std::declval<T&>().foo(std::declval<Args>()...))>, Args...>
: std::true_type {};
template <typename T, typename... Args>
using CheckArgs = Check<T, void_t<T>, Args...>;
int main() {
std::cout << std::boolalpha << Test<A, int>::value << '\n'; // true
// std::cout << CheckArgs<A, int, bool, char>::value << '\n'; // ambiguous
}
The last line in main() is ambiguous. Firstly, why is it ambiguous, while the first line in main() is not? And secondly, how to fix the code so that the last line in main will compile (it is supposed to evaluate to true since int, bool, char are arguments of A::foo)?
You want
template <typename T, typename, typename... Args>
struct Check : std::false_type {};
template <typename T, typename... Args>
struct Check<T, void_t<decltype(std::declval<T&>().foo(std::declval<Args>()...))>, Args...>
: std::true_type {};
You want the primary template to provide the default case - which is false, and the partial specialization to supply the true case. When you write two partial specializations, both are viable, and there's no ordering between the two, so it ends up being ambiguous
And this is just reimplementing a more constrained version of std::experimental::is_detected.