Can Boost Proto adapt structures to a getter setter type API

136 Views Asked by At

This seems like a common problem. I've got two massive sets of code that need to be glued together: one that uses simple structs to hold data, the other has APIs that only expose getter/setter methods.

Is it possible to use Boost.Proto to define a mapping that could then be used to auto-generate the code that invokes the getters/setters? Conceptually, the piece that seems most difficult is the synthesis of the function names to call, as that would involve compile-time string concatenation. Other challenges involve mapping enum types from one to the other and custom initialization or conversion code.

Having a drop-in-place Proto-based solution that solves this problem would be a huge benefit to a wide variety of people.

E.g., I have an API with types such as this:

// These classes use getters/setters.
class Wheel
{
    int number_of_lugnuts_;
public:
    void initialize_wheel(bool);
    void set_number_of_lugnuts(int);
    int get_number_of_lugnuts();
};

class Engine
{
public:
    enum Gas_type_t {
        Unleaded,
        Premium
    };
private:
    Gas_type_t gas_type_;
public:
    void initialize_engine(bool);
    void set_gas_type(Gas_type_t);
    Gas_type_t get_gas_type();
};

While I also have millions of lines of code with the same data in simple directly-accessed structs:

// This code has simple data structures.
struct Car
{
    // These POD members are used by a large body of existing code.
    int lugnut_count;
    enum FUEL_TYPES {
        NORMAL_FUEL,
        HI_OCTANE
    };
    FUEL_TYPES fuelType;
};

Now the old-fashioned way is to add a lot of converters:

// The manual way to accomplish this for only the Wheel API.
// This has to be repeated similarly for the Engine API.
void convert_to_wheel(Wheel& w)
{
    w.initialize_wheel(true); // how can initialization be handled?
    w.set_number_of_lugnuts(lugnut_count);
}

void convert_from_wheel(Wheel& w)
{
    lugnut_count = w.get_number_of_lugnuts();
}

But, in the style of Boost.Spirit, I would like to use Proto to create an EDSL allowing me to specify the mapping, and have the compiler generate the repetitive code for me.

I can define enough Proto terminals to get this constructor to compile:

Car()
{
    // So can we define an API mapping like this?
    // Would strings be the only way to accomplish this?
    // This appears structurally similar to how Spirit grammars are defined.
    // This is a very rough attempt because it's unclear if this is possible.
    define_api_mapping<Wheel>
        (initialization_code((_dest_ ->* &Wheel::initialize_wheel)(true)))
        (map_member(lugnut_count) = map_getset("number_of_lugnuts"))
    ;
    define_api_mapping<Engine>
        (initialization_code((_dest_ ->* &Engine::initialize_engine)(true)))
        (map_member(fuelType) = map_getset("gas_type"))
    ;
    define_enum_mapping<FUEL_TYPES>
        (enum_value(NORMAL_FUEL) = enum_value(Engine::Unleaded))
        (enum_value(HI_OCTANE) = enum_value(Engine::Premium))
    ;
}

Conversion could conceptually be a simple function call:

// Declare some objects.
Car c;
Engine e;
Wheel w1, w2;

// Set some values.
c.lugnut_count = 20;

// Convert the old fashioned way.
c.convert_to_wheel(w1);

// Convert the new way.
convert(c, w2);

It's an ugly start, but I'm now baffled by how to mangle the names to generate the calls to the getters and setters.

Is this possible? What would the solution look like?

0

There are 0 best solutions below