Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Advanced C++: Currying and std::bind

Tech 1

1. Currying Process

1.1. Introduction of operator()

Consider a function that returns a different result on each call. For example, two calls should yield different values.

1.1.1. Simple Approach

Using a global (or static) variable that is modified and returned each time.

#include <iostream>

int counter;
int incrementCounter() {
    return ++counter;
}

int main() {
    std::cout << "Hello, World!" << std::endl;
    std::cout << std::boolalpha << (incrementCounter() == incrementCounter()) << std::endl;
    return 0;
}

1.1.2. operator() Overlaoding

Using a class with operator() overloaded (functor).

class Functor {
public:
    int x = 0;
    int operator()() {
        return ++x;
    }
};

void test2() {
    Functor func;
    std::cout << std::boolalpha << (func() == func()) << std::endl;
}

1.2. Chain Adding

Now consider a functon that can be called in a chain: add(1) = 1, add(1)(2) = 3, add(1)(2)(3) = 6, etc. It should also support comparisons like add(1) == 1, and operations like add(1) + 3, add(1) - 3.

  • The function add(1) should return a callable object that can accept further arguments (chainable).
  • This suggests returning *this from operator(), enabling chaining.
  • For comparisons, overload operator==.
  • For arithmetic, overload operator+ and operator-.
  • For output, overload <<.
  • Alternatively, define an implicit conversion to int to simplify comparisons, arithmetic, and output.
class Functor {
public:
    int value;
    
    Functor() : value(0) {}
    Functor(int x) : value(x) {}
    
    Functor& operator()(int val) {
        value += val;
        return *this;
    }
    
    bool operator==(int x) const {
        return value == x;
    }
    
    Functor& operator-(int x) {
        value -= x;
        return *this;
    }
    
    Functor& operator+(int x) {
        value += x;
        return *this;
    }
    
    friend std::ostream& operator<<(std::ostream& out, const Functor& f) {
        out << f.value;
        return out;
    }
    
    // Implicit conversion to int (can replace ==, arithmetic, and <<)
    // operator int() const { return value; }
};

int main() {
    Functor f1;
    f1(1);
    std::cout << f1.value << std::endl;
    
    Functor f2;
    f2(1)(2);
    std::cout << f2.value << std::endl;
    
    Functor f3;
    std::cout << std::boolalpha << (f3(1) == 1) << std::endl;
    
    Functor f4(1);
    f4 = f4 - 2;
    f4 = f4 + 5;
    std::cout << f4.value << std::endl;
    std::cout << f4 << std::endl;
    
    return 0;
}

This demonstrates operator overloading and object-oriented design in C++.

1.3. Currying Process

The chaining above is a form of currying. This is also seen in lambdas that return lambdas:

// Equivalent: add(1, 2) -> add(1)(2)
void test4() {
    auto add = [](int x) {
        return [x](int y) {
            return x + y;
        };
    };
    std::cout << add(1)(2) << std::endl;
}

2. std::bind

std::bind is used for partial function application (binding arguments).

#include <iostream>
#include <functional>

int add(int a, int b) {
    std::cout << "a = " << a << ", b = " << b << std::endl;
    return a + b;
}

int main() {
    using namespace std::placeholders;
    
    auto f1 = std::bind(add, 1, _1);
    std::cout << f1(2) << std::endl;  // Calls add(1,2)
    
    auto f2 = std::bind(add, _1, 1);
    std::cout << f2(2) << std::endl;  // Calls add(2,1)
    
    std::cout << std::bind(add, 1, _1)(2) << std::endl;
    std::cout << std::bind(add, _1, _2)(3, 4) << std::endl;
    std::cout << std::bind(add, _2, _1)(3, 4) << std::endl;
    std::cout << std::bind(add, _1, _1)(3, 4) << std::endl;
    std::cout << std::bind(add, _2, _2)(3, 4) << std::endl;
    
    // C++20: std::bind_front
    // std::cout << std::bind_front(add, 1)(2) << std::endl;
    // C++23: std::bind_back
    // std::cout << std::bind_back(add, 2)(1) << std::endl;
    
    return 0;
}

Output of bind examples

  • The placeholder _i refers to the i-th argument of the resulting callable.
  • Multiple placeholders can map to the same argument.
  • std::bind is closely related to perfect forwarding and std::forward.
Tags: C++

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.