What is a C++ delegate?
Asked Answered
S

6

178

What is the general idea of a delegate in C++? What are they, how are they used and what are they used for?

I'd like to first learn about them in a 'black box' way, but a bit of information on the guts of these things would be great too.

This is not C++ at its purest or cleanest, but I notice that the codebase where I work has them in abundance. I'm hoping to understand them enough, so I can just use them and not have to delve into the horrible nested template awfulness.

These two The Code Project articles explain what I mean but not particularly succinctly:

Strickman answered 5/3, 2012 at 14:18 Comment(7)
Are you talking about managed C++ under .NET?Stpeter
Did you look at the Wikipedia page for Delegation?Facetious
delegate is not a common name in c++ parlance. You should add some information to the question to include the context in which you have read it. Note that while the pattern might be common, the answers may differ if you talk about delegate in general, or in the context of C++CLI or any other library that has a particular implementation of delegate.Quathlamba
@SirYakalot - you should accept one of the answers! : )Incorrigible
@user1158692 I usually wait until I need to do a big rebuild and then do a backlog of selecting correct answers.Strickman
wait 2 years ?;)Fregoso
Still waiting...Incorrigible
T
208

You have an incredible number of choices to achieve delegates in C++. Here are the ones that came to my mind.


Option 1 : functors:

A function object may be created by implementing operator()

struct Functor
{
     // Normal class/struct members

     int operator()(double d) // Arbitrary return types and parameter list
     {
          return (int) d + 1;
     }
};

// Use:
Functor f;
int i = f(3.14);

Option 2: lambda expressions (C++11 only)

// Syntax is roughly: [capture](parameter list) -> return type {block}
// Some shortcuts exist
auto func = [](int i) -> double { return 2*i/1.15; };
double d = func(1);

Option 3: function pointers

int f(double d) { ... }
typedef int (*MyFuncT) (double d);
MyFuncT fp = &f;
int a = fp(3.14);

Option 4: pointer to member functions (fastest solution)

See Fast C++ Delegate (on The Code Project).

struct DelegateList
{
     int f1(double d) { }
     int f2(double d) { }
};

typedef int (DelegateList::* DelegateType)(double d);

DelegateType d = &DelegateList::f1;
DelegateList list;
int a = (list.*d)(3.14);

Option 5: std::function

