Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting between function pointer types

I have a program that has to store functions as void (*) (void*). Creating functions with that signature leads to code duplication (usually the first line is to cast the void pointer to the correct type) and reduces type safety (the void pointer can be cast to anything, such as the wrong type), so I was wondering if I can take a function of type void (*)(T*), and then convert it to a void(*)(void*) and call it like that, in a way similar to this:

#include <iostream>
#include <string>

void printer(std::string* string)
{
    std::cout << *string << std::endl;
}

int main() 
{
    //Cast the function to a one that takes a void pointer
    auto func = reinterpret_cast<void(*)(void*)>(&printer);
    //Create a string and call the function with it
    std::string string = "Hello World";
    func(&string);
    return 0;
}

The above code compiles and runs fine (on ideone) but I was wondering if it's at all standards compliant or if it's undefined behaviour and is just working correctly for my specific example and OS

like image 994
Orfby Avatar asked Jun 07 '26 22:06

Orfby


2 Answers

This is undefined behavior.

[expr.call]/1:

Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior.

[expr.reinterpret.cast]/6:

A function pointer can be explicitly converted to a function pointer of a different type. Except that converting a prvalue of type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.

C++ doesn't allow for very many function casts at all to work, even for things you might think would be safe like changing const details. When you need such a thing, use an explicit wrapper function instead. A non-capturing lambda could be one easy way to write one without naming it; or you could define a general template to wrap other functions in a way you need.

The one case which is allowed, since C++17, is converting a pointer to non-throwing function (e.g. marked noexcept) to a potentially-throwing function pointer type (without exception specification or noexcept(false)), then calling it via the potentially-throwing function type. This sort of function pointer conversion is allowed implicitly, so no reinterpret_cast or static_cast or other is even needed.

like image 68
aschepler Avatar answered Jun 10 '26 12:06

aschepler


Behaviour of doing so is undefined. Standard (draft) says:

[expr.reinterpret.cast] A function pointer can be explicitly converted to a function pointer of a different type. [Note: The effect of calling a function through a pointer to a function type (9.2.3.5) that is not the same as the type used in the definition of the function is undefined. — end note] Except that converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are function types) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified. [Note: See also 7.3.11 for more details of pointer conversions. — end note]


You could use a lambda:

void(*func)(void*) = [](void *string) {
    printer(static_cast<std::string*>(string));
};
like image 32
eerorika Avatar answered Jun 10 '26 12:06

eerorika



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!