Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I dynamically pick a member from two similar C++ structures while avoiding code duplication?

Tags:

c++

Having the following structures:

class XY
{
private:
    int test;
};
class XYZ
{
private:
    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 should I do about &object->test depending on a different type of object? What if the object is passed into a function like the following?

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 much code like this, so I can't write an if condition every time. Also, I am unable to change the structures' layout in any way, shape, or form.

like image 452
iFarbod Avatar asked Sep 27 '21 21:09

iFarbod


2 Answers

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;
like image 171
Remy Lebeau Avatar answered Oct 10 '22 16:10

Remy Lebeau


#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);

If you want to keep the syntax of ->, there is no way but to use this method and declare each of your class members in XYZWrapper:

struct XYZWrapper
{
    XYZWrapper(void* ptr)
    {
        if (x == 1)
        {
            extra = nullptr; // doesn't exist in x == 1
            test = reinterpret_cast<XY*>(ptr)->test;
        }
        else if (x == 2)
        {
            extra = reinterpret_cast<XYZ*>(ptr)->extra;
            test = reinterpret_cast<XYZ*>(ptr)->test;
        }
    }
    XYZWrapper* operator->() { return this; }
    short* extra;
    int* test;
}

*XYZWrapper(object)->test = 1;
void* addressTo = XYZWrapper(object)->test;

void SomeFunction(XYZWrapper object)
{
    *object->test = 1;
    if (x == 2) *object->extra = 4;
}
like image 43
rez Avatar answered Oct 10 '22 16:10

rez