Pertanyaan c ++ diturunkan dari abstrak murni dengan nested struct


Saya mencoba untuk membuat gula sintaksis yang mirip dengan konsep properti c #.

Saya telah membaca posting ini: C # -seperti sifat di c ++ asli?. Ini membantu, tetapi tidak memiliki desain yang saya inginkan. Saya juga tidak setuju dengan beberapa pernyataan yang dibuat di sana bahwa konsep propertinya buruk, karena saya gagal melihat perbedaan antara sekumpulan metode yang berjudul get_id () dan set_id () dan operator yang berlebihan yang mengekspos konsep yang sama, memungkinkan kode menjadi lebih bersih ketika kelas dikonsumsi, dan umumnya putus asa akses publik ke bidang swasta, juga memisahkan implementasi publik dari desain pribadi.

Namun, kode yang saya dapatkan (terinspirasi oleh tautan itu) BENAR-BENAR sulit dan akan sulit dipertahankan. Saya ingin tahu apakah ada yang punya saran untuk membersihkan ini, atau yang lebih penting, tahu cara yang lebih baik untuk melakukan ini.

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: Itu terjadi pada saya bahwa saya tidak mengungkapkan apapun tentang niat saya dengan ini. Ini akan digunakan dalam program penjadwalan, dan kami akan melakukan banyak perbandingan dan penugasan semua data di kelas. 'someClass' secara efektif akan menjadi antarmuka pada data (sangat sedikit metode yang dibutuhkan, banyak data yang harus relatif transparan). Ini akan didefinisikan dalam pustaka statis yang akan di-link oleh eksekutabel. 'derivedClass' secara efektif akan menjadi implementasi eksternal, diimplementasikan dalam sebuah dll yang akan dimuat secara dinamis. Alasannya adalah untuk mengaktifkan "hot swapping" dari dll dengan yang lain yang mengimplementasikan backend file yang berbeda. Kami memiliki rencana untuk mengimplementasikan backend penyimpanan xml, sqlite, dan mysql menggunakan sistem plugin untuk memuatnya.

Jadi pada dasarnya, saya membutuhkan desain yang memungkinkan someClass menjadi antarmuka virtual yang diwarisi oleh derivedClass, yang dimuat oleh metode pabrik, melewati sistem plugin, dan digunakan pada akhirnya oleh consumerClass.


5
2018-02-18 23:46


asal


Jawaban:


Jadi pada dasarnya, saya membutuhkan desain yang memungkinkan someClass menjadi virtual   antarmuka

Jika saya memahami Anda dengan benar, niat ini bertentangan dengan solusi yang Anda hadapi. Warisan virtual hanya menyangkut fungsi anggota virtual. Dan tidak ada cara Anda dapat secara dinamis mewarisi nested structs karena ini adalah subjek dari meta-programming leksikal yang tidak didukung dalam C ++.

Saran saya adalah berpikir dengan hati-hati jika Anda dapat membuat objek Anda tidak berubah. Kemudian beberapa adaptasi pola desain Builder / Factory akan memungkinkan Anda untuk menghilangkan penggunaan properti.

Anda juga dapat mempertimbangkan untuk menulis generator kode. Dalam kasus jika Anda mengambil kata kunci markup yang baik, itu mungkin mudah.

MEMPERBARUI Saya telah menambahkan beberapa kode untuk mengklarifikasi saran saya.

Pertama-tama kami menyiapkan beberapa objek tambahan. Mereka harus dimasukkan ke dalam file header yang dapat diakses baik untuk Perpustakaan maupun Klien. Objek-objek ini tidak akan pernah dimodifikasi.

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

Demikian pula Anda dapat mendefinisikan GetProperty dan SetProperty.

Misalkan Anda memiliki kontrak data yang berisi dua bidang Tekanan: int dan Keterangan: string. Kemudian Anda mendefinisikan kontrak data:

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

Dan implementasinya di perpustakaan:

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;

1
2018-02-22 10:07



Membuat kelas pembantu properti yang harus dipromosikan sebagai anggota kelas yang ingin Anda tambahkan properti adalah ide yang buruk, karena Anda akan memiliki biaya anggota properti untuk semua instance kelas ini.

Solusi yang menawarkan mekanisme "properti" dekat dengan apa yang ditawarkan bahasa lain dapat tersedia di C ++ menggunakan sesuatu seperti yang saya jelaskan nanti.

Satu-satunya batasan adalah bahwa Anda mengakses properti menggunakan

instance.property (); // mendapatkan

dan

instance.property () = 42; // untuk mengatur

#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
2018-02-23 11:43