Skip to main content

Posts

Showing posts from 2018

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