How to Take the Address of a Function Defined in Standard Library

Can I take the address of a function defined in standard library?

Short answer

No.

Explanation

[namespace.std] says:

Let F denote a standard library function ([global.functions]), a standard library static member function, or an instantiation of a standard library function template.
Unless F is designated an addressable function, the behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer to F.
[Note: Possible means of forming such pointers include application of the unary & operator ([expr.unary.op]), addressof ([specialized.addressof]), or a function-to-pointer standard conversion ([conv.func]).
— end note ]
Moreover, the behavior of a C++ program is unspecified (possibly ill-formed) if it attempts to form a reference to F or if it attempts to form a pointer-to-member designating either a standard library non-static member function ([member.functions]) or an instantiation of a standard library member function template.

With this in mind, let's check the two calls to std::invoke.

The first call

std::invoke(std::boolalpha, std::cout);

Here, we are attempting to form a pointer to std::boolalpha. Fortunately, [fmtflags.manip] saves the day:

Each function specified in this subclause is a designated addressable function ([namespace.std]).

And boolalpha is a function specified in this subclause.
Thus, this line is well-formed, and is equivalent to:

std::cout.setf(std::ios_base::boolalpha);

But why is that? Well, it is necessary for the following code:

std::cout << std::boolalpha;

The second call

std::cout << std::invoke(static_cast<ctype_func>(std::tolower), 'A') << "\n";

Unfortunately, [cctype.syn] says:

The contents and meaning of the header <cctype> are the same as the C standard library header <ctype.h>.

Nowhere is tolower explicitly designated an addressable function.

Therefore, the behavior of this C++ program is unspecified (possibly ill-formed), because it attempts to form a pointer to tolower, which is not designated an addressable function.

Conclusion

The expected output is not guaranteed.
In fact, the code is not even guaranteed to compile.


This also applies to member functions.
[namespace.std] doesn’t explicitly mention this, but it can be seen from [member.functions] that the behavior of a C++ program is unspecified (possibly ill-formed) if it attempts to take the address of a member function declared in the C++ standard library. Per [member.functions]/2:

For a non-virtual member function described in the C++ standard library, an implementation may declare a different set of member function signatures, provided that any call to the member function that would select an overload from the set of declarations described in this document behaves as if that overload were selected. [ Note: For instance, an implementation may add parameters with default values, or replace a member function with default arguments with two or more member functions with equivalent behavior, or add additional signatures for a member function name. — end note ]

And [expr.unary.op]/6:

The address of an overloaded function can be taken only in a context that uniquely determines which version of the overloaded function is referred to (see [over.over]). [ Note: Since the context might determine whether the operand is a static or non-static member function, the context can also affect whether the expression has type “pointer to function” or “pointer to member function”. — end note ]

Therefore, the behavior of a program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer to a member function in the C++ library.

(Thanks for the comment for pointing this out!)

Alternative to taking the address of a standard library function / possibly ill-formed behaviour

is there no safe way to call (in this example) toupper by pointer?

Not directly, only through one level of indirection (see below).

does a static_cast make the function pointer to a std lib function safe?

No. It can nail down an overload set to one particular function signature, but this doesn't have anything to do with whether you're allowed to take the address of that function.

Is there another way to achieve the below functionality via a single function with signature int fun(int)

There is an alternative, you can wrap the function call in two lambdas. This requires little change to the original snippet:

bool choice = true;
int (*fun)(int);

if (choice)
fun = [](int ch){ return std::toupper(ch); };
else
fun = [](int ch){ return std::tolower(ch); };

int t = fun('x');

This works nicely because both lambdas have no state and the same signature, so they implicitly convert to the function pointer.

Holding or passing around non-addressable-functions since C++20

This rule comes to us from P0551. The wording here is "unspecified (possibly ill-formed)" - not undefined behavior, not ill-formed NDR, nothing like that.

Now, the library is largely designed, specified, and implemented, around direct use of APIs. The library specifies what x.foo(y, z) means and the implementation has to follow that specification. But there are many ways that this could be implemented - maybe foo takes some extra default arguments, or can be a template, or is an overload set.

Moreover, maybe in C++N, there's just x.foo(y, z). But then in C++N+1, there's a new proposal that adds x.foo(y) too. For instance, in C++03 there was just the one vector::push_back but now there are two.

What is the reason for this new restriction in C++20?

The reason for the restriction (and it isn't really conceptually new, it's more that it's finally articulated) is to permit changing the standard library. These sorts of changes are only observable if you take the address of one of these functions - and it's basically the library saying that it doesn't care if these changes break your code because it's your fault, not the committee's/library's.

See also Standard Library Compatibility.

Isn't such a restriction breaking legacy code?

Not really. It's more heavily frowning upon code that does that and then not being concerned if any future changes might break it.

What is the right way since C++20 to hold or pass around non-addressable-functions?

Wrap them in a lambda. That lambda can even be stateless, which allows you to still convert it to a function pointer. It's still a function pointer, but it's insulated from any future standard library changes.

Why I can't take address of the std::addressof function

As you already hinted yourself, taking the address of std::addressof has unspecified behavior, and so may or may not work, because it is not designated as an addressable function in the standard.

More practically speaking though, the issue is that there are at least two overloads for std::addressof, both being templates with one template parameter.

One taking T& as a function parameter and the other taking const T&& as a function parameter (where T is the template parameter). The latter is defined as delete'd.

std::addressof<int> is not sufficient to decide which of these to choose.

The library implementation could also choose to implement these specified overloads in different ways, or add additional overloads, which is why taking the address is unspecified.

Where are the functions in the C standard library defined?

gcc comes with (binary) object files (not C source files) which contain implementations of all the standard C functions. When you use gcc to link object files into an executable file, the linker automatically includes the object files which implement the standard library functions. According to this thread, that standard object file will probably be called libc.a or libc.so.

Say you include a call to printf in your program. When the linker tries to resolve where that call should go, it will find the definition of printf in libc.a, and make your function call point there.

Look at http://gcc.gnu.org/onlinedocs/gcc/Link-Options.html and note the -nostdlib and -nodefaultlibs options. You can use these options to tell gcc's linker not to include the standard library object files by default.

How to pass a function template as a template argument?

You will have the same issue with other io manipulators that typically are functions that take the stream as parameter, when they are templates. Though you can wrap them in a non-template callable:

#include <iostream>

template<typename... Args>
void print(Args const&... args)
{
(std::cout << ... << args);
}

int main()
{
std::cout << 1 << 2 << 3 << std::endl; // ok
print(1, 2, 3); // ok
print(1, 2, 3, [](std::ostream& o) -> std::ostream&{
o << std::endl;
return o;
}); // no error!
}

Output:

123
123123

The syntax is rather heavy so you might want to use a helper type, though I'll leave it to you to write that (just joking, I don't think it is trivial, but I might give it a try later ;). After pondering about it for a while, I am almost certain that there are only the two alternatives: Instantiate the function (see other answer), or wrap the call inside a lambda, unless you want to write a wrapper for each single io manipulator of course.



Related Topics



Leave a reply



Submit