c++ · comma-operator · esoteric · python · templates · variadic-templates

The usefully esoteric

Today was the day to spend half an hour playing around with variadic templates, and I learned a lot, though not so much about variadic templates. To me this is the real purpose about messing around with language features you haven’t used much. Sure you might have the best intentions of really digging down into that blog post of ten or fifteen compelling examples…but then you see a novel keyword and it’s all off as you dive after that keyword, which has suddenly become so much more interesting than the useful thing you thought you’d learn that morning.

Case in point, I wanted to use variadic templates to concatenate vectors. I started by concatenating a global vectors within the variadic template…easy. Then I wanted to return a vector initialized within the templated function…also no problem and done just as one would expect. Then I ran a performance test of these vs. a braced initializer list, and the braced initializer list won by six orders of magnitude in speed.

So I went looking on Stack Overflow to see what magic was on offer there. Sometimes I know what I’m looking for is non-sensical. You can’t beat initializer lists, I imagine, but usually with C++ there’s a lot of variety, and I wanted to see it. I don’t know that a question about concatenating vectors with variadic templates is all that esoteric, but the syntax that came out of the question stumped me :

template<typename T>
void append_to_vector(std::vector<T>& v1, const std::vector<T>& v2) {
std::cout << v2[0] << std::endl;
for (auto& e : v2) v1.push_back(e);
}
template<typename T, typename... A>
std::vector<T> concat(std::vector<T> v1, const A&... vr) {
int unpack[] { (append_to_vector(v1, vr), 1)... };
(void(unpack));
return v1;
}

What the heck was going on with these two lines?


int unpack[] { (append_to_vector(v1, vr), 1)... };
(void(unpack));

An interlude into the comma operator and parallels with Python

The explanation of exactly what is happening is best left to the kind SO response to my initial inquiry as to what the heck the code meant, but it made me notice the comma operator, which I haven’t done in a long time. Per cplusplus.com,

The comma operator (,) is used to separate two or more expressions that are included where only one expression is expected.

That description is straightforward, and the usefulness of this operator becomes obvious when you see it in action. Consider this expression:

i = (1, 2, 3, (f()), 4);

This will run f() (or anything else you stuff in the list), and then assign 4 to i. This expression also uses the comma operator but you’ll get a slightly different result:

i = 1, 2, 3, (f()), 4;

This will also run f() but will return 1 because the assignment operator takes precedence over the comma operator (the latter wants to return the rightmost value). That doesn’t mean rest of the parameters in this comma-separated list are ignored but only that the return statement changes. So in this line of code what’s actually going on is parentheses to cordon off each use of the comma operator:

To me the comma operator is a great example of Pythonic C++. Note the comma operator Interestingly this is not a case of C++ becoming more Python because Python doesn’t have a comma operator. What Python has if you want to execute a function and assign at the same time is two options. First you can do the following:

_, i = f(), 4

Secondly you can use the or operator if f returns False or doesn’t return anything since Python will evaluate f() but then keep on going since f() is not True.

i = f() or 4

This is because so long as f() doesn’t return anything, f is returning None, which is evaluated to false.

Back to C++

So this all comes down to using the comma operator in a clever way to run the templated function while unpacking the variadic template parameters. Notice that the function called is templated, so you can expand it to handle a jumble of parameters. I did that by adding the following to the code above:

template<typename T, typename S>
void append_to_vector(std::vector<T>& v1, S v2) {
std::cout << "got an int instead of a vector" << std::endl;
}
template<typename S, typename T>
void append_to_vector(S v1, std::vector<T>& v2) {
std::cout << "got an int instead of a vector" << std::endl;
}
template<typename S, typename T>
void append_to_vector(S v1, T v2) {
std::cout << "got two non-vectors" << std::endl;
}

So you can take advantage of variadic templates in a way that isn’t just treating them like compile-time containers. You can throw parameter lists like the following at concat:

std::vector<std::vector<int>> VV;
VV.emplace_back(std::initializer_list<int>{9, 9, 9, 9, 9, 9});
VV.emplace_back(std::initializer_list<int>{1, 2, 3});
VV.emplace_back(std::initializer_list<int>{5, 5, 5});
auto bigV = concat( VV[0], 3, 4, VV[1], VV[2]);

I don’t have a compelling use case right now, but I hope I’ll eventually get back to you with one. For the time being, I’m glad I went off course to see fun uses of the comma operator. As usual, the code’s on Github.

Final esoterica

I’ll add two things I also learned wandering around when I ought to have busted through some straightforward variadic templates examples.

  1.  I found out about fold expressions, which are quite elegant, very Pythonic, and likely to make variadic templates even easier and more expressive.
  2. I discovered that the name “Arena” designates a memory management class that ignores deallocation requests. I had come across Arena when learning how to use a custom allocator to lay out vectors contiguously. I’d thought the name was a bit strange but reused it in deference to the example I built upon. Now I know I should change that name because my class deallocates memory as well as allocating it.

Still want more esoterica?

If you’re interested in more esoterica for the day, check out Evan Wallace’s collection of obscure C++ features. If I come across other nice posts, I’ll be sure to add them.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s