Home > database >  Examples using reinterpret_cast that do not trigger UB
Examples using reinterpret_cast that do not trigger UB

Time:10-27

Reading https://en.cppreference.com/w/cpp/language/reinterpret_cast I wonder what are use-cases of reinterpret_cast that are not UB and are used in practice?

The above description contains many cases where it is legal to convert a pointer to some other type an then back, which is legal. But that seems of less practical use. Accessing an object through a reinterpret_cast pointer is mostly UB due to violations of strict-aliasing (and/or alignment), except accessing through a char*/byte*-pointer.

One helpful exception is casting a integer-constant to a pointer and accessing the target object, which is useful for manipulation of HW-registers (in µC).

Can anyone tell some real use-cases of relevance of reinterpret_cast that are used in practice?

CodePudding user response:

Some examples that come to mind:

  • Reading/writing the object representation of an object, for example to write the byte representation of the object to a file and read it back:

    // T must be trivially-copyable object type!
    T obj;
    
    //...
    
    std::ofstream file(/*...*/);
    file.write(reinterpret_cast<char*>(obj), sizeof(obj));
    
    //...
    
    std::ifstream file(/*...*/);
    file.read(reinterpret_cast<char*>(obj), sizeof(obj));
    

    Technically it is currently not really specified how accessing the object representation should work, but there is a current proposal to clarify this in the standard, see https://github.com/cplusplus/papers/issues/592.

  • Obtaining a pointer to a specific memory address. Whether and for which address values this is possible is implementation-defined, as is any possible use of the resulting pointer:

    auto ptr = reinterpret_cast<void*>(0x12345678);
    
  • Reinterpreting between signed and unsigned variants of the same integral type, especially char and unsigned char for strings, which may be useful if an API expects an unsigned string.

    auto str = "hello world!";
    auto unsigned_str = reinterpret_cast<const unsigned char*>(str);
    

    While this is allowed by the aliasing rules, technically pointer arithmetic on the resulting unsigned_str pointer is currently not defined by the standard. But I don't really see why it isn't.

  • Accessing objects nested within a byte buffer (especially on the stack):

    alignas(T) std::byte buf[42*sizeof(T)];
    new(buf sizeof(T)) T;
    
    // later
    
    auto ptr = std::launder(reinterpret_cast<T*>(buf   sizeof(T)));
    

    This works as long as the address buf sizeof(T) is suitably aligned for T, the buffer has type std::byte or unsigned char, and obviously is of sufficient size. The new expression also returns a pointer to the object, but one might not want to store that for each object. If all objects stored in the buffer are the same type, it would also be fine to use pointer arithmetic on a single such pointer.

  • Casting a void* returned by dlsym (or a similar function) to the actual type of a function located at that address. Whether this is possible and what exactly the semantics are is again implementation-defined:

    // my_func is a C linkage function with type `void()` in `my_lib.so`
    
    // error checking omitted!
    
    auto lib = dlopen("my_lib.so", RTLD_LAZY);
    
    auto my_func = reinterpret_cast<void(*)()>(dlsym(lib, "my_func");
    
    my_func();
    
  • Related