Home > Blockchain >  C Returning a member from one of 2 structs using macros or templates
C Returning a member from one of 2 structs using macros or templates

Time:12-15

I am working on a plugin that runs inside a host program against a proprietary PDK. At times there will be breaking changes in the PDK, so my code uses wrapper classes that allow it to work with more than one version of the host while encapsulating the changes from version to version.

Here is a very simplified example that illustrates the kind of issue I would like to address. Of course, I'm dealing with many more members that 2.

struct DataV1 // I cannot modify this
{
   int a;
   float b;
};
struct DataV2 // I cannot modify this
{
   float b;
   int a;
   long c;
};

class DataWrapper // my class
{
private:
   bool _forV1; // determined at run-time
   DataV1 _dataV1;
   DataV2 _dataV2;

public:
   DataWrapper(); // initializes _forV1

   int GetA() const;
   void SetA(int value);
   float GetB() const;
   void SetB(float value);

   long GetC() const { return _dataV2.c } // only exists in v2
   void SetC(long value) { _dataV2.c = value; } // only exists in v2
};

I would like to avoid duplicating in every getter and setter the logic that chooses the member from one version of the struct or the other. Note that while the order of members is rearranged, the types and member names are the same. I came up with this macro:

#define DATA_ACCESS(MEMBER) const_cast<decltype(_dataV1.MEMBER)&>(([&]() -> const decltype(_dataV1.MEMBER)& \
                                    { return (_forV1) ? _dataV1.MEMBER : _dataV2.MEMBER; })())

This allows for a somewhat elegant implementation of the property accessor functons:


   int GetA() const { return DATA_ACCESS(a); }
   void SetA(int value) { DATA_ACCESS(a) = value; }
   float GetB() const { return DATA_ACCESS(b); }
   void SetB(float value) { DATA_ACCESS(b) = value; }

I am posting this question to see if anyone has a better idea, especially an idea that doesn't involve a macro. Thanks.

CodePudding user response:

With std::variant, you might do something like:

class DataWrapper // my class
{
private:
   std::variant<DataV1, DataV2> data;

public:
   DataWrapper(); // initializes _forV1

   int GetA() const { return std::visit([](auto& arg){ return arg.a; }, data); }
   void SetA(int a) const { std::visit([&a](auto& arg){ arg.a = a; }, data); }
    // ...
};
  • Related