Archive for the ‘Programming’ Category

Adopting C++11: no-brainer features

Friday, February 20th, 2015

C++11/14 is a significant change from C++98/03, and features like move semantics take a while to get used to. Also, people tend to be quite conservative about adopting new features (especially if they look unfamiliar). It took us in the games industry a while to move to C++ from C. But here are what I consider the no-brainer, programming-in-the-small things to adopt. Extra safety and expressiveness with zero runtime impact and pretty much no potential for misuse (never say never, but you’d really have to go out of your way).

1. using instead of typedef

Not only is it a whole two characters fewer to type, it’s easier to read in the common function pointer case, and you can use it for template aliases, which often saves a lot more verbosity in the rest of the code. No more typename everywhere! Compare:

// old and busted: typedef
template <class T>
struct Foo
{
  // I can't typedef Foo<T>::type 
  // and I have to use typename everywhere
  typedef T type;
};
 
// Typical usage: a function pointer
typedef void (*funcptr)(int);
// new hotness: using
template <class T>
struct Foo
{
  using type = T;
};
 
// This pattern is used a lot in C++14 STL
template <class T>
using foo_t = typename Foo<T>::type;
 
// Function pointer is easier to read
using funcptr = void (*)(int);

2. static_assert

Compile-time asserts; what’s not to like? Odds are you have a homegrown C++98 TMP version of this somewhere; now it’s part of the language. Extra safety for zero runtime cost.

3. nullptr

Again, extra safety for zero cost. Ditch your zeros and NULLs, and you can safely have functions overloaded on pointer and integral types.

4. scoped enums (enum class)

The compiler can help prevent accidental confusion between types, and the enum values don’t leak any more. Hurrah! (It’s like Haskell’s newtype for integers!)

5. forward declarations of enums, underlying type for enums

This works on (new) scoped and (old) unscoped enums alike. You don’t have to put in fake bit-width values to force the size of an enum any more, and you can hide the values separately from the declaration, so you don’t need to recompile everything when you add a value.

6. override

When (for example) the class you’re deriving from changes upstream, and now you’re accidentally not overriding a member function you thought you were… you’d really like to know that. With override, a bug that might take you a couple of hours to track down becomes a trivial-to-fix compile error. (Often mentioned in the same breath as override is final, and it’s fine, but much less usefully applicable.)

Now, there are also a couple of things to stop using.

1. rand()

Stop using rand(). Really, it’s bad. Deep down, we always knew it was “bad” but maybe we convinced ourselves it was OK for “small”/”quick-and-dirty” tasks. It isn’t. And now, it’s not any easier or faster than just doing the Right Thing with <random>. STL explains it all in rand() considered harmful.

2. bind

Lambdas supersede bind in every way. They’re more straightforward to write, easier to read, as powerful and at least as efficient if not more so, and just… you know, not so weird. And if you think lambdas take some getting used to, well, I don’t think I ever got used to bind

VS2010 woes: tuples as map keys

Friday, February 20th, 2015

Another day, another compiler/library bug. If you’re unfortunate enough to still be using Visual Studio 2010, don’t use tuples as map keys.

#include <map>
#include <tuple>
#include <utility>
 
using namespace std;
 
typedef tuple<int *, int> FooT;
typedef map<FooT, int> MapT;
 
int main(int, char*[])
{
  MapT m;
 
  // put a value in the map
  {
    FooT f(nullptr, 0);
    m[f] = 1337;
  }
 
  // retrieve a value
  int s;
  {
    FooT f(nullptr, 0);
    auto i = m.find(f);
    if (i != m.end())
    {
      // VS2010 says:
      // error C2440: 'initializing' : 
      // cannot convert from 'const FooT' to 'int *'
      s = i->second;
    }
  }
 
  return 0;
}

A problem with C++ lambdas?

Wednesday, February 18th, 2015

C++ lambdas are wonderful for all sorts of reasons (especially with their C++14-and-beyond power). But I’ve run into a problem that I can’t think of a good way around yet.

