Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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".

like image 611
Ivan Avatar asked Dec 06 '22 08:12

Ivan


2 Answers

This is what templates are for:

template<class tstr>
std::uint8_t
do_something(std::uint8_t* INFO_FROM_HARDWARE)
{
    tstr MyInstance;
    std::memcpy(&MyInstance, INFO_FROM_HARDWARE, sizeof MyInstance);
    MyInstance.useThis; //Calling this memeber with no problem
    // access MyInstance within the template
}

// usage
if(someVariable)
{
    do_something<tstr1>(INFO_FROM_HARDWARE);
}
else
{
    do_something<tstr2>(INFO_FROM_HARDWARE);
}

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

Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?

The linked problem isn't an issue for your use case because the potential set of template arguments is a finite set. The very next FAQ entry explains how: Use explicit instantiations of the template.

like image 102
eerorika Avatar answered Dec 25 '22 09:12

eerorika


as suggested by AndyG, how about std::variant (there's no mention of the c++ standard you are using so maybe a c++17 solution is ok - also worth using <insert other variant implementation> if no c++17 available).

here's an example

std::variant knows what type is stored in it and you can use visit anytime you wish to use any of the members in there (snippet here for clarity):

// stolen from @eerrorika (sorry for that :( )
struct hardware {
    uint8_t a = 'A';
    uint8_t b = 'B';
    uint8_t c = 'C';
    uint8_t d = 'D';
};

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

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

// stuff

if(true)
{
    msg = *reinterpret_cast<tstr1*>(&hw);
} 
else
{
    msg = *reinterpret_cast<tstr2*>(&hw);
}

std::visit(overloaded {
    [](tstr1 const& arg) { std::cout << arg.useThis << ' '; }, 
    [](tstr2 const& arg) { std::cout << arg.useThis << ' '; }
}, msg);

EDIT: you can also do a variant of pointers EDIT2: forgot to escape some stuff...

like image 21
ben10 Avatar answered Dec 25 '22 09:12

ben10