How does std::invoke(C++1z) work? - c++

namespace detail {
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template <class Base, class T, class Derived>
inline auto INVOKE(T Base::*pmd, Derived&& ref) ->
decltype(std::forward<Derived>(ref).*pmd) {
return std::forward<Derived>(ref).*pmd;
}
template <class PMD, class Pointer>
inline auto INVOKE(PMD pmd, Pointer&& ptr) ->
decltype((*std::forward<Pointer>(ptr)).*pmd) {
return (*std::forward<Pointer>(ptr)).*pmd;
}
template <class Base, class T, class Derived, class... Args>
inline auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args) ->
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)) {
return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...);
}
template <class PMF, class Pointer, class... Args>
inline auto INVOKE(PMF pmf, Pointer&& ptr, Args&&... args) ->
decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)) {
return ((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...);
}
} // namespace detail
template< class F, class... ArgTypes>
decltype(auto) invoke(F&& f, ArgTypes&&... args) {
return detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
I saw the implementation above from here:
http://en.cppreference.com/w/cpp/utility/functional/invoke
Then I wonder how the compilers match the exact version required. Does SFINAE work on trailing return type?

does SFINAE work on tailing return type?
Yes. The trailing return type syntax doesn't enable any new functionality, it merely makes it easier to write some cases of return types that depend on parameter types.
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...) { ... }
could have equivalently been written as
template <class F, class... Args>
inline decltype(std::forward<F>(std::declval<F&>())(std::forward<Args>(std::declval<Args&>())...))
INVOKE(F&& f, Args&&... args) { ... }
for instance, and the same for all the others. That could be simplified, but even if you simplify it, the fact that the return type cannot use the same syntax as the return expression makes it hard to follow what's going on. Hence the new syntax.
The only time SFINAE doesn't work is for deduced return types. They also use auto, but it's not the auto keyword by itself that disables SFINAE.

Related

Determine the return type of a callable passed to a template