(or boost::function if your standard library doesn't support it). It is slower, but it is the most flexible.

#include <functional>
std::function<int(double)> f = [can be set to about anything in this answer]
// Usually more useful as a parameter to another functions

Option 6: binding (using std::bind)

Allows setting some parameters in advance, convenient to call a member function for instance.

struct MyClass
{
    int DoStuff(double d); // actually a DoStuff(MyClass* this, double d)
};

std::function<int(double d)> f = std::bind(&MyClass::DoStuff, this, std::placeholders::_1);
// auto f = std::bind(...); in C++11

Option 7: templates

Accept anything as long as it matches the argument list.

template <class FunctionT>
int DoSomething(FunctionT func)
{
    return func(3.14);
}
Treadwell answered 5/3, 2012 at 14:40 Comment(13)
Great list, +1. However, only two really count as delegates here - capturing lambdas and the object returned from std::bind, and both really do the same thing, except lambdas aren't polymorphic in the sense that they can accept different argument types.Piranesi
@J.N: Why do you recommend not to use function pointers but seem to be okay with using the member methods pointers ? They are just identical!Abigail
@MatthieuM. : fair point. I was considering function pointers to be legacy, but that's probably only my personal taste.Treadwell
@Piranesi : my idea of a delegate is rather empiric. Maybe I am mixig too much function object and delegate (blame my former C# experience).Treadwell
could you also just explain briefly what you think a delegate is nad what it does at the beginning of your question? I'm trying to grasp what it is that they do that would make someone want to use them over simply function pointers or passing references around. Thank you.Strickman
@SirYakalot Something that behaves like a function, but that may hold state at the same time and can be manipulated like any other variable. One use for instance is to take a function that takes two parameters and force the 1st parameter to have certain value creating a new function with a single parameter (binding in my list). You can't achieve this with function pointers.Treadwell
I keep hearing this reference to 'state', what exactly do people mean by this?Strickman
That the function has internal variables that are preserved from call to call. i.e. int f() { static int i = 0; return ++i }. Each call to f() will give a different result despite the parameters being the same.Treadwell
Great job. This really helped me digging into c++ a little deeper :) I'm personally using std::function for now. One thing is for class member function you can only pass static function.Blackford
@Treadwell or anyone else, is there any kind of general performance generalizations when comparing the use of a lambda as delegate to an std::function delegate? You mention std::function being slower, but not really compared to what it's slower than.Asberry
The question was 'what are they', not 'list a bunch of ways to implement them'.Discrimination
They don't exist in C++ per se. Hence the list.Treadwell
@Blackford "for class member function you can only pass static function". Not true. See my answer. https://mcmap.net/q/137739/-what-is-a-c-delegateIncorrigible
I
43

A delegate is a class that wraps a pointer or reference to an object instance, a member method of that object's class to be called on that object instance, and provides a method to trigger that call.

Here's an example:

template <class T>
class CCallback
{
public:
    typedef void (T::*fn)( int anArg );

    CCallback(T& trg, fn op)
        : m_rTarget(trg)
        , m_Operation(op)
    {
    }

    void Execute( int in )
    {
        (m_rTarget.*m_Operation)( in );
    }

private:

    CCallback();
    CCallback( const CCallback& );

    T& m_rTarget;
    fn m_Operation;

};

class A
{
public:
    virtual void Fn( int i )
    {
    }
};


int main( int /*argc*/, char * /*argv*/ )
{
    A a;
    CCallback<A> cbk( a, &A::Fn );
    cbk.Execute( 3 );
}
Incorrigible answered 5/3, 2012 at 14:23 Comment(3)
which provides a method to trigger the call? the delegate? how? function pointer?Strickman
The delegate class will offer a method like Execute() which trigger the function call on the object the delegate wraps.Incorrigible
Instead of Execute, I would highly recommend in your case overriding the call operator - void CCallback::operator()(int). The reason for this is in generic programming, callable objects are expected to be called like a function. o.Execute(5) would be incompatible, but o(5) would fit nicely as a callable template argument. This class could also be generalized, but I assume you kept it simple for brevity (which is a good thing). Other than that nitpick, this is a very useful class.Yongyoni
A
23

The need for C++ delegate implementations are a long lasting embarassment to the C++ community. Every C++ programmer would love to have them, so they eventually use them despite the facts that:

  1. std::function() uses heap operations (and is out of reach for serious embedded programming).

  2. All other implementations make concessions towards either portability or standard conformity to larger or lesser degrees (please verify by inspecting the various delegate implementations here and on codeproject). I have yet to see an implementation which does not use wild reinterpret_casts, Nested class "prototypes" which hopefully produce function pointers of the same size as the one passed in by the user, compiler tricks like first forward declare, then typedef then declare again, this time inheriting from another class or similar shady techniques. While it is a great accomplishment for the implementers who built that, it is still a sad testimoney on how C++ evolves.

  3. Only rarely is it pointed out, that now over 3 C++ standard revisions, delegates were not properly addressed. (Or the lack of language features which allow for straightforward delegate implementations.)

  4. With the way C++11 lambda functions are defined by the standard (each lambda has anonymous, different type), the situation has only improved in some use cases. But for the use case of using delegates in (DLL) library APIs, lambdas alone are still not usable. The common technique here, is to first pack the lambda into a std::function and then pass it across the API.

Ares answered 29/3, 2013 at 16:50 Comment(4)
I've done a version of Elbert Mai's generic callbacks in C++11 based on other ideas seen elsewhere, and it does seem to clear most of the issues you cite in 2). The only remaining nagging issue for me is i'm stuck using a macro in the client code to create the delegates. There is probably a template magic way out of this which i haven't yet solved.Racket
I am using std::function without heap in embedded system and it still works.Jocko
std::function doesn't always dynamically allocateTensive
This answer is more of a rant than an actual answerTensive
S
10

Very simply, a delegate provides functionality for how a function pointer SHOULD work. There are many limitations of function pointers in C++. A delegate uses some behind-the-scenes template nastyness to create a template-class function-pointer-type-thing that works in the way you might want it to.

ie - you can set them to point at a given function and you can pass them around and call them whenever and wherever you like.

There are some very good examples here:

Strickman answered 23/3, 2012 at 14:32 Comment(0)
R
5

An option for delegates in C++ that is not otherwise mentioned here is to do it C style using a function ptr and a context argument. This is probably the same pattern that many asking this question are trying to avoid. But, the pattern is portable, efficient, and is usable in embedded and kernel code.

class SomeClass
{
    in someMember;
    int SomeFunc( int);

    static void EventFunc( void* this__, int a, int b, int c)
    {
        SomeClass* this_ = static_cast< SomeClass*>( this__);

        this_->SomeFunc( a );
        this_->someMember = b + c;
    }
};

void ScheduleEvent( void (*delegateFunc)( void*, int, int, int), void* delegateContext);

    ...
    SomeClass* someObject = new SomeObject();
    ...
    ScheduleEvent( SomeClass::EventFunc, someObject);
    ...
Rodenticide answered 3/3, 2016 at 18:20 Comment(0)
N
1

Windows Runtime equivalent of a function object in standard C++. One can use the whole function as a parameter (actually that is a function pointer). It is mostly used in conjunction with events. The delegate represents a contract that event handlers much fulfill. It facilitate how a function pointer can work for.

Neglect answered 25/9, 2014 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.