C ++ получить чистый абстрактный с вложенной структурой - PullRequest
5 голосов
/ 19 февраля 2012

Я пытаюсь настроить синтаксический сахар, похожий на концепцию свойства c #.

Я читал этот пост: C # -подобные свойства в нативном c ++? . Это полезно, но не хватает дизайна, который я хочу. Я также с уважением не согласен с некоторыми из сделанных там утверждений о том, что концепция свойства имеет плохую форму, так как я не вижу различия между набором методов с названием get_id () и set_id () и перегрузкой оператора, которая раскрывает ту же концепцию, позволяет коду быть чище, когда класс используется, и, как правило, препятствует общедоступному доступу к частным полям, также отделяя публичную реализацию от частного дизайна.

Однако код, который я придумал (вдохновленный этой ссылкой), ДЕЙСТВИТЕЛЬНО грязен, и его будет довольно сложно поддерживать. Мне интересно, есть ли у кого-нибудь какие-либо предложения, чтобы это исправить, или, что более важно, знает лучший способ сделать это.

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;
    }
};

РЕДАКТИРОВАТЬ 1: Мне приходит в голову, что я ничего не рассказал о своих намерениях с этим. Он будет использоваться в программе планирования, и мы будем много сравнивать и присваивать все данные в классах. 'someClass' фактически будет интерфейсом для данных (требуется очень мало методов, много данных, которые должны быть относительно прозрачными). В статической библиотеке будет определено, что исполняемый файл будет связывать. «Производный класс» фактически будет внешней реализацией, реализованной в dll, которая будет динамически загружаться. Причина этого заключается в том, чтобы включить «горячую» замену dll другой, которая реализует другой файловый бэкэнд. У нас есть планы реализовать бэкэнды хранилищ xml, sqlite и mysql, используя систему плагинов для их загрузки.

В общем, мне нужен дизайн, который позволяет someClass быть виртуальным интерфейсом, который наследуется производным классом, который загружается фабричным методом, передается через систему плагинов и, в конце концов, используется потребителем класса.

Ответы [ 2 ]

1 голос
/ 23 февраля 2012

Создание вспомогательного класса свойства, который должен быть создан как член класса, для которого вы хотите добавить свойство, является плохой идеей, поскольку вы будете иметь стоимость членов свойства для всех экземпляров этого класса.

Решение, которое предлагает механизм «свойств», близкий к тому, что предлагают другие языки, может быть сделано доступным в C ++ с использованием чего-то вроде того, что я опишу позже.

Единственное ограничение - доступ к свойству с помощью

instance.property ();// чтобы получить

и

instance.property () = 42;// установить

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

// ------------------------------------------------------------------
1 голос
/ 22 февраля 2012

В общем, мне нужен дизайн, который позволил бы некоторому классу быть виртуальным интерфейсом

Если я правильно вас понимаю, это намерение противоречит предложенному вами решению.Виртуальное наследование касается только виртуальных функций-членов.И нет никакого способа, которым вы могли бы динамически наследовать вложенные структуры, потому что это предмет лексического метапрограммирования, который не поддерживается в C ++.

Я предлагаю тщательно подумать, можете ли вы сделать свой объект неизменным.Тогда некоторая адаптация шаблона проектирования Builder / Factory позволит вам исключить использование свойств.

Вы также можете подумать о создании генератора кода.В случае, если вы выберете хорошие ключевые слова для разметки, это может быть легко.

ОБНОВЛЕНИЕ Я добавил код, чтобы уточнить мое предложение.

Прежде всего мы подготовим несколько вспомогательных объектов.Они должны быть помещены в заголовочный файл, доступный как для библиотеки, так и для клиента.Эти объекты никогда не будут изменены.

GetSetProp <> ----> IGetSetProp <----- PropFunctionAdapter </p>

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();
    }
};

Аналогичным образом вы можете определить GetProperty и SetProperty.

Предположим, у вас есть контракт данных, содержащий два поля Pressure: int и Description: string.Затем вы определяете данные контракта:

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() {}
};

и его реализацию в библиотеке:

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...