Question

(5.2.10/6) C++03 A pointer to a function can be explicitly converted to a pointer to a function of a different type. The effect of calling a function through a pointer to a function type (8.3.5) that is not the same as the type used in the definition of the function is undefined. Except that converting an rvalue 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 4.10 for more details of pointer conversions.]

The following is what I'm trying to do, while it is clear that the result of converting fp1 to fp2 will produce a original pointer but at the same point the wording in standard goes "The result of such a pointer conversion is unspecified" What does it mean by that?

int f() { return 42; }

int main()
{
    void(*fp1)() = reinterpret_cast<void(*)()>(f);

    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);

    // Safe to call the function ?
    fp2();
}
Was it helpful?

Solution

You are misreading the standard, the "unspecified" part only applies to other conversions:

Except [special case] the result of such a pointer conversion is unspecified.

That [special case] is the one where you convert back to the original function pointer type, like in your example. In this special case the conversions yield the original pointer value, so it's ok to use it like in your example.

Only for other conversions the result is unspecified.

OTHER TIPS

Yes it is safe.
reinterpret_cast just allows you to cast one pointer to another but it does not guarantee any safety and the result of using such an pointer is unspecified unless it is typecast-ed back to its original type.

The standard quote mentioned specifies that if you typecast a function pointer of one type to another type and try to call a function through it then the result is undefined.

However,
reinterpret_cast guarantees you that if you cast the typecast-ed pointer back to the original type then the pointer is well formed.

Your code tries to do the second and hence it is safe.

Yep, you're safe. What you're doing is covered by the "Except that converting..." case.

The reason that it's called out is to let you pass function pointers through another type of function pointer. So you could define something like:

enum CallbackType {
    eFuncPtrVoidReturningVoid,
    eFuncPtrVoidReturningInt,
    // ... more as needed ...
};

class CallbackRecord
{
public:
    CallbackRecord(void (*cb)()): cbType(eFuncPtrVoidReturningVoid), cbFunc(cb) 
        {}
    CallbackRecord(int (*cb)()): cbType(eFuncPtrVoidReturningInt), 
        cbFunc(reinterpret_cast<void (*)()>(cb)) {}
    void operator()() const;
protected:
    CallbackType cbType;
    void (*cbFunc)();
};

void CallbackRecord::operator()() const
{
    switch(cbType)
    {
    case eFuncPtrVoidReturningVoid:
        (*cbFunc)();
        break;

    case eFuncPtrVoidReturningInt:
        while((*reinterpret_cast<int (*)()>(cbFunc))())
            ;
        break;
    }
}

While you could just say "make all the callbacks return int", this would require you to write wrappers for anything not conforming to the calling convention if the number of callback types gets beyond two. Allowing these function pointer typecasts gives you an alternate means of supporting multiple callback types, and keeps us from needing to turn CallbackRecord into a template. It also allows subclassing or marshaling to replace the switch statement above without requiring the use of virtual methods.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top