C++ std::apply made usable

So little C++ so much good!

std::apply is a typical example of those numerous standard C++ std lib types begging to be made comfortable.  Not just usable.

In its raw form, it requires a lot of typing. And a lot of typing means a lot of bugs. Made by the team of course, not you. So, I made one little proxy type aka “wrapper” that is really simple but makes std::apply usage, much more palatable. And safe.

The Use Case

Requirement: There exists a variadic “summa” function. It simply adds together all of its arguments and returns the result. The types of arguments must be allowed to be “anything”.

I think Bjarne thinks: “the ability to think abstract” is the key. The first abstraction I will produce is this simple callable object. It is indeed recursive but does not have to be.

( summa() as lambda is possible and is presented here).

Usage is simple. But. It works for use cases only when all the arguments are of the same type.

And here is the cruel last-minute requirement, you already might have sensed: That callable object is not owned by you or your team. It is in the library your company has paid for and it can not be changed.

Not a problem says you, we will use std::apply. But you are smart and you do not want to use std::apply in its native form.

The Solution

OK, let me show you immediately the API, I have developed. It is not made of function calls, it is made of one abstraction.

To use the above callable object (or any other callable object), we would need first to “runtime package” it as “applicator”. And in that sense, we do not need to own it.  Instead, we will use a wrapper aka “proxy” to make the whole business of using std::apply much easier to comprehend and use. Your fellow developers aka users will never need to see the implementation, they will just use the applicator. In that sense, it is a “proxy” object.

The design and API usage philosophy is: you first make a specific “applicator” from a specific Callable object. Then you use it, wherever and whenever you need it.

The key difference vs the naked summa(...) usage is you can use all these fancy types, std::apply allows you to use. Thus anything that behaves like a tuple: std::pair, std::array , and on top of that some more types thanks to this proxy API.

Ok, but is this really usable? How the code using it feels and looks. Read on.

Before and After

For the “before and after”  comparisons, to each API example below, I have added std::apply raw usage, on the line below.

Perhaps not a big deal. Shorter; but just slightly shorter. Ok, how about using tuples (as authors of std::apply intended), as the primary and only argument type to be passed  to std::apply. Here is the difference.

The key requirement and its solution

That’s a big difference in ease of usage. Obviously one does depend on the existence of the required “+” operator for sumator to work:

Obviously, you can create the necessary operator yourself:

Adding int and string will now work. Silly but true.

Keep in mind this is a silly use case. It just proves the API concept. The same API will work with anything that is an invocable object. Without needing to change that object. It makes the callable object readily and truly reusable.  In a non-intrusive way.

More arg types

And then there are arg types that my API and its proxy can do, and that std::apply can not do on its own:

Initializer list’s, std::apply simply does not let you use. And what about native arrays?

I know std::array is rather nice, and I use it too whenever I can, but in real-life code, native arrays no one can avoid. An ocean of them native arrays in fact, all used by legacy API.  Instead of writing your own transformations, feel free to use my API.

There are more perhaps convincing types of examples.  What about F( a, b,  F(c, d)) situation? Function result used as one of the arguments to the same function. Let’s see:

Caveat Emptor

One can attempt this kind of easy computations with or without the help of my API. I might think if you need to use std::apply, this API you might find rather useful.

The primary purpose of this API is to encapsulate and add value to std::apply. That in turn improves the resiliency of your code (projects) as it will not be sprinkled with ad-hoc usage very likely in concert with some bugs.

You can even use it as a blueprint to your own proxy object that will also enforce some additional business rules. The key thing is you have it and thus you can use it.

The Free Code

Is rather simple and lovingly short. Here is the full implementation straight from the Godbolt. There is no magic.

summator is proxy, using std::apply to invoke summa(...). The output produced is:

To casual C++ blog readers, this might look silly.  But if you find yourself you really need std::apply, that API is probably far from silly.

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: