Question

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...)
Was it helpful?

Solution 3

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.]

OTHER TIPS

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 :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top