C++11: How to alias a function? [duplicate]
Asked Answered
C

7

91

If I have a class Foo in namespace bar:

namespace bar
{
    class Foo { ... }
};

I can then:

using Baz = bar::Foo;

and now it is just like I defined the class in my namespace with the name Baz.

Is it possible to do the same for functions?

namespace bar
{
    void f();
}

And then:

using g = bar::f; // error: ‘f’ in namespace ‘bar’ does not name a type

What is the cleanest way to do this?

The solution should also hold for template functions.

Definition: If some entity B is an alias of A, than if any or all usages (not declarations or definitions of course) of A are replaced by B in the source code than the (stripped) generated code remains the same. For example typedef A B is an alias. #define B A is an alias (at least). T& B = A is not an alias, B can effectively implemented as an indirect pointer, wheres an "unaliased" A can use "immediate semantics".

Coan answered 25/3, 2012 at 21:12 Comment(11)
what is it that you want to achieve? If you state what you actually need it might be possible to provide alternatives, but as it is, it is not even clear what you mean by aliasWillardwillcox
@DavidRodríguez-dribeas: There is little confusion over what "alias" means in the above. In general if B is an alias of A, than if you replace usages of A with B, than the generated code remains unchanged. Why you would want this is also straightforward. I want to give a library function a second name/namespace. I suspect the cleanest way is to just wrap a call to the old name with an always_inline function of the new name. The wrapper will be compiled out, leaving something indistinguishable from a direct call to the old name, as desired.Coan
Well, it is still not clear, for example, whether a function pointer defined in the destination namespace meets your requirements or not. It is not the same function but your program can go along without ever noticing (there are situations where it matters, but they are not that common). Additionally, you did not state whether you need a different name or just a different namespace, so a using-declaration could also meet your needs... Remember to ask about what you need, as that is what enables uses of different alternatives that might not exactly fit your textual description.Willardwillcox
@DavidRodríguez-dribeas: A function pointer would produce different code than a normal function call as the function pointer needs to be dereferenced before the call.Coan
Do you mean c++ code or the generated code? On the C++ side, source code, the function will be automatically be dereferenced, in the generated binary it will be different. That is why it is important to state your requirements, which is what I am trying to get through: state the requirements.Willardwillcox
@DavidRodríguez-dribeas: Once again, Generated Code. See my second comment above where I define what an alias means.Coan
We are having quite a bit of misunderstanding. I am probably not being as clear as I wanted, but let's try again: When you ask a question you should make clear what your requirements are. Not as a comment, but rather as part of the question, there are some things that seem obvious to you (alias == same generated code) that are not clear as alias does not have a precise definition --of which I provided other possible interpretations. Also note that the solution that you seem to prefer does not guarantee the same exact binary code.Willardwillcox
@DavidRodríguez-dribeas: Under what circumstances would the solution of wrapping the function call in an inline function not produce the same generated code?Coan
inline does not force inlining, only suggests. Even if you force inlining through compiler specifics, the compiler might generate a non-inline version of the wrapper (gcc does, even when not used --i.e. the address is not taken-- in code)Willardwillcox
@DavidRodríguez-dribeas: As I said "always_inline" refering to the attribute(s) that force inlining. See section 6.39 An Inline Function is As Fast As a Macro in the GCC manual.Coan
This doesn't answer your question given current C++, but if/when the "Generalizing alias declarations" proposal p0945r0 is accepted, you will be able to say exactly that, using g = bar::f;.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0945r0.html (wg21.link/p0945r0)Elmerelmina
D
82

You can define a function alias (with some work) using perfect forwarding:

template <typename... Args>
auto g(Args&&... args) -> decltype(f(std::forward<Args>(args)...)) {
  return f(std::forward<Args>(args)...);
}

This solution does apply even if f is overloaded and/or a function template.

Domitian answered 25/3, 2012 at 21:58 Comment(9)
It doesn't apply if f is a function template that can't use type deduction.Configuration
@R.MartinhoFernandes: Can you provide an example of such a function template that can't use type deduction?Coan
@user1131467 Any function that uses a template argument to determine its return type (like boost::lexical_cast), or uses a template argument as a de facto argument (like std::get).Configuration
@RMartin if it would assume "ExplicitArgs" to be empty if you don't specify it could rewrite it to template<typename ...ExplicitArgs, typename ...Args> void foo(Args...args) and call the other one like f<ExplicitArgs...>(args...). Unfortunately it only does the "assume-to-be-empty" for a trailing template parameter pack, and not for the one-before-trailing one.Bagasse
@RMartin actually GCC and Clang already do exactly that, so that the above ExplicitArgs way even works in the wild! They consider both template parameter packs "trailing".Bagasse
The correct and shorter answer which also avoids the problems of ExplicitArgs is given by Jason Haslam below!Demolition
This drops noexcept information and so is not a precise alias in C++17 anymore: coliru.stacked-crooked.com/a/f3ee222a98c97184Asseveration
This answer is partially wrong - it is only a workaround for lack of function aliases in C++. What is more, calling g instead of f may break the ABI, especially if both f and g are a strong symbols.Overstrain
How do I use this?Ciaphus
S
67

