C# delegates and equivalent in C++

Introduction

Delegate is a concept defined in C# (not in Java, and not to be confused with Delegation design pattern in Kotlin). In C#, a delegate is "type that represents references to methods with a particular parameter list and return type"

C++ doesn't have the concept of delegate, but we could implement the concepts using C++ tools such as function pointer, Lamda function, std::function

In this blog, I discuss a simple need for delegate in C# . I then demonstrate how delegate is used. Finally, I describe in brief how it could be similarly implemented with C++.

How to give extension to a framework

Let say, I design a Robot "framework", where I would define certains features for my framework such as canRun, canCarry, canFight. I would use an interface to define some public APIs so that clients can call and implement certain concrete classes to implement the APIs. Overtime I would add in new releases that includes more concrete classes that implements the features differently. For instance, in below diagram, I have 2 concrete classes that implement IRobot.

What if I want to give my client even more freedom. I want the client to even customize canRun by accepting different types of parameters?

One way to implement this is probably using function template and class template. I could provide something like:

template<typeName T> void canRun(T fueltype);

Then if someone has properly initiated an instance of WarRobot, can call canRun and provide a different fuel type.

IRobot clientRobot = new WarRobot();
clientRobot.canRun(airplaneFuel);
clientRobot.canRun(dieselFuel);

If I want to give client the ability to customize/implement the logic of canRun, I would use polymorphism, basically allowing client to create a custom class and inherit the interface.

Using delegate to give extension to a framework

C# offer a simple solution: using delegate.

I would have to define a delegate: a function prototype with keyword "delegate". For example:

public delegate void canPerform(Fuel)

The above delegate defines a function that accepts Fuel as a parameter. The delegate name is canPerform

Client can use the delegate to first call the function that have same definition (return void, and accept Fuel as a parameter)

var clientRobot = new WarRobot();
Fuel fuel = new Fuel();
clientRobot.canPerform = clientRobot.canRun(fuel);
clientRobot.canPerform = clientRobot.canCary(fuel);

It is a bit silly at this point, since we definitely don't need this word coding styles. We can simply call clientRobot.canRun(fuel).

But if we want to implement the custom logic. Then the delegate is helpful:

// define a function with same function signature as a delegate
static void canDance(Fuel fuel) {
// run dance logic
}

static void main(){
    var clientRobot = new WarRobot();
    Fuel fuel = new Fuel();
    clientRobot.canPerform = canDance(fuel);   // client Robot can run a custom logic)
}

Here is my full working program to illustrate usage of delegate as extension:

Other usages of delegates:

Delegates are not only used to give extension to already designed frameworks as illustrated above. They are also used as:

  • predicate: predicate is special delegate that returns boolean value and is often provided as a parameter to another function. That function is called with the condition result provided by the predicate as parameter. For example List.FindAll is a built-in generic c# that finds an item in a list, that satisfies the predicate.
  • callback function: delegate is passed in as a callback function to another function. Call back function is often used as a way to return result, or invoked an action after a forward action is run.
  • lamda expression is just shorthand notation for anonymous function and is often provided as a parameter to a function. The function must have been declared as using a delegate in its parameter, which is replaced by the lamda expression when it is called.

Both Callback and Lamda expression are heavily used in designed pattern even-driven architecture. Even-Driven architecture design pattern is also a practical way to decoupling software modules, giving framework extension capability. I have another blog post about Even Driven architecture implementation in C# here:

More on Delegates

  • every delegate inherits System.Multicast.Delegate and is essentially a class.
  • multicast delegate can "points" to many functions. Thus we could something like this:
clientRobot.canPerform = canDance(fuel);
clientRobot.canPerform = canRun(fuel);    // use the same delegate to call another function
clientRobot.canPerform += canDance(fuel);  // it can even add. The delegate is treated like and object.

Built-in Delegates

  • Func<T, TResult> is a function that takes in parameter of any type and output the return as TResult. Func is built-in delegate to use for any function that have the same signature. Likewise, we also have more of Func such as Func<T, U, TResult>
  • Action<T> is another standard delegate that has 1 to 4 parameters and doesn't return value.

Implementing "delegate" in C++

We can see that delegate is similar to a function pointer, a pointer that points to address of a function, in C++.

C++ syntax to define function pointer and use the function:

// define function pointer as a member of class WarRobot
void (WarRobot::*canPerform)(Fuel fuel) = nullptr;

// using typedef and std::function:
typedef std::function<Fuel()> canPerform;  

In client code, we have to implement a function which same signature and assign the function pointer to the address of that function.

// implement a function with the same signature as the function pointer.
void myCustomDance (Fuel fuel) {
// some logic
}

int main(){
   WarRobot myRobot;
   Fuel fuel;
   myRobot.canPerform = &myCustomDance(fuel);
}

Function pointer find its usage in callback as well. I have a more detail blog at Different ways to implement callbacks – My sky (freewindcode.com)

Leave a Reply

Your email address will not be published. Required fields are marked *