Skip to main content

Posts

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 testfails 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 *, int)&…
Recent posts

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.

Inheritancestd::variantNeed not know all the derived types upfront (open-world assumption)Must know all the cases upfront (closed-world assumption)Dynamic Allocation (usually)No dynamic allocationIntrusive (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 classesAlgorithm in one placeLanguage supported (Clear errors if pure-virtual is not implemented)Library supported (poor error messages)Creates a first-class abstractionIt’s just a containerKeeps fluent interfaces…

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 with even b…

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 Transcripts00: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 example. Therefore, language-supported pattern matching is muc…

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::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…