The constexpr function pointer can be used as a function alias.

namespace bar
{
    int f();
}

constexpr auto g = bar::f;

It is highly likely (but not guaranteed by the language) that using g uses bar::f directly. Specifically, this depends on compiler version and optimization level.

In particular, this is the case for:

  • GCC 4.7.1+, without optimization,
  • Clang 3.1+, without optimization,
  • MSVC 19.14+, with optimization.

See assembly generated by these compilers.

Subzero answered 13/4, 2018 at 14:39 Comment(4)
This is the best answer, but you should add that this is not a real alias - compiler actually is allowed to not inline the function call and make a real call. But I believe most compilers will optimize it out, like Compiler Explorer shown.Overstrain
@jaskmar, I updated the answer assuming you meant indirect/direct call instead of inlining. Inlining is not relevant here.Housemaster
Nice solution. But it does not work for template and overloaded functions.Mixie
In my case (gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)) i had to add & as following: constexpr auto g = &bar::f;.Annabelannabela
A
27

Classes are types, so they can be aliased with typedef and using (in C++11).

Functions are much more like objects, so there's no mechanism to alias them. At best you could use function pointers or function references:

void (*g)() = &bar::f;
void (&h)() = bar::f;

g();
h();

In the same vein, there's no mechanism for aliasing variables (short of through pointers or references).

Agiotage answered 25/3, 2012 at 21:19 Comment(13)
So what is going on here then: auto g = bar::f; ?Coan
@user1131467: You declare and define a new object.Agiotage
What is the type of that object? And what is going on here: inline void g() { bar::f(); }Coan
Bashphorism 2 alert?! That last one defines an entirely new function.Agiotage
What's bashphorism 2? No wait, why is the sky blue? No wait, why is steam coming out your ears? No wait, why are you picking up that axe?Coan
When defining aliases, I usually like to see them made const. Otherwise a great workaround. This still is quite different from using, however, because it neither pulls in multiple overloads, nor overloads with already-visible definitions with the same name.Godson
IIRC, function references work better than function pointers. (but it's been a while since I was playing with this, and I don't know if function references are portable either)Ramiform
@Hurkyl: There's no real difference between function references and pointers, as the former always immediately decay into the latter. You can say foo(), (*foo)() and (**foo)() as much as you like.Agiotage
@Kerrek: I recall seeing performance differences when I was testing it. Unfortunately, I cannot recall what precise variations I was experimenting with.Ramiform
@Kerrek if you say &foo, then you get the address of the function pointer instead of the function. If foo is a reference, this would work correctly and yield the address of the function.Bagasse
@JohannesSchaub-litb: It's true that there's a function reference somewhere, but it decays to a function pointer very easily, to the extend that you can keep dereferencing, as each reference immediately decays to a pointer again.Agiotage
@KerrekSB I am saying that if you have void f(); void (&rf)() = f; then both &f and &rf yield the address of f. But if rf would be a function pointer, void (*rf)() = f;, then &rf returns its address rather than that of the function, which is bad.Bagasse
@JohannesSchaub-litb: oh, now I get it. Yes, you're right, references are better in that regard.Agiotage
O
22

It's possible to introduce the function into a different scope without changing its name. That means that you can alias a function with a different qualified name:

namespace bar {
  void f();
}

namespace baz {
  using bar::f;
}

void foo() {
  baz::f();
}
Ovotestis answered 17/2, 2016 at 21:6 Comment(5)
I don't know why this isn't the accepted answer: I just tried it and it works, even for template functions (without needing to be prefixed with template <typename> to do so). As far as I can tell it pulls in all of the overloads for the name.Liripipe
This is the best solution, but does not work for inlines: namespace bar { static inline __attribute__((always_inline)) void f(); } namespace baz { using bar::f; } void foo() { baz::f(); } $ g++ -g -std=gnu++11 -x c++ -Wall -Wextra -c tns.C x.C:3:8: warning: inline function 'void bar::f()' used but never defined void f(); ^ tns.C: In function 'void foo()': tns.C:3:8: error: inlining failed in call to always_inline '...': function body not available x.C:11:11: error: called from here baz::f();Receivership
namespace bar { static inline __attribute__((always_inline)) void f(); } namespace baz { using bar::f; } void foo() { baz::f(); }Receivership
This does not answer the question because you can't give the function a different name. f remains f, even though it is in different namespace. Using function under different name is what alias is supposed to be.Mixie
Also, you can't use this solution for static functions inside class. Only for functions inside namespaces. This is not required in the question. But I need this anyway.Mixie
D
17

Absolutely:

#include <iostream>

