Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ derive pure abstract w/ nested struct

I'm trying to set up a syntactical sugar similar to the c# property concept.

I've read this post: C#-like properties in native c++?. It's helpful, but lacks the design I want. I also respectfully disagree with several of the assertions made there that the property concept is bad oo form, as I fail to see the difference between a set of methods titled get_id() and set_id() and an operator overload that exposes the same concept, allows code to be cleaner when the class is consumed, and generally discourages the public access to private fields, also decoupling the public implementation from private design.

However, the code that I've come up with (inspired by that link) is REALLY kludgy and will be quite difficult to maintain. I'm wondering if anyone has any suggestions to clean this up, or more importantly, knows a better way to do this.

class someClass
{
public:
    struct someProperty_struct{
        virtual someProperty_struct& operator=(const int&){return *this;}
        virtual operator int(){return 0;}
    }someProperty;
};

class derivedClass : someClass
{
    int i;
public:
    struct intProperty: someClass::someProperty_struct
    {
    protected:
        friend class derivedClass;
        derivedClass *outer;
    public:
        virtual someProperty_struct& operator=(const int& value){
            outer->i = value;
            return *this;}
        virtual operator int(){return outer->i;}
    };
    derivedClass()
    {
        intProperty p = intProperty();
        p.outer = this;
        someProperty = p;
    }
};

class consumerClass
{
public:
    someClass* data;
    consumerClass()
    {
        data = (someClass*)(new derivedClass());
    }
    void doSomething()
    {
        data->someProperty = 7;
        int x = data->someProperty;
    }
};

EDIT 1: It occurs to me that I didn't reveal anything about my intentions with this. It's going to be used in a scheduling program, and we're going to be doing a lot of comparison and assignment of all the data in the classes. 'someClass' will effectively be an interface on the data (very few methods needed, lots of data that should be relatively transparent). It will be defined in a static library that the executable will link. 'derivedClass' will effectively be an external implementation, implemented in a dll which will be dynamically loaded. The reason for this is to enable "hot swapping" of the dll with another one which implements a different file backend. We have plans to implement xml, sqlite, and mysql storage backends using a plugin system to load them up.

So basically, I need a design that allows someClass to be a virtual interface that is inherited by derivedClass, which is loaded by factory method, passed through the plugin system, and used in the end by consumerClass.

like image 652
lassombra Avatar asked Feb 18 '12 23:02

lassombra


2 Answers

So basically, I need a design that allows someClass to be a virtual interface

If I understand you correctly, this intention contradicts the solution you have came up with. The virtual inheritance concerns only virtual member functions. And there is no way you could dynamically inherit nested structs because it is a subject of the lexical meta-programming which is not supported in C++.

My suggestion is to think carefully if you can make your object immutable. Then some adaptation of the Builder/Factory design pattern will allow you to eliminate the usage of properties.

You may also consider to write a code generator. In case if you pick up a good markup keywords, it may be easy.

UPDATE I've added some code to clarify my suggestion.

First of all we prepare some auxiliary objects. They should be put in a header file accessible for both the Library and the Client. These objects will never be modified.

GetSetProp <>----> IGetSetProp <----- PropFunctionAdapter

template<class _Ty>
struct __declspec(novtable) IGetSetProp
{
    typedef std::tr1::shared_ptr<IGetSetProp<_Ty>> ptr_t;
    virtual void set_Prop(_Ty const& val);
    virtual _Ty get_Prop() const;
};

template<typename _Ty>
class PropFunctionAdapter : public IGetSetProp<_Ty>
{
    std::function<_Ty(void)> getter;
    std::function<void(_Ty const&)> setter;
public:
    PropFunctionAdapter(std::function<_Ty(void)> _getter, std::function<void(_Ty const&)> _setter)
        : getter(_getter)
        , setter(_setter)
    {
         // One may want to verify that getter and setter are not empty
    }

    virtual ~PropFunctionAdapter() throw() {}

    inline static std::tr1::shared_ptr<typename PropFunctionAdapter<_Ty>> Create(std::function<_Ty(void)> _getter, std::function<void(_Ty const&)> _setter)
    {
        return std::make_shared<typename PropFunctionAdapter<_Ty>>(_getter, _setter);
    }

public:
    void set_Prop(_Ty const& val)
    {
        setter(val);
    }

    _Ty get_Prop() const
    {
        return getter();
    }
};

template<class _Owner, class _Ty>
typename IGetSetProp<_Ty>::ptr_t CreateAdapter(_Owner& source, _Ty(_Owner::*getter)() const, void(_Owner::*setter)(_Ty const&))
{
    return PropFunctionAdapter<_Ty>::Create(
        std::tr1::bind(std::mem_fn(getter), &source),
        std::tr1::bind(std::mem_fn(setter), &source, std::tr1::placeholders::_1));
}

template<class _Ty>
class GetSetProp
{
    typename IGetSetProp<_Ty>::ptr_t prop;
public:
    GetSetProp(typename IGetSetProp<_Ty>::ptr_t _prop)
        : prop(_prop)
    {
    }

