Skip to main content

Posts

New Tools for a More Functional C++

I presented the following slide deck at the ACCU meetup yesterday.

New Tools for a More Functional C++ from Sumant Tambe
Abstract: Variants have been around in C++ for a long time and C++17 now has std::variant. We will compare inheritance and std::variant for their ability to model sum-types (a fancy name for tagged unions). We will visit std::visit and discuss how it helps us model the pattern matching idiom. Immutability is one of the core pillars of Functional Programming (FP). C++ now allows you to model deep immutability; we'll see a way to do that using the standard library. We'll explore if `return std::move(*this)` makes any sense in C++. Immutability may be a reason for that.
Recent posts

Binding std::function to member functions

I realized that std::function can be bound to member functions without requiring the *this object. Consider the following examples. // std::string::empty is a const function. All variables from e1 to e5 are fine. std::function<bool(std::string)> e1 = &std::string::empty; std::function<bool(std::string &)> e2 = &std::string::empty; std::function<bool(const std::string &)> e3 = &std::string::empty; std::function<bool(std::string *)> e4 = &std::string::empty; std::function<bool(const std::string *)> e5 = &std::string::empty; // std::string::push_back is not a const function. p4 and p5 don't compile. std::function<void(std::string, char)> p1 = &std::string::push_back; std::function<void(std::string &, char)> p2 = &std::string::push_back; std::function<void(std::string *, char)> p3 = &std::string::push_back; // These two don't compile because push_back is a non-const function std::func…

Avoiding intermediate copies in std::accumulate

std::accumulate makes a ton of copies internally. In fact it's 2x the size of the number of elements in the iterator range. To fix, use std::ref and std::reference_wrapper for the initial state. std::shared_ptr is also a possibility if the accumulated state must be dynamically allocated for some reason. Live code on wandbox.

Update: Please see alternative solutions in the comments section.

#include <iostream> #include <cstdlib> #include <algorithm> #include <vector> #include <string> #include <numeric> #include <functional> struct Vector : public std::vector<int> { Vector(std::initializer_list<int> il) : std::vector<int>(il){ std::cout << "Vector(std::initializer_list)\n"; } Vector() { std::cout << "Vector()\n"; } Vector(const Vector &v) : std::vector<int>(v) { std::cout << "Vector(const Vector &)\n"; } Vector & operator = (co…

Playing with C++ Coroutines

While looking for some old photos, I stumbled upon my own presentation on C++ coroutines, which I never posted online to a broader audience. I presented this material in SF Bay ACCU meetup and at the DC Polyglot meetup in early 2016! Yeah, it's been a while. It's based on much longer blogpost about Asynchronous RPC using modern C++. So without further ado.

C++ Coroutines from Sumant Tambe

Folding Monadic Functions

In the previous two blog posts (Understanding Fold Expressions and Folding Functions) we looked at the basic usage of C++17 fold expressions and how simple functions can be folded to create a composite one. We’ll continue our stride and see how "embellished" functions may be composed in fold expressions.

First, let me define what I mean by embellished functions. Instead of just returning a simple value, these functions are going to return a generic container of the desired value. The choice of container is very broad but not arbitrary. There are some constraints on the container and once you select a generic container, all functions must return values of the same container. Let's begin with std::vector.
// Hide the allocator template argument of std::vector. // It causes problems and is irrelevant here. template <class T> struct Vector : std::vector<T> {}; struct Continent { }; struct Country { }; struct State { }; struct City { }; auto get_countries…

Folding Functions

In the last post we looked at basic usage of C++17 Fold Expressions. I found that many posts on this topic discuss simple types and ignore how folds may be applicable to more complex types as well. [Edit: Please see the comments section for some examples elsewhere in the blogosphere.] In this post I'm going to describe folding over functions.

Composing Functions

Function composition is a powerful way of creating complex functions from simple ones. Functions that accept a single argument and return a value are easily composable. Consider the following example to compose two std::functions. template <class A, class B, class C> std::function<C(A)> compose(std::function<B(A)> f, std::function<C(B)> g) { return [=](A a) -> C { return g(f(a)); }; } int main(void) { std::function<int(std::string)> to_num = [](std::string s) { return atoi(s.c_str()); }; std::function<bool(int)> is_even = [](int i) { return i%2==0; }; auto is_str_even…

Understanding Fold Expressions

C++17 has an interesting new feature called fold expressions. Fold expressions offer a compact syntax to apply a binary operation to the elements of a parameter pack. Here’s an example. template <typename... Args> auto addall(Args... args) { return (... + args); } addall(1,2,3,4,5); // returns 15. This particular example is a unary left fold. It's equivalent to ((((1+2)+3)+4)+5). It reduces/folds the parameter pack of integers into a single integer by applying the binary operator successively. It's unary because it does not explicitly specify an init (a.k.a. identity) argument. So, let add it. template <typename... Args> auto addall(Args... args) { return (0 + ... + args); } addall(1,2,3,4,5); // returns 15. This version of addall is a binary left fold. The init argument is 0 and it's redundant (in this case). That's because this fold expression is equivalent to (((((0+1)+2)+3)+4)+5). Explicit identity elements will come in handy a little la…