How to static cast throwing function pointer to noexcept in C++17? - c++

C++17 makes noexcept part of a function's type. It also allows implicit conversions from noexcept function pointers to potentially throwing function pointers.
void (*ptr_to_noexcept)() noexcept = nullptr;
void (*ptr_to_throwing)() = ptr_to_noexcept; // implicit conversion
http://eel.is/c++draft/expr.static.cast#7 says that static_cast can perform the inverse of such a conversion.
void (*noexcept_again)() noexcept = static_cast<void(*)() noexcept>(ptr_to_throwing);
Unfortunately, both GCC and clang tell me otherwise: https://godbolt.org/z/TgrL7q
What is the correct way to do this? Are reinterpret_cast and C style cast my only options?

You might have skipped over the important part:
The inverse of any standard conversion sequence not containing an lvalue-to-rvalue, array-to-pointer, function-to-pointer, null pointer, null member pointer, boolean, or function pointer conversion, can be performed explicitly using static_­cast.
Currently, a function pointer conversion includes only the conversion from noexcept to potentially throwing. Because you're doing the inverse of a function pointer conversion, static_cast will not work, just like you can't static_cast a pointer to an array, or any of the other conversions listed there.
So yes, reinterpret_cast would be appropriate and also raises the appropriate alarm bells that should come with discarding noexcept.

Related

Why can't I static_cast a void* to a pointer-to-function?

This can be compiled (despite being UB (right?) because fvp == nullptr)
int f;
void* fvp{};
decltype(f)* fp = static_cast<decltype(f)*>(fvp);
but this cannot
void f() {}
void* fvp{};
decltype(f)* fp = static_cast<decltype(f)*>(fvp);
because (Clang says)
Static_cast from 'void *' to 'decltype(f) ' (aka 'void ()()') is not allowed [bad_cxx_cast_generic]
Why is this the case? And where from the standard do I understand this? I guess [expr.static.cast] is where I should look at; specifically 7, but I'm not sure.
Follow up question on the reason why I wanted to ask this one.
This is not allowed because [expr.static_cast] does not allow it. Indeed, the relevant language is:
No other conversion shall be performed explicitly using a static_­cast.
Since there is no conversion listed that would permit conversion from void* to a function pointer type, that conversion is not allowed.
The rationale is that function and data pointers may have different sizes or alignment requirements.
You might consider a reinterpret_cast, which conditionally supports such conversions. But it would be better to use a design that does not require such conversions. If you need to type-erase a function pointer, you will still need reinterpret_cast but can use e.g. void(*)() as the erased type, which is always supported.
where from the standard do I understand this? I guess [expr.static.cast] is where I should look at; specifically 7, but I'm not sure
Basically, there is no guarantee for a full-round trip between void* and a function pointer.
This can be seen from expr.reinterpret.cast#8 which says:
Converting a function pointer to an object pointer type or vice versa is conditionally-supported. The meaning of such a conversion is implementation-defined, except that if an implementation supports conversions in both directions, converting a prvalue of one type to the other type and back, possibly with different cv-qualification, shall yield the original pointer value.

Difference between Function pointer casting in c and c++

