Index ¦ Archives  ¦ Atom  ¦ RSS

C++ Templates I

As anybody who's looked at Cara's capnp->Python conversion code, they may have noticed that I've taken a liking to C++ metaprogramming via templates. I started my initial foray into C++ back in '06 when modifying an existing codebase for a robotics competition. It didn't use templates, and for the next few years any C++ codebases I used avoided templating as if it didn't exist, and when I did run into it it was limited to anything dealing with the STL, making it seem truly magical.

Around 2013, the company I was working at finally started accepting parts of C++11 into use. After perusing all the new changes in C++11, it seems that the standards folk spent a lot of time making templates more useful, especially using type_traits. From there through C++14 and 17, things have gotten much, much better and I think it's a time the average C++ programmer was introduced to templating as a normal part of C++ programs, rather than just in the STL.

Here's a quick example of a more powerful form of overloading:

template<typename T>
T ReturnsWhatItGets(T x) {
  return x;
};

This function is able to alter its return type based on its input. This makes it possible to create mathematical functions that don't assume you want a double:

template<typename T>
T Ceiling(T x);

However, the above function can be misused by passing in integers. First, let's make the above function only work for floating point numbers.

template<typename T>
std::enable_if<std::is_floating_point<T>::value, T>::type Ceiling(T x);

std::enable_if takes advantage of SFINAE, a fancy way of saying "If the compiler can't make this work for the given types, it doesn't exist." If the first argument of std::enable_if doesn't evaluate to true, then ::type won't exist, which makes the compiler as if that definition of Ceiling() doesn't exist. In C++14 and 17, some helpers were introduced to make this less verbose:

template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T> Ceiling(T x);

Now let's allow integral inputs as a separate declaration:

template<typename T>
std::enable_if_t<std::is_integral<T>, T> Ceiling(T x) { return x };

Now that it's been introduced, it seems simple, right? If you used Ceiling() on any integral or floating point type, it'll just work. And if you attempt to pass a struct or other object, the compiler will output errors that claim no Ceiling() could be chosen, then output why it couldn't use either of the above instances.

Quick compiler recommendation: If you're using Clang, then you'll get slightly better looking errors. GCC has stepped up its game in this regard, but Clang is still better looking in my opinion.

© Fahrzin Hemmati. Built using Pelican. Theme by Giulio Fidente on github.