Reputation: 1455
Lets say I have 2 struct
s:
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_cast
s (althought it's a working solution for my question before this EDIT). This is why I insist on MyInstance
being "complete".
Upvotes: 4
Views: 408
Reputation: 117298
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';
}
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.
Upvotes: 3
Reputation: 241
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...
Upvotes: 4
Reputation: 238321
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.
Upvotes: 5
Reputation: 28737
A simple reference to the struct member might be what you need:
uint8_t &useThis=SomeVariable
?reinterpret_cast<tstr1*>(INFO_FROM_HARDWARE)->useThis
:reinterpret_cast<tstr2*>(INFO_FROM_HARDWARE)->useThis;
Upvotes: 3