If you’re up to date with C++, of course you know that rvalue references and move semantics are a major thing. At this point, there are a lot of blog posts, youtube/conference videos, and even books about how they work, and also about how forwarding references (formerly known as universal references) work with templates and std::forward to provide nice optimal handling of objects with move semantics.

In C++14 the ability to handle lambda captures with move semantics was added with generalized lambda capture; another piece of the puzzle as far as move-semantic completeness goes.

Another lesser-known but important piece of the complete picture is that class member functions can have reference qualifiers. (You can find this in the standard in section 9.3.1 [class.mfct.non-static]). This means that just as we write member functions with const modifiers, we can overload member functions with & or && modifiers and they will be called according to the value category of this. So you know when you can call std::move on data members safely.

Now, lambdas are conceptually like anonymous classes where the body is the operator() and the captured variables are the data members. And we can write lambdas with a mutable modifier indicating that the data members are mutable. (By far the common case is for them to be const, so the ordinary usage of const on a member function is inverted for lambdas.)

I said conceptually because it turns out they aren’t completely like that in at least one important way: lambdas can’t have reference qualifiers. Maybe for good reason – how would the compiler implement that? How would the programmer who wanted that behaviour specify the overloads (s)he’s wanting? These are tricky questions to answer well. But it is a problem – as far as I can tell so far, there is no way to know, inside a lambda, what the value category of the lambda object is. So the performance promise of the move semantic model falls down in the face of safety concerns: I don’t know whether I can safely move from a captured variable inside a lambda.

If anyone has any ideas about this, please let me know. Google and StackOverflow can tell me all about how move captures work with lambdas, but nothing about how to move things out of lambdas safely, or divine the value category of a lambda object. All the things I’ve tried have either not worked, or have resulted in suboptimalities of various kinds. (And frankly, if anything had worked, at this point I’d put it down to a compiler quirk and not to be relied on.)

As far as I can tell, it’s a definite shortcoming of C++ that there’s no way to do this in a compile-time, type-inferred, lambda-land way. I don’t see this in the core language issues – is it a known problem, or is there a known way to solve it that I don’t know yet?

Clang/GCC weirdness

Thursday, February 5th, 2015

Consider the following code:

#include <iostream>
#include <type_traits>
using namespace std;
 
// base template
template <typename T>
struct what_type
{
  void operator()()
  {
    cout << "T" << endl;
  }
};
 
// specialization 1
template <typename T, size_t N>
struct what_type<T[N]>
{
  void operator()()
  {
    cout << "T[" << N << "]" << endl;
  }
};
 
// specialization 2
template <>
struct what_type<int[0]>
{
  void operator()()
  {
    cout << "int[0]" << endl;
  }
};
 
int main(void)
{
  int x[] = {};
  what_type<std::remove_reference_t<decltype(x)>>()();
 
  return 0;
}

I compile this with GCC (g++ (Ubuntu 4.9.1-16ubuntu6) 4.9.1) and I get:

bdeane@epsilon:~/dev/scratch$ g++ -std=c++1y main.cpp 
bdeane@epsilon:~/dev/scratch$ ./a.out
T

When I use clang (Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final) (based on LLVM 3.5.0)) I get:

bdeane@epsilon:~/dev/scratch$ clang++ -std=c++1y main.cpp 
bdeane@epsilon:~/dev/scratch$ ./a.out
int[0]

It seems GCC is wrong here: I would expect specialization 2 to be selected. Now, if I remove specialization 2, clang gives the same output as GCC (i.e. T).

If I put an assert in the base template member function, both clang and gcc think that T = int[0] and give almost identical messages:

