September 13, 2020

Example of apply and invoke

Do you know the functions Apply and Invoke? Those two methods from “std” are linked to the concept of “callable” objects.

A “callable” object definition is simple. It could be a function, or a class object which overloads “operator()” (which can be a method!).

The purpose of “Apply” and “Invoke”

One of the goals of those functions is to invoke an object directly inside a function. But, why using Invoke and Apply when I can simply call “myFunction()” or “myObject.myMethod()”?

You could, for instance, switch a simple pointer to change the callable object or the “class object” call! Moreover, apply has a great feature that can help you to do cleaner code!

During this post, I will show you different implementations of Apply and Invoke, their differences, and purpose in detail.

So, here we go!

To feature the different usages, I will use the following class:

class Rectangle
{
    public:
    Rectangle(const unsigned int height, const unsigned int width, const std::string rectangleName = "Rectangle");
    virtual ~Rectangle() {};
    unsigned long getArea() const;
    static unsigned long getAreaStatic(const Rectangle& rectangle);
    void changeConfiguration(const unsigned int height, const unsigned int width, const std::string rectangleName = "Rectangle");
 
    private:
    unsigned int height;
    unsigned int width;
    std::string rectangleName;
};
 
int multiplicationTwoArgs(short a = 1, short b = 1)
{
    return (a*b);
}
 
int multiplicationThreeArgs(short a = 1, short b = 1, short c = 1)
{
    return (a*b*c);
}

How to use Invoke

For “invoke”, you should include the header “functional”:

#include <functional>

The usage of “invoke” is pretty simple.

At first, you should include the callable object. It could be a pointer to a method, a function, or a static function.

After this first parameter, you put the necessary arguments (like a simple call!). For instance, the call of “myFunction(int a, int b)”, will be converted to “std::invoke (myFunction, a, b).”

Then, we can exploit invoke in the following conditions:

  • Call a function/Static function
  • Call a method/Static method
  • Call with Lambda expressions
  • Unfortunately, the “default argument values” are not supported (see example below).

For a better comprehension of invoke, let’s see this example:

void invokeUsage()
{
    Rectangle mySquare(2, 2, "Square");
    // Invoke from class
    std::cout << "====== DATA MEMBER INVOKE ======" << std::endl;
    std::cout << "My Square area = " << std::invoke(&Rectangle::getArea, mySquare) << std::endl;
 
    std::cout << "====== STATIC MEMBER INVOKE ======" << std::endl;
    std::cout << "My Square area = " << std::invoke(&Rectangle::getAreaStatic, mySquare) << std::endl;
 
    std::cout << "====== MULTIPLE ARGUMENTS INVOKE ======" << std::endl;
    std::invoke(&Rectangle::changeConfiguration, mySquare, 3, 4, "Rectangle");
 
    // Invoke with function + arguments
    std::cout << "====== FUNCTION INVOKE ======" << std::endl;
    std::cout << std::invoke(multiplicationThreeArgs, 1, 2, 3) << std::endl;
    // std::invoke(&Rectangle::changeConfiguration, mySquare, 8, 4);    <===== Invalid!
 
    // Lambda invoke
    std::cout << "====== LAMBDA INVOKE ======" << std::endl;
    std::invoke([]() { std::cout << "From lambda!" << std::endl; });
}

Now, let’s build this with the option c++17:

g++ --std=c++17 InvokeAndApply.cpp

And here we go!

./a.out
Square created of:
height = 2
width = 2
====== DATA MEMBER INVOKE ======
My Square area = 4
====== STATIC MEMBER INVOKE ======
My Square area = 4
====== MULTIPLE ARGUMENTS INVOKE ======
Changing name Square to Rectangle
New size: height = 3, width = 4
====== FUNCTION INVOKE ======
6
====== LAMBDA INVOKE ======
From lambda!

How to use Apply

As “invoke”, “apply” requires the header “functional”:

#include <functional>

Apply is one of my favorites features from C++17. Its functionality and behavior are similar to invoke. Excepted that “apply” arguments are stored in a pair, an array, or a tuple! I cannot count the number of times I wanted a function like this in C++!

This kind of inputs is ideal to structure your data and simplify readability. Let’s see an example with apply:

void applyUsage()
{
    std::tuple myTuple(1, 2, 3);
    std::array myArray = {2, 2, 2};
    std::pair myPair(1, 2);
 
    // Usage of tuple
    std::cout << std::apply(multiplicationThreeArgs, myTuple) << std::endl;
 
    // Usage of array
    std::cout << std::apply(multiplicationThreeArgs, myArray) << std::endl;
 
    // Usage of pair
    // std::apply(multiplicationThreeArgs, myPair);     <== Invalid!
    std::cout << std::apply(multiplicationTwoArgs, myPair) << std::endl;
 
}

and let’s run:

./a.out
6
8
2

Of course, you can also call methods from a class:

void applyUsageWithClass()
{
    Rectangle mySquare(2, 2, "Square");
    std::tuple myTuple(mySquare, 16, 9, "Golden Rectangle");
 
    // Usage of tuple with class
    std::apply(&Rectangle::changeConfiguration, myTuple);
}

And here is the output:

./a.out
Square created of:
height = 2
width = 2
Changing name Square to Golden Rectangle
New size: height = 16, width = 9

Let's summarize...

Invoke

  • Usage of callable objects - Function, Method.
  • Arguments are passed in Invoke.
  • Doesn't support "default values".

Apply

  • Usage of callable objects - Function, Method.
  • Argument are passed through a Tuple, Array or Pair.
  • Doesn't support "default values".

Conclusion

As you saw during this post, Invoke and Apply are similar in many points. The only difference concerns the input configuration.

For "Invoke", inputs are directly exploited. If a tuple or array is put as a parameter, Invoke will not unpack this.

Concerning "Apply", only two parameters can enter. However, the second argument is an unpacked Tuple.

About the author 

Axel Fortun

​Developer specialized in Linux environment and embedded systems.
​Knowledge in multiple languages as C/C++, Java, Python​ and AngularJs.
​Working as Software developer since 2014 with a beginning in the car industry​ and then in ​medical systems.