namespace Bar
{
   void test()
   {
      std::cout << "Test\n";
   }


   template<typename T>
   void test2(T const& a)
   {
      std::cout << "Test: " << a << std::endl;
   }
}

void (&alias)()        = Bar::test;
void (&a2)(int const&) = Bar::test2<int>;

int main()
{
    Bar::test();
    alias();
    a2(3);
}

Try:

> g++ a.cpp
> ./a.out
Test
Test
Test: 3
>

A reference is an alias to an existing object.
I just created a reference to a function. The reference can be used in exactly the same way as the original object.

Diction answered 26/3, 2012 at 0:30 Comment(9)
Doesn't this require an extra dereference when alias is called? Also how does this work for template functions?Coan
AS you can see it works fine. With templates it works for specific instantiations you can not alias a template but you can alias a specific version of a template.Diction
A better idiom for this is: constexpr auto &alias = Bar::test;. The constexpr part guarantees that the reference will be substituted at compile-time. The auto part won't work for a templated function, though.Hubey
"A reference is an alias to an existing object. I just created a reference to a function." -- and functions are not objects. So references-to-function are not an alias to an existing object (just as function pointers are not object pointers) :-)Ranite
@SteveJessop: What is an object? Is it not something that takes up space in memory? A function occupies space in memory and has an address. So why is it not an object? (At a conceptual level (probably not a language level)).Diction
@LokiAstari: OK, if you want to use "object" to mean something conceptual, that's different from what it says it means in the standard, that's fine I guess. It will cause confusion among people like me, though who are used to using the language-level definition when talking about C++ :-)Ranite
@SteveJessop: I was trying to use the language level definition. But obviously (as you state) function does not fit that definition in C++. But alias is not a part of the language definition either. Have you a suggestion for better wordage of the above?Diction
@LokiAstari: I think I've seen "entity" used to mean, roughly, "an object, function, class or member". But of course you can't take a reference to a class, and there's no reference-to-member either. So unfortunately the best term I can think of that's precise, is "object or function". Anyway the questioner is (it turns out) using a special meaning of "alias" and it's not necessarily true that a reference is an alias by that definition, although sometimes it is...Ranite
@RichardSmith you should convert that comment to an answer - it's great!Shirty
N
9

It's not standard C++, but most compilers provide a way of doing this. With GCC you can do this:

void f () __attribute__ ((weak, alias ("__f")));

This creates the symbol f as an alias for __f. With VC++ you do the same thing this way:

#pragma comment(linker, "/export:f=__f")
Narco answered 16/7, 2014 at 0:28 Comment(8)
And will this work with overloaded functions, templates, everything?Eberle
@Eberle Yes - ish. You can make the compiler emit an alias for any concrete function that it generates - they're just entry points in an executable object with names assigned to them, after all. Overloaded functions are just functions, with the name mangled to reflect the function signature. Template functions don't have executable names themselves, but instantiations of those template functions do - and the compiler can emit aliases for them. Whether it's much use to you is another matter.Narco
One difficulty with this is that you must use the mangled symbol name for the function in the alias( ) declaration. Your example looks wrong (for this reason); or even if it's correct on some system, it will be much more complicated for a function with parameters..Photic
@Davmac: only if you want the alias to be a mangled name. There is nothing wrong with creating an unmangled alias, though it will be of more use from languages other than C++.Narco
@Narco I'm not saying the alias will be mangled (or not). It is the symbol that you want to alias to that is problematic. If it is mangled then you must use the mangled name to create the alias. The question asks about creating an alias for bar::foo so you would have to use the mangled name of that symbol when creating the alias. Eg it won't be "__foo", more like "_ZN3bar3fooEv".Photic
Ah, yes, true in VC++. The GCC method doesn't require you to name the original symbol, though.Narco
@Tom, yes it does. That's what the "__f" (which should be "_ZN3bar3fooEv") is.Photic
Yep, quite right; in my GCC 9.2.1 this is void f() __attribute__ ((weak, alias("_ZN3bar1fEv")));Narco
K
2

You can use good old macros

namespace bar
{
    void f();
}

#define f bar::f

int main()
{
    f();
}
Kursh answered 2/11, 2017 at 17:7 Comment(4)
This can have very unexpected effects depending on the name of the macro. See this answer for reasons (especially the "Macros have no namespace" is dangerous here).Stakeout
Macros are old, but nowhere near good in C++.Overstrain
You need to take care when using macroses obviously. Every decent C++ programmer knows that. I would not recommend to define such a macro in a header. But in source file it should be fine. With macroses you can always shoot yourself in the foot. If your technology stack permits you probably should use Paweł Bylica's answer. But for older compilers you can consider this solution to ease your life a little bit.Kursh
I no longer like macros. Enums instead of defines; constexpr functions instead of macros; inline instead of macros; templates instead of macros. Only macros I used are includes and conditional header control.Meddle

© 2022 - 2024 — McMap. All rights reserved.