The behavior of my code is different in c and c++.
void *(*funcPtr)() = dlsym(some symbol..) ; // (1) works in c but not c++
int (*funcPtr)();
*(void**)(&funcPtr) = dlsym(some symbol..) ; // (2) works in c++
I dont understand how the 2nd casting works in c++ while the 1st casting doesnt work in c++. The error message for (1) show is invalid conversion from void* to void*() in c++.
The problem is that dlsym returns a void* pointer. While in C, any such pointer is implicitly convertible into any other (object!) pointer type (for comparison: casting the result of malloc), this is not the case in C++ (here you need to cast the result of malloc).
For function pointers, though, this cast is not implicit even in C. Apparently, as your code compiles, your compiler added this implicit cast for function pointers, too, as a compiler extension (in consistency with object pointers); however, for being fully standard compliant, it actually should issue a diagnostic, e. g. a compiler warning, mandated by C17 6.5.4.3:
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.
But now instead of casting the target pointer, you rather should cast the result of dlsym into the appropriate function pointer:
int (*funcPtr)() = reinterpret_cast<int(*)()>(dlsym(some symbol..));
or simpler:
auto funcPtr = reinterpret_cast<int(*)()>(dlsym(some symbol..));
or even:
int (*funcPtr)() = reinterpret_cast<decltype(funcPtr)>(dlsym(some symbol..));
(Last one especially interesting if funcPtr has been declared previously.)
Additionally, you should prefer C++ over C style casts, see my variants above. You get more precise control over what type of cast actually occurs.
Side note: Have you noticed that the two function pointers declared in your question differ in return type (void* vs. int)? Additionally, a function not accepting any arguments in C needs to be declared as void f(void); accordingly, function pointers: void*(*f)(void). C++ allows usage of void for compatibility, while skipping void in C has totally different meaning: The function could accept anything, you need to know from elsewhere (documentation) how many arguments of which type actually can be passed to.
Strictly speaking, none of this code is valid in either language.
void *(*funcPtr)() = dlsym(some symbol..) ;
dlsym returns type void* so this is not valid in either language. The C standard only allows for implicit conversions between void* and pointers to object type, see C17 6.3.2.3:
A pointer to void may be converted to or from a pointer to any object type. A pointer to
any object type may be converted to a pointer to void and back again; the result shall
compare equal to the original pointer.
Specifically, your code is a language violation of one of the rules of simple assignment, C17 6.5.16.1, emphasis mine:
the left operand has atomic, qualified, or unqualified pointer type, and (considering
the type the left operand would have after lvalue conversion) one operand is a pointer
to an object type, and the other is a pointer to a qualified or unqualified version of
void, and the type pointed to by the left has all the qualifiers of the type pointed to
by the right;
The reason why it might compile on certain compilers is overly lax compiler settings. If you are for example using gcc, you need to compile with gcc -std=c17 -pedantic-errors to get a language compliant compiler, otherwise it defaults to the non-standard "gnu11" language.
int (*funcPtr)();
*(void**)(&funcPtr) = dlsym(some symbol..) ; // (2) works in c++
Here you explicitly force the function pointer to become type void**. The cast is fine syntax-wise, but this may or may not be a valid pointer conversion on the specific compiler. Again, conversions between object pointers and function pointers are not supported by the C or C++ standard, but you are relying on non-standard extensions. Formally this code invokes undefined behavior in both C and C++.
In practice, lots of compilers have well-defined behavior here, because most systems have the same representation of object pointers and function pointers.
Given that the conversion is valid, you can of course de-reference a void** to get a void* and then assign another void* to it.

Should I use static_cast in assignments and return statements and why?

Here are two very similar snippets:
vector<int> a;
int n = static_cast<int>(a.size());
// ---------
int f(const vector<int>& a) {
return static_cast<int>(a.size());
}
Here I explicitly cast a value of type size_t to type int. If I omit static_cast then the same cast applies implicitly.
Of which kind would this implicit cast be? Is it safe to omit static_cast in explicit assignments and return statements?
This would be an integral conversion. And a narrowing one at that.
For the variable initialization, if you want a specific type, a good approach is to do what Herb Sutter suggests in his "Almost Always Auto" GotW article:
auto n = int{a.size()};
Use list initialization whenever possible. It will prevent narrowing conversions and you'll be flagged by your compiler to use an explicit cast when required (such as the case above).
The necessarily of casting is determined by the necessarily of conversion. So the real question here should "be why would I return / store an int instead of vector<int>::size_type?" If your program logic doesn't really require such conversion then don't perform it at all. If your program logic requires such conversion (for example you need to pass this value into a call to a third-party function that accepts int) then you should use static_cast. Omitting static_cast in this case would be a sign of unintended narrowing conversion and will trigger corresponding compiler warnings, such as warning C4267: 'initializing': conversion from 'size_t' to 'int', possible loss of data or warning: conversion to 'int' from 'std::vector<int>::size_type {aka long unsigned int}' may alter its value [-Wconversion]
This cast is not that safe, actualy the value of n may be implementation defined (c++ standard [conv.integral]):
If the destination type is signed, the value is unchanged if it can be represented in the destination type otherwise, the value is implementation-defined.
If you enable all warning, and don't use the static_cast, your compiler may inform you about a narrowing conversion. If you use the static_cast, you inform reader of your code that you know for sure that a.size() <= std::numeric_limits<int>::max() or that you know what your implementation is going to do if such a condition does not hold.
(notice that it could be possible that previous comparison also invokes implementation defined conversion if std::size_t is smaller than int, the c++ standard allowes it)

Why would you convert to a void pointer?

