Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::bind with pointer to a function object

Given a pointer to a function object:

std::shared_ptr<std::function<double(double)>> f;

is there a built-in construct in C++11 that allows be to use this pointer in a std::bind statement? For example:

std::function<double()> g = std::bind(built_in(f), 0.0);

What I am doing is to use variadic template as:

template<typename Ret, typename... Args>
std::function<Ret(Args...)> get_fn(const std::shared_ptr<std::function<Ret(Args...)>>& f)
{
    return [=](Args... args) -> Ret { return (*f)(args...); };
}

This allows me to write:

std::function<double()> g = std::bind(get_fn(f), 0.0);

Thanks in advance.

Edit: I should have been clearer. I need to use the instance of the function object inside the shared pointer for optimization purposes.

like image 269
Allan Avatar asked Oct 25 '12 17:10

Allan


2 Answers

If we had a Standard function object for the dereference operator (much like std::plus<T> is for adding T with T) then we could involve a nested bind expression, but that is not the case -- plus we'd need a call operator because the usual substitutions that take place during the evaluation of nested bind expressions don't apply to the first argument! Your solution introduces an additional std::function, so I'm proposing some alternatives that don't.

Write your own call and dereference operators:

struct call {
    template<typename Callable, typename... Args>
    auto operator()(Callable&& callable, Args&&... args) const
    // don't use std::result_of
    -> decltype( std::declval<Callable>()(std::declval<Args>()...) )
    { return std::forward<Callable>(callable)(std::forward<Args>(args)...); }
};

struct dereference {
    template<typename T>
    auto operator()(T&& t) const
    -> decltype( *std::declval<T>() )
    { return *std::forward<T>(t); }
};

template<typename IndirectCallable, typename... Args>
auto indirect_bind(IndirectCallable&& f, Args&&... args)
-> decltype( std::bind(call {}
                       , std::bind(dereference {}, std::declval<IndirectCallable>())
                       , std::declval<Args>()... ) )
{ return std::bind(call {}
                   , std::bind(dereference {}, std::forward<IndirectCallable>(f))
                   , std::forward<Args>()... ); }

You can then do auto g = indirect_bind(f, 0.0);. Here's a proof-of-concept that also shows how placeholders are appropriately dealt with.

I mention the above solution because functors like call and dereference are useful bricks -- I personally have them among my tools. My preferred solution however would be to write a polymorphic functor that does the indirection and call in one go:

template<typename Indirect>
struct indirect_callable_type {
    // Encapsulation left as an exercise
    Indirect f;

    template<typename... Args>
    auto operator()(Args&&... args)
    // don't use std::result_of
    -> decltype( std::declval<Indirect&>()(std::declval<Args>()...) )
    { return f(std::forward<Args>(args)...); }

    template<typename... Args>
    auto operator()(Args&&... args) const
    // don't use std::result_of
    -> decltype( std::declval<Indirect const&>()(std::declval<Args>()...) )
    { return f(std::forward<Args>(args)...); }

    // Lvalue and rvalue *this version also left as an exercise
};

template<typename T>
indirect_callable_type<typename std::decay<T>::type>
make_indirect_callable(T&& t)
{ return { std::forward<T>(t) }; }

which you can, in fact, use as auto g = std::bind(make_indirect_callable(f), 0.0);.

I should mention that unlike your solution, those requires writing some types out-of-line. This is an unfortunate situation due to the inherent limitations of lambda expressions. Should you wish to stick with what you currently have, I have a minor recommendation that inside the lambda you std::forward<Args>(args)... the parameters. This might prove useful if you ever deal with move-only types.

like image 86
Luc Danton Avatar answered Oct 18 '22 19:10

Luc Danton


There's a nice little hack you can use here, which is that INVOKE on a pointer to member function automatically indirects its object argument when passed a pointer or (even) a smart pointer (20.8.2p1 second bullet, definition of INVOKE):

#include <functional>
#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<std::function<double(double)>> f
        = std::make_shared<std::function<double(double)>>(
            [](double d) { return d + 10.0; });
    std::function<double()> g = std::bind(
        &std::function<double(double)>::operator(), f, 0.0);
    std::cout << g() << std::endl;
}

If you don't want to mention explicitly the type of f:

std::function<double()> g = std::bind(
    &std::remove_reference<decltype(*f)>::type::operator(), f, 0.0);
like image 24
ecatmur Avatar answered Oct 18 '22 19:10

ecatmur