14

Am i wrong about the following?

C++ standards says that conversion between pointer-to-function and pointer-to-object (and back) is conditionnaly-supported with implementation-defined semantics, while all C standards says that this is illegal in all cases, right?

void foo() {}

int main(void)
{
    void (*fp)() = foo;
    void* ptr = (void*)fp;
    return 0;
}

ISO/IEC 14882:2011

5.2.10 Reinterpret cast [expr.reinterpret.cast]

8 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 cvqualification, shall yield the original pointer value.

I can't find anything about it in C standard right now...

FrozenHeart
  • 19,844
  • 33
  • 126
  • 242
  • It's defined in POSIX in both C and C++ though. – netcoder Jan 02 '13 at 16:32
  • 2
    Isn't *conditionally-supported with implementation-defined semantics* just standardese for *it might work, but doesn't have to*? Unlike C which says *it doesn't have to work*. – Bo Persson Jan 02 '13 at 16:33
  • @BoPersson I think it means: *it will either fail on compilation or work this way*. – Gorpik Jan 02 '13 at 16:36
  • I was thinking more about the portability of the code. The "implementation-defined semantics" can be a killer, if we don't check what it's supposed to do on a specific implementation. It just *could* compile and do something stupid. – Bo Persson Jan 02 '13 at 16:40

5 Answers5

12

  • In C++03, such conversions were illegal (not UB). The compiler was supposed to issue a diagnostic. A lot of compilers on Unix systems didn't issue a diagnostic. This was essentially a clash between standards, POSIX vs C++.
  • In C++11, such conversions are "conditionally supported". No diagnostic is required if the system does supports such conversions; there's nothing to diagnose.
  • In C, such conversions officially are undefined behavior, so no diagnostic is required. If the system happens to do the "right" thing, well that's one way to implement UB.
  • In C99, this is once again UB. However, the standard also lists such conversions as one of the "common extensions" to the language:

    J.5.7 Function pointer casts
    A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).
    A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

David Hammen
  • 32,454
  • 9
  • 60
  • 108
9

You're right, the C(99) standard says nothing about conversion from pointer-to-function to pointer-to-object, therefore it's undefined behaviour.*


*Note, however, that it does define behaviour between pointer-to-function types.
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Thanks! And what about C89 / C90 in this case? – FrozenHeart Jan 02 '13 at 16:26
  • 1
    @NikitaTrophimov The same, C99 wouldn't loose previously enforced behavior. – cooky451 Jan 02 '13 at 16:33
  • 2
    @NikitaTrophimov: I don't have the c90 standard to hand, but C99 wouldn't have made previously-defined behaviour undefined. – Oliver Charlesworth Jan 02 '13 at 16:33
  • @NikitaTrophimov: Oh, I don't know, I'm afraid. – Oliver Charlesworth Jan 02 '13 at 16:39
  • @NikitaTrophimov: C90 doesn't mention it either (actually it doesn't even define any conversions for function pointer types, i.e.: between pointer-to-function types like Oli mentioned). And C11 doesn't add anything on that subject. – netcoder Jan 02 '13 at 16:48
  • @netcoder Are you sure? See 6.3.4 Cast operators: A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function that has a type that is not compatible with the type of the called function. the behavior is undefined. – FrozenHeart Jan 02 '13 at 16:53
  • @netcoder "actually it doesn't even define any conversions for function pointer types, i.e.: between pointer-to-function types like Oli mentioned" - it's defined in 6.3.4 Cast operators of ISO/IEC 9899:1990 – FrozenHeart Jan 02 '13 at 16:58
  • @NikitaTrophimov: Oh it seems you're right (that's 3.3.4 by the way, not 6.3.4). It's odd to find it in the operators section rather than conversions. – netcoder Jan 02 '13 at 17:02
  • It's also worth mentioning that this leads to a nice big inconsistency between the lnaguage standard and the POSIX C standard library: `dlsym()`, which is supposed to retrieve function pointers, returns `void *`. Now guess how many conforming implementations are/can be in the world... –  Jan 02 '13 at 17:13
5

In all C standards the conversion between pointer-to-function and pointer-to-object is not defined, in C++ before C++11, the conversion was not allowed and compilers had to give an error, but there were compilers which accepted the conversion for C and backward compatibility and because is useful for things like dynamically loaded libraries access (for instance the dlsym POSIX function mandates its use). C++11 introduced the notion of conditionally-supported features an used it to adapt the standard with the existing practice. Now either the compiler should reject the program trying to do such conversion or it should respect the limited constraints given.

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • But any situation not explicitly defined in standard is UB, am i wrong? C99, 4.p2 – FrozenHeart Jan 02 '13 at 16:38
  • @NikitaTrophimov, I went back to the standard and reworded my answer. It was (and still is if I haven't skipped anything relevant) UB in C. It had to be a compilation error in C++ (but was accepted by some implementation). – AProgrammer Jan 02 '13 at 16:55
  • @NikitaTrophimov C++ describes the C cast in term of reinterpret_cast and 5.2.10/1 in C++98 states "Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast." – AProgrammer Jan 02 '13 at 18:51
5

Though the behavior of the cast is not defined by the "core" of the standard, this case is explicitly described as invalid in the C99 rationale document (6.3.2.3, Pointers):

Nothing is said about pointers to functions, which may be incommensurate with object pointers and/or integers.

Even with an explicit cast, it is invalid to convert a function pointer to an object pointer or a pointer to void, or vice versa.

And since it may be useful, it is also mentioned in the Annex J of the standard as a "common extension" (C11 Standard J.5.7, Function pointer casts):

A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).

A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

Describing this as an extension means that this is not part of the standard requirements (but it wouldn't be needed, the omission of any explicit behavior is enough).

effeffe
  • 2,821
  • 3
  • 21
  • 44
1

The POSIX standard specifies:

2.12.3 Pointer Types
All function pointer types shall have the same representation as the type pointer to void. Conversion of a function pointer to void * shall not alter the representation. A void * value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information.

Note: The ISO C standard does not require this, but it is required for POSIX conformance.

This requirement ends up meaning that any C or C++ compiler that wants to be able to support POSIX will support this kind of casting.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226