开发者

Function decorators for std::bind in C++0x

开发者 https://www.devze.com 2023-03-24 23:21 出处:网络
I need a function wrapper for std::bind that will be called before the function it\'s wrapper, passing the arguments along to the wrapped functions.

I need a function wrapper for std::bind that will be called before the function it's wrapper, passing the arguments along to the wrapped functions.

std::function<void (int)> foo = postbind<int>(service, handle);

I've so far got down to that

std::function<void (int)> foo = postbind(service, handle);

How can I remove that template parameter? It seems to come down to the type deduction from the object generation function (postbind) not being intelligent enough.

#include <functional>

template<typename T>
void foo(std::function<void (T)> func)
{
}

void handler(int x)
{
}

int main()
{
    foo(handler);
    return 0;
}

Says error: no matching function for call to 'foo(void (&)(int))'

Yet, the code sample:

template<typename T>
void foo(T t)
{
}

int main()
{
    foo(99);
    return 0;
}

This works. Any ideas how to make this work? I need to be able to pass std::bind to it and have the result cast successfully to std::function.

How can I remove the template parameters? Thanks.


Q. What is service and this class meant to do?

A. Encapsulate a function wrapper that boost::asio::io_service->posts out of the current thread.


Full sourcecode:

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

class io_service
{
};

typedef std::shared_ptr<io_service> service_ptr;

template <typename Arg1>
class postbind_impl_1
{
public:
    typedef std::function<void (Arg1)> function;

    postbind_impl_1(service_ptr service, function memfunc)
      : service_(service), memfunc_(memfunc)
    {
    }

    void operator()(Arg1 arg1)
    {
        // do stuff u开发者_JAVA百科sing io_service
        memfunc_(arg1);
    }
private:
    service_ptr service_;
    function memfunc_;
};

template <typename Arg1>
postbind_impl_1<Arg1> postbind(service_ptr service, 
        typename postbind_impl_1<Arg1>::function handle)
{
    return postbind_impl_1<Arg1>(service, handle);
}

// ----------------

void handle(int x)
{
    std::cout << x << "\n";
}

int main()
{
    service_ptr service;
    std::function<void (int)> foo = postbind(service, handle);
    foo(110);
    return 0;
}


AFAICT argument types of a bind expression are not deducible, so what you want is pretty much impossible.


How do you expect the compiler to know to use std::function? In this code:

#include <functional>

template<typename T>
void foo(T func)
{
}

void handler(int x)
{
}

int main()
{
    foo(handler);
    return 0;
}

T is NOT std::function<void (int)>. It's void (&)(int) (like the error message said), a reference to a function, not a functor object.

Deduction of the argument type of the passed function should work, try:

#include <functional>

template<typename T>
std::function<void (T)> foo(void (*func)(T))
{
}

void handler(int x)
{
}

int main()
{
    foo(handler);
    return 0;
}

Demo: http://ideone.com/NJCMS

If you need to extract argument types from either std::function or a plain function pointer, you'll need a helper structure:

template<typename>
struct getarg {};

template<typename TArg>
struct getarg<std::function<void (TArg)>> { typedef TArg type; };

template<typename TArg>
struct getarg<void (*)(TArg)> { typedef TArg type; };

template<typename TArg>
struct getarg<void (&)(TArg)> { typedef TArg type; };

template<typename T>
std::function<void (typename getarg<T>::type)> foo(T func)
{
}

void handler(int x)
{
}

int main()
{
    foo(handler);
    return 0;
}

Demo: http://ideone.com/jIzl7

With C++0x, you can also match anything that implicitly converts to std::function, including return values from std::bind and lambdas: http://ideone.com/6pbCC


I'm not entirely sure what you're trying to achieve, but here's a naive wrapper that stores a list of actions:

template <typename R, typename A>
struct wrap
{
  typedef std::function<R(A)> func;

  wrap(func f_) : f(f_) { }

  void prebind(func g) { prebound.push_back(g); }

  R operator()(A arg)
  {
    for (auto it = prebound.cbegin(); it != prebound.cend(); ++it)
    {
      func g = *it;
      g(arg);
    }

    f(arg);
  }
private:
  std::vector<func> prebound;
  func f;
};

wrap<void, int> make_wrap(std::function<void(int)> f)
{
  return wrap<void, int>(f);
}

Usage:

auto foowrap = make_wrap(foo);

foowrap.prebind(std::function<void(int)>(action1);
foowrap.prebind(std::function<void(int)>(action2);

foowrap(12);  // calls action1(12), action2(12), foo(12)


To all the naysayers who said this wasn't possible :)

/*
 * Defines a function decorator ala Python
 *
 *   void foo(int x, int y);
 *   function<void ()> wrapper(function<void (int)> f);
 *
 *   auto f = decorator(wrapper, bind(foo, 110, _1));
 *   f();
 */

#include <functional>

template <typename Wrapper, typename Functor>
struct decorator_dispatch
{
    Wrapper wrapper;
    Functor functor;

    template <typename... Args>
    auto operator()(Args&&... args)
        -> decltype(wrapper(functor)(std::forward<Args>(args)...))
    {
        return wrapper(functor)(std::forward<Args>(args)...);
    }
};

template <typename Wrapper, typename Functor>
decorator_dispatch<
    Wrapper,
    typename std::decay<Functor>::type
>
decorator(Wrapper&& wrapper, Functor&& functor)
{
    return {wrapper, functor};
}
0

精彩评论

暂无评论...
验证码 换一张
取 消