I saw the following code in this question:
class CFoo
{
int a;
public:
CFoo():a(1){}
~CFoo(){}
getNum(){return a;}
};
void tfunc(void* data)
{
CFoo* foo = static_cast<CFoo*>(data);
std::cout << "Number: " << foo->getNum();
delete foo;
}
int main()
{
CFoo* foo = new CFoo;
void* dt = static_cast<void*>(foo);
tfunc(dt); // or tfunc(static_cast<void*>(food));
return 0;
}
and started to wonder about why you would convert a pointer to a type to a pointer to void instead of simply assign the void pointer the the actual pointer. Like when calling tfunc in the code code above, he could have called it like tfunc(&foo) instead of converting the pointer to a type to void pointer as he did using the static cast static_cast<void*>(foo);, right?
The wording of your question is a bit confusing, you are not actually asking why converting to void*, you are asking why use explicit casting instead of rely on implicit casting, i.e.
tfunc(static_cast<void*>(food)); // explicit casting
vs
tfunc(food); // implicit casting
The issue is a bit more general than casting to void*. Unfortunately C++ allows a fair amount of implicit dangerous castings. E.g. between singed and unsigned, from a wider integer or floating point to a narrower one, conversions to bool and conversions to void*. All this conversions have the potential to silently introduce bugs in the code. c++11 did a little step in the right direction by not allowing narrowing in the new uniform initializer syntax {}, but due to backward compatibility all previous implicit casts are still allowed.
That's why explicit casting is encouraged. It helps showing the intent of the writer. It shows that the conversion is explicitly desired, as opposed to happening silently, possibly without the author knowledge. Also very important, it helps on code review or when reading the code by being a flag sort of "Here there is cast. Be aware of that when you look over this code"
From the example above I do not understand why the signature of function tfunc is void tfunc(void* data) and not void tfunc(class CFoo* data).
Anyway, it is valid to convert a pointer to any type T into a void *, and it is valid to convert such a pointer back to T* later. The conversion from T* can be implicit, whereas conversion from void* back to T* needs to be explicit. So, in your example, tfunc(foo) is equivalent to tfunc(static_cast<void*>(foo)) and to tfunc(dt).
Concerning the point "conversion of CFoo* to void*, there is a standard conversion (cf. this c++ online draft standard):
4 (1) Standard conversions are implicit conversions with built-in
meaning. Clause 4 enumerates the full set of such conversions.
4.10 Pointer conversions (2) A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type
“pointer to cv void”. The result of converting a non-null pointer
value of a pointer to object type to a “pointer to cv void” represents
the address of the same byte in memory as the original pointer value.
...
I'd prefer the implicit conversion over explicit conversions, as the the built-in meaning of the (implicit) standard conversion meets exactly the requirements.
A use case for void* in a function signature could be that one wants to pass objects of unrelated type and decide inside the function to which type it has to be casted back. Suppose, for example, a function handling callbacks for different response types, and each response type might pass different (class hierarchically independent) objects:
void someCallback(int responseType, void *context) {
if(responseType == 1) {
CFoo1* foo1 = static_cast<CFoo1*>(context);
...
}
else {
CFoo2* foo2 = static_cast<CFoo2*>(context);
...
}
}

conversion for void* as a method argument

For a method with an argument of void* is C++ employing static/reinterpret_cast to make the conversion or is there a different mechanism in play here?
void foo(void* p)
{
// ... use p by casting it back to Base first, using static/reinterpret cast
}
Base* base(new Derived);
foo(base); // at this exact line, is there a static/reinterpret_cast going on under the covers?
I am asking because it seems that on one hand the standard says that for c-style cast, C++ will go and try a C++ cast (static, reinterpret, const) until something that works is found. However I can't find a reasonable explanation as to what goes on when a method with a void* argument is called. On the face of thing there is no cast, so what happens?
The language specification does not express the behavior in terms of static_cast or reinterpret_cast in this case. It just says that the pointer base is implicitly converted to void * type. Conversions that can be performed implicitly are called standard conversions in C++. Conversion of any object pointer type to void * is one of the standard conversions from pointer conversions category.
It is true that this is the same conversion that can be preformed explicitly by static_cast, but static_cast is completely irrelevant in this case. Standard conversions in C++ work "by themselves" without involving any specific cast operators under the cover.
In fact, it is the behavior of static_cast that is defined in terms of standard conversions for such cases, not the other way around.