Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

This legendary C++ delegate article

http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates

can be easily converted into C++11, without the need for fancy preprocessor magic in the original. I'd like to know if I got all the necessary C++11 nuances right. Suggestions?

#ifndef DELEGATE_HPP
# define DELEGATE_HPP

#include <utility>

template <typename T> class delegate;

template<class R, class ...A>
class delegate<R (A...)>
{
  typedef R (*stub_ptr_type)(void*, A...);

  delegate(void* o, stub_ptr_type m)
    : object_ptr(o),
      stub_ptr(m)
  {
  }

public:
  delegate() : stub_ptr(0) { }

  template <
    typename T,
    typename = typename std::enable_if<
      !std::is_same<delegate, typename std::remove_reference<T>::type>::value
    >::type
  >
  delegate(T&& f)
  {
    *this = from_functor(std::forward<T>(f));
  }

  delegate(delegate const&) = default;

  delegate& operator=(delegate const&) = default;

  template <R (*function_ptr)(A...)>
  static delegate from_function()
  {
    return delegate(0, function_stub<function_ptr>);
  }

  template <class C, R (C::*method_ptr)(A...)>
  static delegate from_method(C* object)
  {
    return delegate(object, method_stub<C, method_ptr>);
  }

  template <class C, R (C::*method_ptr)(A...)>
  static delegate from_const_method(C const* object)
  {
    return delegate(const_cast<C*>(object), const_method_stub<C, method_ptr>);
  }

  template <typename T>
  static delegate from_functor(T&& f)
  {
    static typename std::remove_reference<T>::type const l(
      std::forward<T>(f));

    return delegate(const_cast<void*>(static_cast<void const*>(&l)),
      functor_stub<T>);
  }

  void reset()
  {
    stub_ptr = 0;
  }

  void swap(delegate& other)
  {
    std::swap(object_ptr, other.object_ptr);
    std::swap(stub_ptr, other.stub_ptr);
  }

  bool operator==(delegate const& rhs) const
  {
    return (stub_ptr == rhs.stub_ptr)
      && (object_ptr == rhs.object_ptr);
  }

  bool operator<(delegate const& rhs) const
  {
    return (object_ptr < rhs.object_ptr)
      || (stub_ptr < rhs.stub_ptr);
  }

  explicit operator bool() const
  {
    return stub_ptr;
  }

  template <typename ...B>
  R operator()(B&&... args) const
  {
    return (*stub_ptr)(object_ptr, std::forward<B>(args)...);
  }

private:
  void* object_ptr;
  stub_ptr_type stub_ptr;

  template <R (*function_ptr)(A...)>
  static R function_stub(void*, A... args)
  {
    return (*function_ptr)(args...);
  }

  template <class C, R (C::*method_ptr)(A...)>
  static R method_stub(void* object_ptr, A... args)
  {
    return (static_cast<C*>(object_ptr)->*method_ptr)(args...);
  }

  template <class C, R (C::*method_ptr)(A...) const>
  static R const_method_stub(void* object_ptr, A... args)
  {
    return (static_cast<C const*>(object_ptr)->*method_ptr)(args...);
  }

  template <typename T>
  static R functor_stub(void* object_ptr, A... args)
  {
    return (*static_cast<T*>(object_ptr))(args...);
  }
};

#endif // DELEGATE_HPP

example use:

#include <iostream>

#include "delegate.hpp"

struct A
{
  void foo(int a)
  {
    std::cout << "method got: " << a << std::endl;
  }
};

void foo(int a)
{
  std::cout << "function got: " << a << std::endl;
}

int main(int argc, char* argv[])
{
  auto d1(delegate<void (int)>::from_function<foo>());

  A a;
  auto d2(delegate<void (int)>::from_method<A, &A::foo>(&a));

  d1(1);
  d2(2);

  int b(2);

  auto d3(delegate(
    [b](){std::cout << "hello world: " << b << std::endl;}));

  d3();

  return 0;
}
share|improve this question

1 Answer

up vote 3 down vote accepted

It looks by and large OK. Just some nitpicks

  • the default constructor could just be delegate() = default;, or otherwise initialize all members... or remove it entirely if it makes no sense.

  • the copy constructor should use initialization, not assignment.

  • swap should return void.

  • Assignment should pass-by-value:

    delegate& operator=(delegate rhs) { rhs.swap(*this); return *this; }
    
  • Invocation should use arbitrary arguments and forwarding:

    template <typename ...B>
    R operator()(B &&... b)
    {
        return (*stub_ptr)(object_ptr, std::forward<B>(b)...);
    }
    

    In fact, you should add enable_if with some variadic version of is_constructible<A, B>... to the operator so you don't create impossible overloads.

share|improve this answer
Thanks for your comments! I think the forwarding is still shaky, as the static function stubs still make copies of their arguments. Can this be fixed? – user1095108 Aug 17 '12 at 19:01
@user1095108: I thought the invocation was auto d = delegate::from_function<foo>(); -- there's no room for arguments, is there? – Kerrek SB Aug 17 '12 at 19:11
What about lambdas? Can we turn them into delegates? – Warren Seine Oct 30 '12 at 0:16
@WarrenSeine I've added lambda support. – user1095108 Feb 16 at 23:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.