Session 3#

Agenda#

  1. C++ Functors and Lambdas

  2. C++ Make and CMake

  3. Excercises

C++ Functors and Lambdas#

`c++ -std=c++17 -o functors_and_lamda_functions functors_and_lamda_functions.cpp

  1/*
  2 Functors and Lambda Functions
  3
  4 Useful References:
  5 * Discovering Modern C++ by Peter Gottschling (amongst many other C++ books)
  6 * https://en.cppreference.com/
  7*/
  8
  9#include <iostream> // standard IO
 10#include <vector>   // standard "vector" library for dynamic arrays
 11#include <algorithm> // for the transform functions
 12
 13
 14/*
 15
 16A functor is essentially a class that has defined the "()" operator.
 17
 18This lets us define objects that behave like functions.
 19
 20There are 2 main reasons we might want a function like object:
 21
 22  1.  function like object allow us to define, use, and modify
 23      properties, for instance the current state of an object.
 24      In some applications this may be valueable and simplify
 25      the code (e.g. the function "knows" informatin that you
 26      would otherwise have to pass to the function.) This also
 27      allows you to initialize frequently used values once,
 28      which is especially useful if they're expensive to compute.
 29
 30  2.  By using a functor, the compiler is often able to do a
 31      better job optimizing. Essentially this happens because
 32      the compiler knows exactly what function is being called,
 33      whereas if you pass a pointer to a function, the compiler
 34      just knows a function is being called (but not necessarily
 35      which one.) This means, for example, the compiler might
 36      inline a functor.
 37
 38*/
 39
 40// example of a simple functor
 41// You see functors defined as structs instead of classes, classes are
 42// more general structure with extra features (that we're not using)
 43class increment
 44{
 45private:
 46    // define a private member variable to hold the amount we will
 47    // increment by
 48    // It is common practice to prepend member variables with "m_"
 49    // but this is purely a style choice.
 50    int m_increment_amount;
 51
 52public:
 53
 54    // constructor. This sets the default values.
 55    // This needs to be the same name as the class
 56    // Note we can overload the constructor. Here,
 57    // we're setting the increment amount to 1 if we're
 58    // not provided an argument or we're setting it
 59    // to the inputed value.
 60    increment() : m_increment_amount(1){};
 61    increment(const int &n) : m_increment_amount(n){};
 62
 63    // Now we're going to define the "()" operator 
 64    // so we can increment the input and return the
 65    // new value
 66    int operator()(const int& input_num) const
 67    {
 68        // add the increment amount to the inputed value
 69        // and return that new value
 70        return m_increment_amount + input_num;
 71    }
 72
 73    void changeIncrementAmount(const int &inc) {
 74        m_increment_amount = inc;
 75    }
 76}; // end of increment class. Note, ends with ;
 77
 78
 79// function to print vectors
 80void print_vector(const std::vector<int>& v) 
 81{
 82    for (const auto& val: v) {
 83        std::cout << val << ", ";
 84    }
 85    std::cout << std::endl;
 86}
 87
 88
 89int main() {
 90
 91    // create an array of values to increment
 92    std::vector<int> v = {1, 2, 3, 4, 5}; // list initialized requires c++11 or later
 93
 94    // use our increment functor to add 1
 95    increment inc_by_one; // initialize the functor with the default 
 96    for (auto &val : v) {
 97        val = inc_by_one(val);
 98    }
 99    print_vector(v);
100
101    // use our increment functor to subtract 1
102    increment dec_by_one(-1); // initialize the functor to subtract one
103    for (auto &val : v)
104    {
105        val = dec_by_one(val);
106    }
107    print_vector(v);
108
109    // use our increment functor to add 2 to each value using the
110    // std::transform function
111    //
112    // There is a subtlety here. The increment(2) argument here is
113    // calling the constructor and setting the increment value. 
114    // Inside the transform function, it is calling v[i] = incremnt(v[i])
115    std::transform(v.begin(), v.end(), v.begin(), increment(2));
116    print_vector(v);
117
118    // subtract 2 from each entry
119    std::transform(v.begin(), v.end(), v.begin(), increment(-2));
120    print_vector(v);
121
122    // The functor we defined above is a lot of work for something that
123    // is pretty trivial (and might be even more work if the function
124    // object needed to be more complex).
125    //
126    // Since C++11, C++ has "lambda expressions," which is basically
127    // a way for the compiler to generate a functor for you using some
128    // shorthand notation
129
130    // Make a lambda function that adds 1
131    // auto requires c++14
132    // the [&] specifies we should capture values by reference
133    // we could also capture by value, pointer, etc.
134    // Then the inputs are in (...), we're taking an int
135    // Finally, we define the function in {...} (just returning input + 1)
136    auto add_one_lambda = [&](const int& val){return val + 1;};
137    std::transform(v.begin(), v.end(), v.begin(), add_one_lambda);
138    print_vector(v);
139
140    // subtract 1 using lambdas
141    auto dec_one_lambda = [&](const int &val){ return val - 1; };
142    std::transform(v.begin(), v.end(), v.begin(), dec_one_lambda);
143    print_vector(v);
144
145    // We can also use lambda functions inline. Here, we'll add 2
146    // Again, for style preferences, I find it easier to read lambda expressions
147    // if they're defined like the above. This example is inline and extactly the
148    // same, but more complicated expressions may be difficult to read.
149    std::transform(v.begin(), v.end(), v.begin(), [&](const int &val){ return val + 2; });
150    print_vector(v);
151
152    // and subtract 3
153    std::transform(v.begin(), v.end(), v.begin(), [&](const int &val){ return val - 3; });
154    print_vector(v);
155}

C++ Make and CMake#

  1. CRC Summer Workshop: Compiling Programs

  2. Makefile Tutorial

  3. CMake Tutorial

  4. fmt Library Example

Excercises#

Problem 2 from Project Euler.