    GetSetProp<_Ty>& operator= (_Ty const& val)
    {
        prop->set_Prop(val);
        return *this;
    }

    operator _Ty() const
    {
        return prop->get_Prop();
    }
};

Similarly you may define GetProperty and SetProperty.

Suppose you have a data contract containing two fields Pressure:int and Description:string. Then you define the data contract:

class BaseClass
{
public:
    GetSetProp<int> Pressure;
    GetSetProp<std::string> Description;
protected:
    BaseClass(IGetSetProp<int>::ptr_t fieldA, IGetSetProp<std::string>::ptr_t fieldB)
        : Pressure(fieldA)
        , Description(fieldB)
    {
    }

    virtual ~BaseClass() throw() {}
};

And its implementation in the library:

class DerivedClass : public BaseClass
{
public:
    // Here you initialize fields assigning them correspondent setters and getters
    // You may define an ATL-like mapping in order to hide implementation details
    DerivedClass()
        : BaseClass(
            CreateAdapter(*this, &DerivedClass::get_Pressure, &DerivedClass::set_Pressure)
            , CreateAdapter(*this, &DerivedClass::get_Description, &DerivedClass::set_Description))
    {
    }

    virtual ~DerivedClass() throw() {}

private:
    void set_Pressure(int const& value)
    {
        val = value;
    }

    int get_Pressure() const
    {
        return val;
    }

    void set_Description(std::string const& description)
    {
        this->description = description;
    }

    std::string get_Description() const
    {
        return description;
    }

private:
    int val;
    std::string description;
};

// The client-side code
DerivedClass d;
BaseClass& b = d; 
b.Description = "Hello";
std::string descr = b.Description;
b.Pressure = 2;
int value = b.Pressure;
like image 155
Igor Chornous Avatar answered Oct 13 '22 12:10

Igor Chornous


Creating a property helper class that must be instanciated as a member of the class you want to add property on is a bad idea, since you'll have the cost of the property members for all the instances of this class.

A solution that offers a "property" mechanism close to what other languages offer can be made available in C++ using something like what I describe later.

The only limitation being that you access the property using

instance.property(); // to get

and

instance.property() = 42; // to set

#include <iostream>
#include <string>

using namespace std;

// ------------------------------------------------------------------

#define PROPERTY_GET_SET(CLASS, NAME, TYPE) GetSetProperty<CLASS, TYPE> NAME() { return GetSetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME, &CLASS::set_##NAME); }
#define PROPERTY_GET(CLASS, NAME, TYPE)     GetProperty<CLASS, TYPE> NAME()    { return GetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME); }
#define PROPERTY_SET(CLASS, NAME, TYPE)     SetProperty<CLASS, TYPE> NAME()    { return SetProperty<CLASS, TYPE>(this, &CLASS::set_##NAME); }

template <typename CLASS, typename TYPE>
struct GetSetProperty {
    typedef TYPE (CLASS::*Getter_t)() const;
    typedef void (CLASS::*Setter_t)(TYPE);
    GetSetProperty(CLASS* instance, Getter_t getter, Setter_t setter) : m_instance(instance), m_getter(getter), m_setter(setter) {}
    operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
    GetSetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
    CLASS* const   m_instance;
    const Getter_t m_getter;
    const Setter_t m_setter;
};

template <typename CLASS, typename TYPE>
struct GetProperty {
    typedef TYPE (CLASS::*Getter_t)() const;
    GetProperty(CLASS* instance, Getter_t getter) : m_instance(instance), m_getter(getter) {}
    operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
    CLASS* const   m_instance;
    const Getter_t m_getter;
};

template <typename CLASS, typename TYPE>
struct SetProperty {
    typedef void (CLASS::*Setter_t)(TYPE);
    SetProperty(CLASS* instance, Setter_t setter) : m_instance(instance), m_setter(setter) {}
    SetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
    CLASS* const   m_instance;
    const Setter_t m_setter;
};

template <typename CLASS, typename TYPE>
ostream& operator<<(ostream& ostr, const GetSetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }

template <typename CLASS, typename TYPE>
ostream& operator<<(ostream& ostr, const GetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }

// ------------------------------------------------------------------

class Dummy
{
public:

    Dummy() : m_value1(42) {}

    PROPERTY_GET_SET(Dummy, Value1, int);
    PROPERTY_GET_SET(Dummy, Value2, const string&);

protected:

    virtual int           get_Value1() const { return this->m_value1; }
    virtual void          set_Value1(int value) { this->m_value1 = value; }

    virtual const string& get_Value2() const { return this->m_value2; }
    virtual void          set_Value2(const string& value) { this->m_value2 = value; }

private:

    int    m_value1;
    string m_value2;
};


int main(int argc, char* argv[]) {

    Dummy d;

    cout << d.Value1() << endl;
    d.Value1() = 3;
    cout << d.Value1() << endl;

    cout << d.Value2() << endl;
    d.Value2() = "test";
    cout << d.Value2() << endl;

    return 0;
}

// ------------------------------------------------------------------
like image 1
rlods Avatar answered Oct 13 '22 11:10

rlods