I have a simple wrapper template that allows free functions (e.g. open() close() etc) to passed as template parameters. The code is as follows:
template <auto fn, typename ReturnType=void>
struct func_wrapper {
template<typename... Args>
constexpr ReturnType operator()(Args&&... args) const {
if constexpr( std::is_same<ReturnType, void>::value) {
fn(std::forward<Args>(args)...);
} else {
return fn(std::forward<Args>(args)...);
}
}
};
This is used as follows:
void CloseFunc2(int a);
into OpenFunc2(const std::string&, int flags);
using FileWrapper2 = DevFileWrapper<func_wrapper<OpenFunc2, int>,
func_wrapper<CloseFunc2>>;
The code is working fine but I would like to remove the requirement to manually specify ReturnType when creating a func_wrapper.
I tried using std::result_of but that failed because fn is a non type template parameter, e.g.:
template<typename... Args>
constexpr auto operator()(Args&&... args) const
-> std::invoke_result<fn(std::forward<Args>(args)...)>::type {
if constexpr( std::is_same<ReturnType, void>::value) {
fn(std::forward<Args>(args)...);
} else {
return fn(std::forward<Args>(args)...);
}
}
the error is:
template-parameter-callable.cpp:48:71: error: template argument for
template type parameter must be a type
constexpr auto operator()(Args&&... args) const ->
std::invoke_result<fn(std::forward<Args>(args)...)>::type {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/
usr/include/c++/v1/type_traits:4009:17:
note: template parameter is declared here
template <class _Fn, class... _Args>
^
1 error generated.
How can I deduce the return type of calling fn at compile time?
template <auto fn>
struct func_wrapper {
template<typename... Args>
constexpr decltype(auto) operator()(Args&&... args) const {
return fn(std::forward<Args>(args)...);
}
};
have you tried this?
I think that works in c++17. Definitely in c++20.
The return type of a callable cannot be determined unless you also know the arguments you are calling it with.
I can extract the return type, but I don't think you need it.
template <auto fn>
struct func_wrapper {
template<typename... Args>
constexpr decltype(auto) operator()(Args&&... args) const {
using ReturnType = std::invoke_result_t<
decltype(fn),
Args&&...
>;
return fn(std::forward<Args>(args)...);
}
};
but as you can see we don't need it.
return f(x) when f returns void is (now) legal.

How to resolve constructor signature in factory function

I want to support one of two possible signatures of the constructor of the class T when creating its instance in the create(...) function below:
template <class Т, typename... Args>
T* create(Special* s, Args&&... args) {
T* t =
// If such a constructor exists, this:
new T(s, std::forward<Args>(args)...);
// Otherwise, this:
new T(std::forward<Args>(args)...);
}
I tried a few monstrous template constructions that did not cut it. The solution for resolving a member function involves SFINAE-failing a decltype of a member function, but this is not apparently possible with a constructor, as it does not have a signature type of its own.
Is this even possible in C++11, and is there any library support?
Just use std::is_constructible:
namespace detail
{
template<typename T, typename... Ts>
auto create(std::true_type, Special* s, Ts&&... args) {
return new T(s, std::forward<Ts>(args)...);
}
template<typename T, typename... Ts>
auto create(std::false_type, Special*, Ts&&... args) {
return new T(std::forward<Ts>(args)...);
}
}
template<class T, typename... Args>
T* create(Special* s, Args&&... args) {
using tag = is_constructible<T, Special*, Args...>;
return detail::create<T>(tag{}, s, std::forward<Args>(args)...);
}
live demo
template <class Т, typename... Args>
T* create_impl(std::true_type, Special* s, Args&&... args) {
return new T(s, std::forward<Args>(args)...);
}
template <class Т, typename... Args>
T* create_impl(std::false_type, Special*, Args&&... args) {
return new T(std::forward<Args>(args)...);
}
template <class Т, typename... Args>
T* create(Special* s, Args&&... args) {
T* t = create_impl<T>(std::is_constructible<T, Special*&, Args&&...>{},
s, std::forward<Args>(args)...);
// ...
return t;
}

C++11 Class template method specialization and variadic templates

I am trying to construct the following kind of templated method using GCC and C++11:
class Foo
{
private:
//Device handle
cudnnHandle_t handle_;
//Batch sizes
std::vector<int> batch_;
public:
Foo(cudnnHandle_t handle, std::vector<int> batch) : handle_(handle), batch_(batch) {}
template<typename T, typename... Args> T create(Args&&... args)
{
if(std::is_base_of<Element,T>::value)
return T(handle_,args...);
if(std::is_same<Block,T>::value)
return T(handle_,batch_,args...);
}
};
This however fails to compile due to the return statements. In case where T is Block, return T(handle_,args...) will fail and in case T is a Element base type return T(handle,batch_,args...) will fail.
I tried partial template specialization which is not allowed:
template<typename T, typename... Args> T create(Args&&... args)
{
return T(handle_,args...);
}
template<typename... Args> Block create<Block>(Args&&... args)
{
return Block(handle_,batch_,args...);
}
And i tried full template specialization which is apparently not supported for variadic templates by GCC:
template<typename T, typename... Args> T create(Args&&... args)
{
return T(handle_,args...);
}
template<> Block create<Block,int>(int n)
{
return Block(handle_,batch_,n);
}
So how would i go about creating a function like create?
Kind regards.
Try taking advantage of SFINAE like this
template <typename T, typename... Args,
typename std::enable_if<std::is_base_of<Element, T>::value>::type* = nullptr>
T create(Args&&... args) {
return T(handle_, std::forward<Args>(args)...);
}
template <typename T, typename... Args,
typename std::enable_if<std::is_same<Block, T>::value>::type* = nullptr>
T create(Args&&... args) {
return T(handle_, batch_, std::forward<Args>(args)...);
}

C++ weak functor with default return value

With the help of this community, I have the following template methods...
// void return
template<typename R, typename = typename std::enable_if<std::is_void<R>::value, R>::type,
typename F, typename T, typename... A>
static std::function<R()> Weak(F func, const std::shared_ptr<T>&& obj, A&&... args)
{
return std::bind(Wrapper<R>(), std::weak_ptr<T>(obj),
std::function<R()>(std::bind(func, obj.get(), args...)));
}
// non void return
template<typename R, R D = R(), typename = typename std::enable_if<std::is_void<R>::value == false, R>::type,
typename F, typename T, typename... A>
static std::function<R()> Weak(F func, const std::shared_ptr<T>&& obj, A&&... args)
{
return std::bind(Wrapper<R>(), std::weak_ptr<T>(obj),
std::function<R()>(std::bind(func, obj.get(), args...)), D);
}
...where Wrapper is a template class that tests the weak pointer and returns the specified default value if it is expired. Usage examples are...
(a) Bind::Weak<void>(func, obj, args...)
(b) Bind::Weak<bool>(func, obj, args...) // default bool is "false"
(c) Bind::Weak<bool, true>(func, obj, args...)
(b) Bind::Weak<int>(func, obj, args...) // default int is "0"
(c) Bind::Weak<int, 42>(func, obj, args...)
Is it be possible to support the following usage instead?
(a) Bind::Weak<void>(func, obj, args...)
(b) Bind::Weak<true>(func, obj, args...)
(c) Bind::Weak<42>(func, obj, args...)
[EDIT] The response from Oktalist below gave me the following idea...
// weak from shared - void return
template<typename F, typename O, typename... A>
static std::function<void()> Weak(F func, const std::shared_ptr<O>&& obj, A&&... args)
{
return std::bind(Wrapper<void>(), std::weak_ptr<O>(obj), std::function<void()>(std::bind(func, obj.get(), std::forward<A>(args)...)));
}
// weak from shared - non-void return
template<typename F, typename R = typename std::enable_if<std::is_void<F>::value == false, F>::type, typename O, typename... A>
static std::function<R()> Weak(R&& val, F func, const std::shared_ptr<O>&& obj, A&&... args)
{
return std::bind(Wrapper<R>(), std::weak_ptr<O>(obj), std::function<R()>(std::bind(func, obj.get(), std::forward<A>(args)...)), val);
}
...which gives this usage...
(a) Bind::Weak(func, obj, args...)
(b) Bind::Weak(true, func, obj, args...)
(c) Bind::Weak(42, func, obj, args...)
The only way I can think of which might help you tackle this problem is to overload your non-void version of Weak for each kind of non-type template argument you might support. In your example this is a bool and an int.
This is not your code as it's simplified but hopefully conveys the idea:
// accepts a type. using enable_if you'd do something like this
// to catch all the non-void types that don't have a default value.
template <class R, ...enable_if to only deal with non-void... >
R Get( R r )
{
return r;
}
// this for if the user wants to put an actual value as a parameter
// to be the default rather than just 'int'
template <int R>
decltype(R) Get( decltype(R) r = R )
{
return r;
}
// ... and do the previous one for each specific type which can have a default
Basically the issue is that specific values like '42' and 'false' won't bind to a 'typename'. So you need specialized templates of the function to accept these values and you can then get the type of that value when you need to using decltype(). This will remove the need for sometimes having one parameter (ie: Get< int >) and other times two (ie: Get< int, 42 > to have the default specified).
Another restriction which seems acceptable for your example but is probably not acceptable in the 'real world' is limitations on the template argument itself. You won't be able to do Weak< foo > where foo is an instance of a class.
I've spent some time on the same problem and came up with this:
#include <functional>
#include <memory>
template <typename F, typename T>
struct Invoker {
typedef std::weak_ptr<T> P;
Invoker(F func, P ptr)
: func_(func),
ptr_(ptr) {
}
template <typename... Args>
typename std::result_of<F(Args...)>::type operator()(Args&&... args) {
typedef typename std::result_of<F(Args...)>::type R;
if (ptr_.lock())
return func_(std::forward<Args>(args)...);
return R();
}
private:
F func_;
P ptr_;
};
template <typename F, typename T, typename... Args>
struct _Bind_helper {
typedef Invoker<decltype( std::bind(std::declval<F>(), std::declval<Args>()...) ), T> InvokerType;
};
template <typename F, typename T, typename... Args>
typename _Bind_helper<F, T, Args...>::InvokerType
weak_bind(std::weak_ptr<T> ptr, F func, Args&&... args) {
typedef typename _Bind_helper<F, T, Args...>::InvokerType R;
return R(std::bind(std::forward<F>(func), std::forward<Args>(args)...), ptr);
}
There are notable limitations on default result value, but it seems to work most of the time :)
Yes, you can deduce the return type of the callable type F using function traits.
Remove typename R from the template parameter list, move typename F to the beginning of the template parameter list, and replace all occurrences of R with typename function_traits<F>::return_type. We can use a template using declaration to help us:
template <typename F>
using Ret = typename function_traits<F>::return_type;
template <typename T>
using EnableIfVoid = typename std::enable_if<std::is_void<T>::value, T>::type;
template <typename T>
using EnableIfNotVoid = typename std::enable_if<! std::is_void<T>::value, T>::type;
// void return
template<typename F, typename = EnableIfVoid<Ret<F>>, typename T, typename... A>
static std::function<void()> Weak(F func, const std::shared_ptr<T>& obj, A&&... args)
{
return std::bind(Wrapper<void>(), std::weak_ptr<T>(obj),
std::function<void()>(std::bind(func, obj.get(),
std::forward<Args>(args)...)));
}
// non void return with explicit default
template<typename F, typename = EnableIfNotVoid<Ret<F>>, typename T, typename... A>
static std::function<Ret<F>()> Weak(Ret<F> d, F func, const std::shared_ptr<T>& obj, A&&... args)
{
return std::bind(Wrapper<Ret<F>>(), std::weak_ptr<T>(obj),
std::function<Ret<F>()>(std::bind(func, obj.get(),
std::forward<Args>(args)...)), d);
}
// non void return with implicit default
template<typename F, typename = EnableIfNotVoid<Ret<F>>, typename T, typename... A>
static std::function<Ret<F>()> Weak(F func, const std::shared_ptr<T>& obj, A&&... args)
{
return Weak(Ret<F>(), func, obj, std::forward<Args>(args)...);
}
I had to make your D parameter a function argument instead of a template parameter, otherwise you would've been forced to write Bind::Weak<decltype(f), 42>(f, obj).
(a) Bind::Weak(func, obj, args...)
(b) Bind::Weak(true, func, obj, args...)
(c) Bind::Weak(42, func, obj, args...)
[I feel you should be able to do this without having to call bind twice, but I don't know enough about what you are trying to do.]

Partial specialization of a template

Consider:
template <typename Function, typename ...Args>
auto wrapper(Function&& f, Args&&... args) -> decltype(f(args...)) {
//...
}
Is there a way to partially specialize the above template for all the cases where decltype(f(args...)) is a pointer?
EDIT:
I think it can be done with an template helper class which takes decltype(f(args...)) as template argument, and specialize the helper class. If you know better solutions let me know.
An SFINAE-based solution:
#include <type_traits>
template<
typename Functor
, typename... Args
, typename Result = decltype(std::declval<Functor&>()(std::declval<Args>()...))
, typename std::enable_if<
std::is_pointer<Result>::value
, int
>::type = 0
>
Result wrapper(Functor&& functor, Args&&... args)
{ /* ... */ }
template<
typename Functor
, typename... Args
, typename Result = decltype(std::declval<Functor&>()(std::declval<Args>()...))
, typename std::enable_if<
!std::is_pointer<Result>::value
, int
>::type = 0
>
Result wrapper(Functor&& functor, Args&&... args)
{ /* ... */ }
You can adapt the test (here, std::is_pointer<Result>) to your needs.
As you see the return type is not a template argument or part of the arguments, so you cannot overload nor specialize. Dispatching on a helper is your best option.
#include <type_traits>
template<typename Func, typename... Args>
void func_impl(Func&& f, Args&&... args, std::true_type)
-> decltype(func_impl(std::forward<Args>(args)...))
{ }
template<typename Func, typename... Args>
void func_impl(Func&& f, Args&&... args, std::false_type)
-> decltype(func_impl(std::forward<Args>(args)...))
{ }
template<typename Func, typename... Args>
auto func(Func&& f, Args&&... args)
-> decltype(func_impl(std::forward<Func>(f), std::forward<Args>(args)...))
{ return func_impl(std::forward<Func>(f), std::forward<Args>(args)...,
std::is_pointer<decltype(f(std::forward<Args>(args)...))>::type); }
It seems a little odd to me to take the function by rvalue reference though and you also omit the forwarding in your original example.
Another possible solution could be a template default argument and overload on that. But that wouldn't work well with the argument list.