Jonathan Boccara's blog

Function Aliases In C++

Published October 27, 2017 - 10 Comments

Daily C++

One thing that dramatically improves the expressiveness of a piece of code is respecting its levels of abstractions.

It sometimes involves massive refactorings, but in many cases it just comes down to choosing good names in your code. Picking a name that is consistent with the abstraction level of the surrounding code can make a difference between making a reader scratch their head and making them… read on.

For this reason, today I’d like to share with you a feature made usable by C++11, that I think didn’t get as much credit as it deserves: function aliases.

C++03: one-liners

Can you see a case where it’s useful to define a function that has just one line of code? And what if that line is just forwarding the parameters to another function?

int f(int parameter)
{
    return g(parameter);
}

This seems pointless: why not call g directly, instead of calling f?

In fact this can be useful, in the case where the name of g isn’t meaningful in the call site. Introducing f gives your call site a way to read” f” instead of “g“.

Also, it decouples the call site from g, which becomes useful if we want to replace g with something else and there are multiple places in the code where it was called. When you use f you only need to change it at one place: inside f.

Here is an example, adapted from the excellent book Code Complete from Steve McConnell. Let’s take a function that generates a new Id. It happens that right now, this Id is generated by the database. But this could change in the future. So if we have a function newIdFromDatabase() it would be worth considering wrapping it into another function, that only mention that we’re getting a new Id:

int newId()
{
    return newIdFromDatabase();
}

This way:

  • we could redirect newId to something else without changing all the places that use it,
  • the calling code does not read any mention of the database, which makes it that much clearer because it keeps this lower level of abstraction hidden.

However, this approach also presents several drawbacks:

  • it could make extra copies if ever the function is not inlined,
  • it can take an annoying amount of code if there are several parameters to be passed down to the low-level function,
  • when debugging, it’s an extra step you need to step through.

This is where function aliases come into play.

C++11: function aliases

C++11 provides another approach for this:

const auto newId = newIdFromDatabase;

This solves most of the above drawbacks: it makes no extra copy, since calling newId is calling newIdFromDatabase, and the declaration is quite straightforward.

EDIT: Note the const! As Stephan T. Lavavej pointed out, having a bare auto newId = newIdFromDatabase would be dangerous because the function pointer newId could be changed and point to something else. It would be like a global variable, but in the form of a function. Very complex and bug-prone.

Here, newId was a function pointer. We could also define it a function reference:

auto& newId = newIdFromDatabase;

In this case we no longer needs a const because this function reference, like all references, can’t be reassigned. But the resulting declaration looks a bit weird. A big thank you to Stephan for those observations.

Note that you don’t even need to have the full definition of newIdFromDatabase available at the point of the function alias declaration. Only its declaration needs to be visible from it. Indeed, the actual resolution is made by the linker, like with any other function.

EDIT: Note that newId is not strictly a function alias since there is no such thing in C++, but a function pointer that semantically plays the role of an alias here.

Let’s mention that C++98 could achieve an roughly equivalent result, as it could manipulate functions:

typedef int (&IdFunction)();
IdFunction newId = newIdFromDatabase;

But the syntax was really not natural to read, and you can imagine that it doesn’t get better when there are more arguments. The real new feature that enables practical function aliasing here is auto.

Note that while one-liners added an extra step in debugging, this approach kind of removes one step. Indeed, when you step in newId at call site, you directly fall into newIdFromDatabase and you don’t even see newId in the call stack. This can be disturbing. Another drawback is that since newId is a function reference, it will not be inlined.

C++14: template function aliases

What if we want to alias a template function?

template<typename T>
void g(T)
{
}

Can we just write:

template<typename T>
const auto f = g<T>;

In C++11 no. In C++14, yes.

The feature of C++14 that enables to do this is variable templates. Indeed, in C++98 only types and functions could be templates. C++11 allowed using declarations to be template too, and C++14 allows values to be templates. Those are called variable templates.

Type deduction didn’t follow

In C++, template functions operate a deduction on the type of their parameters. For example, with the following call:

g(42);

we don’t need to specify that T is int. The compiler deduces it automatically. Read Item 1 of Effective Modern C++ to know exactly how that deduction works.

But the thing is, template functions aliases don’t do type deduction. So to call them you need to specify the template types explicitly, even if the parameters carry all the necessary information to deduce them:

f<int>(42);

This seems like a serious limitation to me (imagine how it would look like on something equivalent to an STL algorithm?), since it hinders readability and it was one of the main advantages we mentioned at the beginning of this article.

There is a workaround to this. I can’t say I like it very much but let’s put it for the sake of comprehensiveness. It consists in using a macro to generate the wrapping function:

#define ALIAS_TEMPLATE_FUNCTION(highLevelF, lowLevelF) \
template<typename... Args> \
inline auto highLevelF(Args&&... args) -> decltype(lowLevelF(std::forward<Args>(args)...)) \
{ \
    return lowLevelF(std::forward<Args>(args)...); \
}

You can then define the “alias”:

ALIAS_TEMPLATE_FUNCTION(f, g)

and since it creates a normal template function, type deduction works normally:

f(42);

And it also has the advantage of preserving the possibility to inline the code inside the lower level function.

EDIT: Security

Stephan also pointed out a downside of function pointers: long-lived function pointers can be a target for security exploits.

My understanding of this exploit is that if a malevolant agent can figure out the value of that pointer, then they would know a memory address that the application is likely to call. They could then replace the code at that address any code, and have it executed. Microsoft uses EncodePointer to protect function pointers and prevent this kind of attack.

Aliasing, aliasing, aliasing

I’ve presented the different ways I know to alias a function in C++, with their advantages and drawbacks. Don’t hesitate to put a comment if you see something missing in this presentation.

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin

Comments are closed