Skip to main content

Posts

Simple Template Currying

Currying is the technique of transforming a function that takes multiple arguments in such a way that it can be called as a chain of functions, each with a single argument. I've discussed Currying on this blog previously in Fun With Lambdas C++14 Style and Dependently-Typed Curried printf . Both blogposts discuss currying of functions proper. I.e., they discuss how C++ can treat functions as values at runtime. However, currying is not limited to just functions. Types can also be curried---if they take type arguments. In C++, we call them templates. Templates are "functions" at type level. For example, passing two type arguments std::string and int to std::map gives std::map<std::string, int> . So std::map is a type-level function that takes two (type) arguments and gives another type as a result. They are also known as type constructors. So, the question today is: Can C++ templates be curried? As it turns out, they can be. Rather easily. So, here we go... #...

Non-colliding Efficient type_info::hash_code Across Shared Libraries

C++ standard library has std::type_info and std::type_index to get run-time type information about a type. There are some efficiency and robustness issues in using them (especially when dynamically loaded libraries are involved.) TL;DR; The -D__GXX_MERGED_TYPEINFO_NAMES -rdynamic compiler/linker options (for both the main program and the library) generates code that uses pointer comparison in std::type_info::operator==() . The typeid keyword is used to obtain a type's run-time type information. Quoting cppreference. The typeid expression is an lvalue expression which refers to an object with static storage duration , of the polymorphic type const std::type_info or of some type derived from it. std::type_info objects can not be put in std::vector because they are non-copyable and non-assignable. Of course, you can have a std::vector<const std::type_info *> as the object returned by typeid has static storage duration. You could also use std::vector<std::ty...

Chained Functions Break Reference Lifetime Extension

I discovered a reference lifetime extension gotcha. TL;DR; Chained functions (that return a reference to *this ) do not trigger C++ reference lifetime extension. Four ways out: First, don't rely on lifetime extension---make a copy; Second, have all chained functions return *this by-value; Third, use rvalue reference qualified overloads and have only them return by-value; Fourth, have a last chained ExtendLifetime() function that returns a prvalue (of type *this). C++ Reference Lifetime Extension C++ has a feature called "reference lifetime extension". Consider the following. std::array<std::string, 5> create_array_of_strings(); { const std::array &arr = create_array_of_strings(); // Only inspect arr here. } // arr out of scope. The temporary pointed to by arr destroyed here. The temporary std::array returned by create_array_of_strings() is not destroyed after the function returns. Instead the "lifetime" of the temporary std::array is ...

Convenient deduction guides for std::function

The objective is to allow the following to be valid C++. #include <functional> struct Test { void func(int) {} }; void test() { std::function deduced = &Test::func; // compiler error std::function<void (Test *, int)> ex = &Test::func; // OK } With class template argument deduction in C++17, type arguments for std::function above should have been deduced. The first line in function test fails to compile as of this writing because std::function does not appear to have deduction guides for conversion from pointer to member functions. On the other hand, explicitly specifying the template arguments makes the compiler happy. The following deduction guide seems to fix the issue. namespace std { // warning: undefined behavior template<class R, class C, class... ArgTypes> function(R(C::*)(ArgTypes...)) -> function<R(C*, ArgTypes...)>; } void test() { std::function deduced = &Test::func; // Now, OK std::function<void (Test *,...

Inheritance vs std::variant

C++17 added std::variant and std::visit in its repertoire. They are worth a close examination. I've been wondering about whether they are always  better than inheritance for modeling sum-types (fancy name for discriminated unions) and if not, under what circumstances they are not. We'll compare the two approaches in this blog post. So here it goes. Inheritance std::variant Need not know all the derived types upfront (open-world assumption) Must know all the cases upfront (closed-world assumption) Dynamic Allocation (usually) No dynamic allocation Intrusive (must inherit from the base class) Non-intrusive (third-party classes can participate) Reference semantics (think how you copy a vector of pointers to base class?) Value semantics (copying is trivial) Algorithm scattered into classes Algorithm in one place Language supported (Clear errors if pure-virtual is not implemented) Library supported (poor error messages) Creates a first-class abstraction It’s just a conta...

Ground Up Functional API Design in C++

There are plenty of reasons why functional APIs look and feel different than more common object-oriented APIs. A tell-tale sign of a functional APIs is existence of a core abstraction and a set of methods with algebraic properties. The abstraction part is of course about being able to talk about just the necessary details and nothing more. The algebraic part is about being able to take one or more instances of the same abstraction and being able to create a new one following some laws . I.e., Lawfully composing smaller parts into a bigger thing that is an instance of the same abstraction the original instances were. Composition by itself is nothing new to OO programmers. Composite, Decorator design patterns are all about putting together smaller pieces into something bigger. However, they miss out on the lawful part. Functional paradigm gives you extra guard rails so that you can bake in some well-understood mathematical properties into the abstraction. I think most people w...

The video of New Tools for a More Functional C++

My previous talk on New Tools for a More Functional C++ ran into some audio issue during the meetup . I did not upload the video back then because it had no audio what-so-ever. I finally got around to record the audio track for the talk and I mixed it with the video. So here is the final video. Have fun with FP in C++! If you don't have 35 minutes, checkout the partial video transcripts below. Functional Programming Tools in C++ from Sumant Tambe on Vimeo . Video Transcripts 00:16 We’re going to talk about functional [programming] tools in C++ and what new capabilities exist in modern C++.  2:00 I'm reviewing  Functional Programming in C++ book by Manning---a good book for C++ programmers to acquire beginner to intermediate level knowledge of FP in C++. 2:30 Sum types and (pseudo) pattern matching in C++ 5:00 Modeling a game of Tennis using std::variant 7:30 std::visit spews blood when you miss a case in the visitor. See an examp...

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. 

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::funct...

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 = (...

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_count...

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_e...

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 ...