Having the following structures:
class XY
{
int test;
};
class XYZ
{
short extra;
int test;
};
I want to use XY::test
and XYZ::test
depending on a runtime variable. Like:
if (x == 1)
{
reinterpret_cast<XY*> (object)->test = 1;
}
else if (x == 2)
{
reinterpret_cast<XYZ*> (object)->test = 1;
}
Is it possible to make this into a nice one-liner? (using templates, macros, etc...?)
What to do about &object->test
depending on a different type of object?
What if the object is passed onto a function like:
void Function(void* object)
{
if (x == 1)
{
reinterpret_cast<XY*> (object)->test = 1;
}
else if (x == 2)
{
reinterpret_cast<XYZ*> (object)->test = 1;
}
}
How can I properly deal with this? I have so many codes like this so I can't write an if condition every time, also, I am unable to change the structures' layout in any way or form.
CodePudding user response:
Since XY::test
and XYZ::test
are at different byte offsets, and you have stated that you cannot change these structs, I think the best you can do is using an int*
pointer, eg:
int *ptest;
switch (x) {
case 1: ptest = &(static_cast<XY*>(object)->test); break;
case 2: ptest = &(static_cast<XYZ*>(object)->test); break;
...
}
*ptest = 1;
Though, if you really want something more general, an alternative would be to do something like this:
const std:unordered_map<int, size_t> offsets = {
{1, offsetof(XY, test)},
{2, offsetof(XYZ, test)}
...
};
...
*reinterpret_cast<int*>(static_cast<char*>(object) offsets[x]) = 1;
CodePudding user response:
Well, since you explicitly say macros may be ok:
#define CAST_IF(N, XY) if (x == N) reinterpret_cast<XY*>(object)
#define ASSIGN(FIELD, VALUE) \
do { \
CAST_IF(1, XY)->FIELD = VALUE; \
else CAST_IF(2, XYZ)->FIELD = VALUE; \
} while (false)
This kind of thing tends to be tolerable in an implementation file (.cpp, .cc or whatever), but a bad idea in an include file that a lot of other code may include (you should at least pick much more unique names in that case, and #undef them afterwards).
CodePudding user response:
#define FlexibleOffset(class1, class2, member) (x == 1 ? offsetof(class1, member) : offsetof(class2, member))
#define FlexibleMember(object, member) *reinterpret_cast<decltype(XY::member)*>(reinterpret_cast<uintptr_t>(object) FlexibleOffset(XY, XYZ, member))
FlexibleMember(object, test) = 1;
void* addressTo = &FlexibleMember(object, test);
CodePudding user response:
Write a function.that converts a void pointer to a variant of pointers.
Then visit and dereference.
You can write a magic member pointer that operates on a variant of pointers if you are addicted to syntax. But that will increase total line count.
End use could look like:
pick_cast<XY,XYZ>(x==2)(object)->*test = 3;
after dozens of lines of abtuse boilerplate.
Things get less stupid if you do away with void ptr, and just pass around variant of pointers.
With no boilerplate:
using ptr_t= std::variant<XY*, XYZ*>;
void foo(ptr_t ptr){
std::visit([&](auto*ptr){ptr->test=3;}, ptr);
}
To turn a void ptr into a ptr_t, you can write one switch to.keep.it simple:
ptr_t get_ptr(int x, void*p){
switch(x){
case 1: return reinterpret_cast<XY*>(p);
case 2: return reinterpret_cast<XYZ*>(p);
}
}