a.out: main.cpp:53: void what_type<int [0]>::operator()() [T = int [0]]: Assertion `false' failed.

So why isn’t specialization 1 selected? (And it makes no difference either if I have a specialization for T[] as well as/instead of T[N].)

Edit:

Zero-sized arrays are illegal in C++, although GCC and Clang both compile them by default, presumably for historical reasons. If I turn on pedantic, both give me a warning.

From n4296 8.5.1 section 4:

An empty initializer list {} shall not be used as the initializer-clause for an array of unknown bound. (note 105)
105) The syntax provides for empty initializer-lists, but nonetheless C++ does not have zero length arrays.

So if GCC/Clang are going to allow zero-length arrays, I think they should be consistent about it and do specialization “correctly”. A friend reports that Clang/LLVM 3.3 does what GCC does here and uses the base template. Clang/LLVM 3.5 uses specialization 2, which is “more” correct. I have yet to try with Clang/LLVM 3.6+.

Edit 2:

Clang 3.7 produces the same output as clang 3.5.

How to print anything in C++ (postscript)

Monday, February 2nd, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

Refactoring! My initial plan for customizing opener/closer/separator for containers turned out to be unwieldy: I realized that it wouldn’t be possible for me to provide default specializations and also allow clients to specialize. Also, you may have noticed that the code for printing pairs, tuples, strings and arrays didn’t allow for customization at all. So I reworked the customization, allowing a formatter object as a parameter to prettyprint() and providing a default one:

template <typename T>
inline stringifier<std::remove_reference_t<T>, default_formatter>
prettyprint(T&& t)
{
  return stringifier<std::remove_reference_t<T>, default_formatter>(
      std::forward<T>(t), default_formatter());
}
 
template <typename T, typename F>
inline stringifier<std::remove_reference_t<T>, F>
prettyprint(T&& t, F&& f)
{
  return stringifier<std::remove_reference_t<T>, F>(
      std::forward<T>(t), std::forward<F>(f));
}

The default_formatter looks like this:

struct default_formatter
{
  // default separator, opener and closer
  template <typename T>
  constexpr const char* separator(const T&) const
  { return ","; }
 
  template <typename T>
  constexpr const char* opener(const T&) const
  { return "{"; }
 
  template <typename T>
  constexpr const char* closer(const T&) const
  { return "}"; }
 
  // use [] for vectors
  template <typename T>
  constexpr const char* opener(const std::vector<T>&) const
  { return "["; }
 
  template <typename T>
  constexpr const char* closer(const std::vector<T>&) const
  { return "]"; }
 
  // etc (more omitted)
  ...
};

And now I can pass the formatter object through to each specialization of stringifier_select, and use it appropriately for pairs, tuples, strings and arrays, as well as iterables. When I want to override the formatter, I simply specify a new formatter type, implement the opener/closer/separator functions for the type in question the way I want to, and pass an instance to prettyprint.

How to print anything in C++ (part 5)

Monday, February 2nd, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

So far, we can print containers, but what about arrays? And what about “pretty-printing” strings – perhaps we need to wrap them with quotes. Well, we know that with the existing code, both arrays and strings count as outputtable. Both std::string and char* (and its variously const friends) can be passed to operator<<, and so can arrays, because they decay to pointers.

So, we should be able to deal with this using common-or-garden specialization on stringifier_select. Specializing for std::string is easy:

template <typename C, typename T, typename A>
struct stringifier_select<std::basic_string<C,T,A>, 
                          is_outputtable_tag>
{
  using S = std::basic_string<C,T,A>;
  explicit stringifier_select(const S& t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << C('\"') << m_t << C('\"');
  }
 
  const S& m_t;
};

And specializing for the other char* related types is also as easy, albeit verbose because of dealing with char*, const char*, char* const and const char* const.

Specializing for arrays of char is just as easy. This time we have just the array size as a template argument. And once again, there is a specialization for arrays of const char that is identical.

template <size_t N>
struct stringifier_select<char[N], is_outputtable_tag>
{
  using S = char[N];
  explicit stringifier_select(const S& t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << '\"' << m_t << '\"';
  }
 
  const S& m_t;
};

Specializing for arrays (other than of char) is also easy, and gives us a chance to abstract out the code that we used for the iterable printing. By happy circumstance (i.e. by design!), arrays support std::begin() and std::end(), so we can write the following:

template <typename T>
std::ostream& output_iterable(std::ostream& s, const T& t)
{
  s << iterable_opener<T>()(t);
  auto b = std::begin(t);
  auto e = std::end(t);
  if (b != e)
    s << prettyprint(*b);
  std::for_each(++b, e,
                [&s, &t] (auto& e)
                { s << iterable_separator<T>()(t)
                    << prettyprint(e); });
  return s << iterable_closer<T>()(t);
}
 
template <typename T, size_t N>
struct stringifier_select<T[N], is_outputtable_tag>
{
  using S = T[N];
  explicit stringifier_select(const S& t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return output_iterable<T[N]>(s, m_t);
  }
 
  const S& m_t;
};

And the code for printing iterable things changes likewise. Unlike the situation with char*, we don’t need to deal with const and non-const separately because here, T itself is inferred to be const or not.

And that’s pretty much it – just a couple more things to add. I mentioned enum classes back in part 1, and here’s how we print out their values:

template <typename T>
struct stringifier_select<T, is_enum_tag>
{
  explicit stringifier_select(T t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << static_cast<std::underlying_type_t<T>>(m_t);
  }
 
  T m_t;
};

Simple. Two final things to add: first, specialize for pretty-printing bool equivalently to using std::boolalpha; second, distinguish a few “unprintable” things and output something for them – classes without operator<<, unions, nullptr. The code that does this is very similar to what we’ve already seen.

So now, we can pretty-print all “normally” printable things, containers, pairs and tuples, callable things, certain unprintables that we can meaningfully label, and really unprintable things with a fallback. I think that’ll do for the time being. It’s been a fun journey, exploring TMP techniques, C++ type support, mapping over tuples, and the amazing void_t.

You can find all the resulting code from this exercise on github.

How to print anything in C++ (part 4)

Monday, February 2nd, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

Callable things. There are several types:

  • functions
  • member functions
  • std::functions
  • bind expressions
  • lambdas
  • objects that support operator() (function objects)

So, going back to my tag code, so far (with everything I’ve added) and including callable things, it will look like:

template <typename T>
using stringifier_tag = std::conditional_t<
  is_outputtable<T>::value,
  is_outputtable_tag,
  std::conditional_t<
    is_callable<T>::value,
    is_callable_tag,
    std::conditional_t<
      is_iterable<T>::value,
      is_iterable_tag,
      std::conditional_t<
        is_pair<T>::value,
        is_pair_tag,
        std::conditional_t<
          is_tuple<T>::value,
          is_tuple_tag,
          void>>>>>;

This is getting to be a large nested “if-statement”, but it’s easy to follow, and the compiler doesn’t mind, so I don’t. Basically, this is the preferential order I want to use for outputting things. But then I discovered something puzzling, that took me a while to figure out. (In my defence, I discovered it late at night when I was probably not too sharp!)

// this outputs "1"!
cout << [](){} << endl;

Lambdas (and in fact, functions) can be passed to operator<< – which means they’ll get is_outputtable_tag and produce 1 when printed. Not good. I want to print “<callable (function)>” or “<callable (function object)>”. (I’m OK with lambdas and function objects coinciding here.) So why does printing a plain lambda work at all? Well, the answer (which took me too long to see, and doubtless you, learned reader, have already seen) is that a non-capturing lambda has an implicit conversion to a function pointer. And that, like all pointers, has an implicit conversion to bool. Anything non-zero (like a perfectly good pointer) is true, and when you print true (without using boolalpha), you get 1.

Hm. Think think think.

So, I think there is going to be a compromise here. And that compromise is going to be triggered if someone deliberately writes a function object with a conversion to bool and supporting operator<<. Because lambdas have operator() and a conversion to bool, and I don’t want to use operator<< on them. So, it’s void_t to the rescue again, and by now I have macroed the detection code.

#define SFINAE_DETECT(name, expr)                \
  template <typename T>                          \
  using name##_t = decltype(expr);               \
  template <typename T, typename = void>         \
  struct has_##name : public std::false_type {}; \
  template <typename T>                          \
  struct has_##name<T, void_t<name##_t<T>>>      \
    : public std::true_type {};

I want to know if something’s implicitly convertible to bool:

void bool_conversion_test(bool);
SFINAE_DETECT(bool_conversion, 
              bool_conversion_test(std::declval<T>()))

I don’t need to implement anything for bool_conversion_test because it’s never called, just used to check well-formedness of conversion. I also want to know if something has a function call operator:

SFINAE_DETECT(call_operator, &T::operator())

And now I can massage is_outputtable appropriately: something that has operator<< but is not a function and is not a function object convertible to bool:

template<typename T>
using is_outputtable = typename std::conditional<
  has_operator_output<T>::value &&
  !std::is_function<T>::value &&
  !(has_call_operator<T>::value && has_bool_conversion<T>::value),
  std::true_type, std::false_type>::type;

Now that I’ve eliminated the callable types from the outputtable types, the detection of a callable type is fairly straightforward, between what the STL gives me and what is easy to make myself:

template <typename T>
struct is_std_function : public std::false_type {};
 
template <typename T>
struct is_std_function<std::function<T>> : public std::true_type {};
 
template<typename T>
using is_callable = typename std::conditional<
  has_call_operator<T>::value
  || is_std_function<T>::value
  || std::is_function<T>::value
  || std::is_bind_expression<T>::value,
  std::true_type, std::false_type>::type;

And now all that’s left is to distinguish between the callable types themselves for the purposes of printing. This can be done with judicious use of std::enable_if.

template <typename T>
constexpr static std::enable_if_t<is_std_function<T>::value, 
                                  const char*>
callable_type() { return "(std::function)"; }
 
template <typename T>
constexpr static std::enable_if_t<std::is_function<T>::value, 
                                  const char*>
callable_type() { return "(function)"; }
 
template <typename T>
constexpr static
std::enable_if_t<std::is_bind_expression<T>::value,
                 const char*>
callable_type() { return "(bind expression)"; }
 
template <typename T>
constexpr static
std::enable_if_t<!is_std_function<T>::value && 
                  has_call_operator<T>::value,
                 const char*>
callable_type() { return "(function object)"; }

Finally, I am ready to write the actual printing code for callable types.

template <typename T>
struct stringifier_select<T, is_callable_tag>
{
  explicit stringifier_select(T) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << "<callable "
             << callable_type<T>()
             << '>';
  }
};

Phew!

Recap: so far, we can print:

  • things that have operator<<
  • containers
  • pair and tuple
  • callable things
  • and by default, things that just say “unknown”

Next, what to do about strings and arrays?

How to print anything in C++ (part 3)

Monday, February 2nd, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

So far, we’ve dealt with things that are already outputtable with operator<<, and things that are iterable with begin() and end(). To round out the “containers”, we need to deal with pair and tuple. It’s simple to print a pair:

template <typename T>
struct stringifier_select<T, is_pair_tag>
{
  explicit stringifier_select(const T& t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << '(' << prettyprint(m_t.first)
             << ',' << prettyprint(m_t.second) << ')';
  }
 
  const T& m_t;
};

Printing a tuple is a little harder. There is no easy way to iterate through a tuple, but that’s what we want to do. A tuple is designed for compile-time member access where you know either the index (from C++11) or the type (from C++14) of the element you want. So what we need is a compile-time way of iterating through an index. And that’s what std::index_sequence is for.

template <typename F, typename...Ts, std::size_t...Is>
inline void for_each_in_tuple(const std::tuple<Ts...>& t,
                              F f,
                              std::index_sequence<Is...>)
{
  (void)(int[]) { 0, (f(std::get<Is>(t), Is), 0)... };
}
template <typename F, typename...Ts>
inline void for_each_in_tuple(const std::tuple<Ts...>& t, F f)
{
  for_each_in_tuple(t, f, std::make_index_sequence<sizeof...(Ts)>());
}

This is a handy function to have around. It applies a function to each element of a tuple. First (in the second function seen here), we create a std::index_sequence equal to the size of the tuple. Then (in the top function) we construct an int array using uniform initialization. To deal with zero-length tuples, the array has one zero element to begin. After that, each element of the array is our function applied to the thing at the current index and the index itself, sequenced with zero using the comma operator, so that whatever is returned, the array gets a zero and is happy.

I decided to pass both the element and the index to the function to deal with the separator fencepost problem (as with containers). So outputting a tuple looks like this:

template <typename T>
struct stringifier_select<T, is_tuple_tag>
{
  explicit stringifier_select(const T& t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    s << '(';
    for_each_in_tuple(m_t,
                      [&s] (auto& e, size_t i)
                      { if (i > 0) s << ',';
                        s << prettyprint(e); });
    return s << ')';
  }
 
  const T& m_t;
};

Going well so far. Next, I’ll look at distinguishing callable things.

How to print anything in C++ (part 2)

Sunday, February 1st, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

We have a basic plan, and the ability to detect concrete types. But how can we detect whether an object supports output with operator<<? For this, there is a recently-discovered amazing trick. Here’s the code:

template <typename...>
using void_t = void;
 
template <typename T>
using operator_output_t = decltype(std::cout << std::declval<T>());
 
template <typename T, typename = void>
struct has_operator_output : public std::false_type {};
 
template <typename T>
struct has_operator_output<T, void_t<operator_output_t<T>>> 
  : public std::true_type {};

The key here is void_t. It’s a template alias that takes any template arguments, and always produces void. So what’s the use of that? Well, it doesn’t take just any old arguments. It can only work with well-formed types. So what happens if we give it a type that isn’t well-formed? Nothing too bad – because of course, Substitution Failure Is Not An Error.

Look at operator_output_t. It uses decltype to produce the type of outputting a T. If T doesn’t support operator<<, this type will be invalid.

Now look at has_operator_output. We specialize it using the result of void_t on operator_output_t<T>. If this is ill-formed (T can’t support operator<<) then substitution fails and the unspecialized template, std::false_type, is used. But if T supports operator<<, the result of void_t is well-formed. It’s still void, but it’s more specialized than the default template and so we get std::true_type!

That is the magic that void_t allows; a whole new frontier of template metaprogramming. (And if we relax the “because-we-can-be-general” variadic template, and convert the template aliases, it’s plain old C++98 specialization! Although it’s the combo with decltype and declval that really gives it phenomenal cosmic power.)

I’m going to set up a template to work on the tag types, so that the default implementation of stringifier in part 1 now becomes stringifier_select with a tag type that we can specialize on:

template <typename T, typename TAG>
struct stringifier_select;
 
template <typename T>
using stringifier = stringifier_select<T, stringifier_tag<T>>;
 
template <typename T, typename TAG>
struct stringifier_select
{
  explicit stringifier_select(const T&) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << "<unknown>";
  }
};

And I can set up type discrimination for outputtable types, and implement their output function trivially:

template <typename T>
using stringifier_tag = std::conditional_t<
  has_operator_output<T>::value,
  is_outputtable_tag,
  void>;
 
template <typename T>
struct stringifier_select<T, is_outputtable_tag>
{
  explicit stringifier_select(const T& t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << m_t;
  }
 
  const T& m_t;
};

So, now that we know the void_t pattern, it’s trivial to use it for detecting any container type; in fact, any type that has begin() and end().

template <typename T>
using begin_t = decltype(std::declval<T>().begin());
 
template <typename T, typename = void>
struct has_begin : public std::false_type {};
 
template <typename T>
struct has_begin<T, void_t<begin_t<T>>> 
  : public std::true_type {};
 
template <typename T>
using end_t = decltype(std::declval<T>().end());
 
template <typename T, typename = void>
struct has_end : public std::false_type {};
 
template <typename T>
struct has_end<T, void_t<end_t<T>>> 
  : public std::true_type {};

And then we have a very straightforward way to detect a container or other iterable type:

template<typename T>
using is_iterable = typename std::conditional<
  has_begin<T>::value && has_end<T>::value,
  std::true_type, std::false_type>::type;

To actually output a container, we will need to output surrounding braces and a comma in between each element. We might want to vary opening/closing/separating characters by container type, so I want to allow them to be specialized:

template <typename T>
struct iterable_opener
{
  constexpr const char* operator()(const T&) const
  { return "{"; }
};
 
template <typename T>
struct iterable_closer
{
  constexpr const char* operator()(const T&) const
  { return "}"; }
};
 
template <typename T>
struct iterable_separator
{
  constexpr const char* operator()(const T&) const
  { return ","; }
};

I originally implemented each of these as a straightforward const char*, but using functions and passing the container itself allows them to access (for example) size information which might be useful.

Now that we have the customization points in place, we can write the specialization of stringifier for containers. There’s a minor wrinkle to deal with fenceposting the separators.

template <typename T>
struct stringifier_select<T, is_iterable_tag>
{
  explicit stringifier_select(const T& t) : m_t(t) {}
 
  std::ostream& output(std::ostream& s) const
  {
    s << iterable_opener<T>()(m_t);
    auto b = m_t.begin();
    auto e = m_t.end();
    if (b != e)
      s << prettyprint(*b);
    std::for_each(++b, e,
                  [&s, this] (auto& e)
                  { s << iterable_separator<T>()(m_t)
                      << prettyprint(e); });
    return s << iterable_closer<T>()(m_t);
  }
  const T& m_t;
};

(Notice that it calls prettyprint to output each element recursively.) And that’s it, we can handle all iterable things. Next, handling pair and tuple.

How to print anything in C++ (part 1)

Sunday, February 1st, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

I thought I’d have a go at writing some code that could print things. A pretty-printer, if you like. What I want to be able to do is this:

 // Print x correctly, where x is ANY type.
cout << prettyprint(x) << endl;

Obviously this is going to involve templates since the type of x may vary.

There are a few things that immediately spring to mind:

  • Maybe x is already printable, that is, operator<< might work “out of the box”.
  • If x is a container, we want to do something with that and print the elements.
  • We probably want to print elements of pair and tuple also.
  • The value of some things is not printable, or at least not sensible to print. For example, functions.

The basic plan that came to mind is this: prettyprint is a function template, which returns an object that wraps the x and has operator<< defined. This class will itself be a template, and the template instantiation we choose will be determined by what sort of thing x is.

This suggest that the very first thing we can write looks something like this:

template <typename T>
struct stringifier
{
  explicit stringifier(const T&) {}
 
  std::ostream& output(std::ostream& s) const
  {
    return s << "<unknown>";
  }
};
 
template <typename T>
inline std::ostream& operator<<(std::ostream& s, 
                                const stringifier<T>& t)
{
  return t.output(s);
}
 
template <typename T>
inline stringifier<T> prettyprint(T&& t)
{
  return stringifier<T>(std::forward<T>(t));
}

So far so good. Now we can print “<unknown>” for any type. We have a place to start specializing things.

A quick poke around C++ type support and some experiments soon fleshed out the plan:

  1. There are at least 6 types of callable things: functions, member functions, std::functions, bind expressions, lambdas, and objects that support operator() (function objects). All of these are “unprintable”. Lambdas are a kind of function object; I don’t know whether they can be meaningfully distinguished or whether that is desirable.
  2. nullptr is its own type and unprintable.
  3. enum class values are not printable out-of-the-box; they will require a cast to std::underlying_type.
  4. pairs should be easy to deal with, but tuples will require some effort. They are designed for static access, not iteration.

So now I have 4 broad categories I want to represent for printing: already-outputtable, unprintable, callable, and containers/pairs/tuples. I decided to make tag types for each specializable thing so as to be able to select on them.

struct is_iterable_tag {};
struct is_pair_tag {};
struct is_tuple_tag {};
struct is_callable_tag {};
struct is_outputtable_tag {};
struct is_enum_tag {};
struct is_unprintable_tag {};

As luck would have it, several of these already have built-in support for detecting:

template<typename T>
using is_unprintable = typename std::conditional<
  std::is_union<T>::value ||
  std::is_class<T>::value ||
  std::is_null_pointer<T>::value,
  std::true_type, std::false_type>::type;
 
// similarly we can use: 
// std::is_enum<T>::value to detect enum or enum class

Detecting pairs and tuples is also easy, using a standard specialization pattern:

template <typename T>
struct is_pair : public std::false_type {};
 
template <typename T, typename U>
struct is_pair<std::pair<T, U>> : public std::true_type {};
 
template <typename T>
struct is_tuple : public std::false_type {};
 
template <typename... Ts>
struct is_tuple<std::tuple<Ts...>> : public std::true_type {};

A good setup so far. We can detect concrete types. Next, how to detect whether something already supports operator<<.