mardi 1 septembre 2020

C++11, wrapper class for handling different versions of C struct versions

EDIT: I think the version is known at run-time instead of compile-time so I'm not able to add it as a compile option to the gcc cmd. Which is why I have to support both versions based on whatever version the hardware reports back.

So I'm dealing with firmware where I am required to support multiple definitions for versions of the same C struct. We created our own header file as defined by the interface documentation of a memory controller based on the vendor's C struct definition.

// For simplicity lets pretend that this is the struct for version 1
typedef struct __attribute__((packed)) ver1 {
    int x;
    int y;
} ver1;

I also have an existing API that uses this interface already that needs to be replaced by some sort of class wrapper (I believe), e.g. or a wrapper that plays well with the existing API.

void function_call(ver1 v1);

Only once instance (ver 1 or ver 2) of the struct can exist at any time ver 1 for a certain fw version, and ver 2 after a certain fw version

ver2 is my extended version of ver1, I am naming it as ver2 for the hope of using some sort of factory to select the right C-style struct.

typedef struct __attribute__((packed)) ver2 {
    int x;
    int y;
    int w; // new
    int z; // new
 } ver2;

Before creating a ver 2 I was looking into options such as the decorator or adaptor design pattern I could try a fancy CRTP template style I found on Hands-On Design Patterns but for simplicity, I'll illustrate with this scheme where I could possibly "add-on" to ver1:

struct ver2 : public ver1 {
    int w;
    int z;
}

But then I learned that C++ doesn't guarantee the same class layout C struct Inheritance vs C++ POD struct Inheritance and potential alignment issues (I'm not too familiar with it) so I don't think it is a real option for me to use.

I found this example on stackoverflow but I don't like the idea of adding include headers in the struct How to handle conflicting struct definitions in a C application. There is a similar example here using a similar base class C++ design for multiple versions of same interface (enumerations / structures in header files) which I don't think I can even use due to inheritance impact on the class layout.

Unless there is a valid reason to use the techniques of the links above, I was considering a wrapper class that returns the right version based on a selector. First I'll define a free function to leverage this.

int get_fw_version(int target);

I'm working on C++11 so I'm limited on auto return type deduction and below is just some draft code I'm trying to think up, not complete, doesn't compile, just illustrating my thought process. I haven't considered composition yet since IDK how that will quite work. Looking for ideas.

 int main() {
     // Roughly how I would like to use it...

    const int fw_ver = get_fw_version(target);
    ver_wrapper ver_inst(fw_ver);

    // I would like to do reuse the existing API if possible
    function_call(ver_inst.data());

    // But I'm flexible enough to change the API if needed
    function_call(ver_inst);

 return 0;
 }

typename <int R> // R for rev
struct ver_traits;

template <>
struct ver_traits<1> {
    using type = ver1;
};

template <>
struct ver_traits<2> {
    using type = ver2;
};

Am I limited to using a base class to create a factory pattern? Init ver1 and 2 differently. Not really a factory but a selector? - shrug

template<int I, typename TT = ver_traits<I>>
TT::type ver_factory(int fw_ver) {
    return ...;
}

class ver_wrapper {
    private:
    int __fw_version;
    T __data; // -> ?? need a good way to handle this, should be a template?
    public:
    ver_wrapper(int fw_ver):__fw_ver(fw_ver){
         // could be switch statement
        if(target == 1) {
            // set up data with ver1 struct (probably w/memcpy)
            data = ver_factory<1>(fw_ver)
        }
        else {
            // init data with ver2 struct (probably w/memcpy)
            data = ver_factory<2>(fw_ver)
        }
    }
    // needs some sort of type deduction here
    T data() {return __data;}
};

Aucun commentaire:

Enregistrer un commentaire