FunktionalTR1Cpp11Eng

Functional in TR1 and C++11

This post continues our journey through the functional features of classical, modern, and future C++. Today, we stop in the present.

 What has modern C++ to offer?

FunktionalTR1Cpp11Eng

In 2005, 13 libraries based on Boost were proposed in the so-called technical report 1 (TR1) as candidates for the upcoming C++ standard. 12 of them made it into C++11 including the two functions std::bind and std::function. Both functions work very well together. On the one hand, std::bind empowers you to make functional objects easily composable. On the other hand, std::functions get these temporary function objects from std::bind and give them a name. Both functions need the header <functional>. You can guess why.

std::bind

You can generate with std::bind function objects in various ways because it empowers you to

  • bind the argument at arbitrary positions,
  • reorder the sequence of the arguments,
  • introduce placeholders for arguments,
  • partially evaluate functions,
  • and directly invoke the new function object, use it in the Standard Template Library (STL) algorithm, or store it in std::function.

std::function

std::function is a polymorphic function wrapper. Therefore, it can take arbitrary callables and give them a name.  Callables are all entities that behave like a function. In particular, these are lambda functions, function objects, and functions themselves. std::function is always needed if you have to specify the callable type.

That was enough theory. Let’s start with the more exciting stuff.

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// bindAndFunction.cpp

#include <functional>
#include <iostream>

double divMe(double a, double b){
  return double(a/b);
}

using namespace std::placeholders;

int main(){

  std::cout << std::endl;

  // invoking the function object directly
  std::cout << "1/2.0= " << std::bind(divMe, 1, 2.0)() << std::endl;

  // placeholders for both arguments
  std::function<double(double, double)> myDivBindPlaceholder= std::bind(divMe, _1, _2);
  std::cout << "1/2.0= " << myDivBindPlaceholder(1, 2.0) << std::endl;

  // placeholders for both arguments, swap the arguments
  std::function<double(double, double)> myDivBindPlaceholderSwap= std::bind(divMe, _2, _1);
  std::cout << "1/2.0= " << myDivBindPlaceholderSwap(2.0, 1) << std::endl;

  // placeholder for the first argument
  std::function<double(double)> myDivBind1St= std::bind(divMe, _1, 2.0);
  std::cout<< "1/2.0= " << myDivBind1St(1) << std::endl;

  // placeholder for the second argument
  std::function<double(double)> myDivBind2Nd= std::bind(divMe, 1.0, _1);
  std::cout << "1/2.0= " << myDivBind2Nd(2.0) << std::endl;

  std::cout << std::endl;

}

 

Do you want to see a few variations of invoking divMe (lines 6 – 8)? To use the simple notation _1, _2 for the placeholders std::placeholders::_1, std::placeholders::_2 in the source code, I have to introduce the namespace std::placeholders in line 10.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    I bind in line 17 in the expression std::bind(divMe, 1, 2.0) the arguments 1 and 2.0 to the function divMe and invoke them in place with (). I bind in line 20 the function object and invoke it with arguments 1 and 2.0. Line 24, 28, and 32 follows a similar strategy. I change the sequence of the arguments in line 24. In line 28, only the first argument is bound; in line 32, only the second argument is. std::function gets the resulting function objects.  Template arguments like double(double, double) (line 24) or double(double) (line 28 and 32) stands for the the callable, which std::function accepts. double(double, double) is a function taking two doubles and returning a double.

    At the end, the output of the program.

     bindAndFunction

    Impressed? I guess, yes. In particular, the last two examples in which std::function gets a function of arity two and returns a function of arity one are quite astonishing. The arity of a function is the number of arguments a function gets. std::bind evaluates in both calls only one argument and uses for the non-evaluated one a placeholder. This technique is called partial function application.

    Partial function application

    Partial function application is quite similar to a technique called currying. Currying is well-known in functional programming. It stands for a technique in which a function takes more than one argument and will successively be transformed into a series of functions taking only one argument. Therefore, a function takes only one argument in the programming language Haskell. I hear your question. How can a function such as add be implemented, which needs two arguments? The magic is happening implicitly. Functions in Haskell that needs n arguments are transformed into functions returning a function that only needs n-1 arguments. The first elements are evaluated in this transformation.

    The name currying is coined by the mathematician Haskell Curry and Moses Schönfinkel. Currying is named after the family name of Haskell Curry; Haskell after his first name. Sometimes, currying is called schönfinkeln.

    A drop of bitterness

    A drop of bitterness remains. As well, std::bind and std::function are almost superfluous with C++11. You can use lambda functions instead of std::bind; you can use most of the time automatic type deduction with auto instead of std::function. We can quite efficiently rewrite the program with the enriched core language in C++11.

     

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    // lambdaAndAuto.cpp
    
    #include <functional>
    #include <iostream>
    
    double divMe(double a, double b){
      return double(a/b);
    }
    
    using namespace std::placeholders;
    
    int main(){
    
      std::cout << std::endl;
    
      // invoking the function object directly
      std::cout << "1/2.0= " << [](int a, int b){ return divMe(a, b); }(1, 2.0) << std::endl;
    
      // placeholders for both arguments
      auto myDivBindPlaceholder= [](int a, int b){ return divMe(a, b); };
      std::cout << "1/2.0= " << myDivBindPlaceholder(1, 2.0) << std::endl;
    
      // placeholders for both arguments, swap the arguments
      auto myDivBindPlaceholderSwap= [](int a, int b){ return divMe(b, a); };
      std::cout << "1/2.0= " << myDivBindPlaceholderSwap(2.0, 1) << std::endl;
    
      // placeholder for the first argument
      auto myDivBind1St= [](int a){ return divMe(a, 2.0); };
      std::cout<< "1/2.0= " << myDivBind1St(1) << std::endl;
    
      // placeholder for the second argument
      auto myDivBind2Nd= [](int b){ return divMe(1, b); };
      std::cout << "1/2.0= " << myDivBind2Nd(2.0) << std::endl;
    
      std::cout << std::endl;
    
    }
    

     

    Let me say a few words about the lambda-functions. The expression [](int a, int b){ return divMe(a, b); }(1, 2.0) defines a lambda function that executes divMe. The trailing braces invoke the lambda function just in place. This will not hold for the lambda functions in lines 28 and 32. Both will be invoked in the subsequent lines. The impressive fact is that the lambda function binds the first (line 32) or the second argument (line 28).

    What’s next?

    The polymorph function wrapper std::function can, most of the time, be replaced by auto. Most of the time because sometimes you have the explicitly specify the callable. That is the domain of std::function. A typical example of such a use case is a dispatch table which I will present in the next post.

     

     

     

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

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