Search code examples
c++c++11structcastingreinterpret-cast

Cast structs with certain common members


Lets say I have 2 structs:

typedef struct
{
    uint8_t useThis;            
    uint8_t u8Byte2;             
    uint8_t u8Byte3; 
    uint8_t u8Byte4;  
} tstr1

and

typedef struct
{
    uint8_t u8Byte1;            
    uint8_t u8Byte2;             
    uint8_t useThis;  
} tstr2

I will only need the useThis member inside a function, but in some cases I will need to cast one struct or the other:

void someFunction()
{
    someStuff();
    SOMETHING MyInstance;
    if(someVariable)
    {
        MyInstance = reinterpret_cast<tstr1*>(INFO_FROM_HARDWARE); //This line of course doesn't work
    }
    else
    {
        MyInstance = reinterpret_cast<tstr2*>(INFO_FROM_HARDWARE); //This line of course doesn't work
    }
    MyInstance->useThis; //Calling this memeber with no problem

    moreStuff();
}
  • So I want to use useThis no matter what cast was done. How can this be done?

  • I want to avoid someFunction() to be template (just to avoid this kind of things)

  • Note tha questions like this have a kind of similar problem but the struct members have the same order

EDIT:

In RealLife these structs are way larger and have several "same named" members. Directly casting a uint8_t as reinterpret_cast<tstr1*>(INFO_FROM_HARDWARE)->useThis would be tedious and require several reinterpret_casts (althought it's a working solution for my question before this EDIT). This is why I insist on MyInstance being "complete".


Solution

  • Using virtual dispatch is usually not what you want when mapping to hardware but it is an alternative.

    Example:

    // define a common interface
    struct overlay_base {
        virtual ~overlay_base() = default;
        virtual uint8_t& useThis() = 0;
        virtual uint8_t& useThat() = 0;
    };
    
    template<class T>
    class wrapper : public overlay_base {
    public:
        template<class HW>
        wrapper(HW* hw) : instance_ptr(reinterpret_cast<T*>(hw)) {}
        uint8_t& useThis() { return instance_ptr->useThis; }
        uint8_t& useThat() { return instance_ptr->useThat; }
    
    private:
        T* instance_ptr;
    };
    

    With that, you can declare a base class pointer, assign it, and use after the if statement:

    int main(int argc, char**) {
    
        std::unique_ptr<overlay_base> MyInstance;
    
        if(argc % 2) {
            MyInstance.reset( new wrapper<tstr1>(INFO_FROM_HARDWARE) );
        } else {
            MyInstance.reset( new wrapper<tstr2>(INFO_FROM_HARDWARE) );
        }
    
        std::cout << MyInstance->useThis() << '\n';
        std::cout << MyInstance->useThat() << '\n';
    }
    

    Demo

    Explanation regarding my comment: "It works, but unless the compiler is really clever and can optimize away the virtual dispatch in your inner loops, it's going to be slower than if you actually take the time to type cast":

    Think of virtual dispatch as having a lookup table (vtable) used at runtime (which is often what actually happens). When calling a virtual function, the program has to use that lookup table to find the address of the actual member function to call. When it's impossible to optimize away the lookup (as I made sure in my example above by using a value only available at runtime) it does take a few CPU cycles extra compared to what you'd get by doing a static cast.