Динамический объект в C ++? - PullRequest
1 голос
/ 10 декабря 2011

Я понимаю, что я, скорее всего, получу много ответов типа «ты не должен этого делать, потому что ...», и они очень приветствуются, и я, вероятно, полностью согласен с твоими рассуждениями, но мне любопытно, как возможно ли это (как я это представляю).

Можно ли определить тип динамического / универсального объекта в C ++, где я могу динамически создавать свойства, которые хранятся и извлекаются в системе типа ключ / значение? Пример:

MyType myObject;

std::string myStr("string1");

myObject.somethingIJustMadeUp = myStr;

Обратите внимание, что, очевидно, somethingIJustMadeUp на самом деле не является определенным членом MyType, но он будет определяться динамически. Потом я мог бы сделать что-то вроде:

if(myObject.somethingIJustMadeUp != NULL);

или

if(myObject["somethingIJustMadeUp"]);

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

Ответы [ 5 ]

3 голосов
/ 10 декабря 2011

C ++ Script - это то, что вы хотите!

Пример:

#include <cppscript>

var script_main(var args)
{
    var x = object();
    x["abc"] = 10;
    writeln(x["abc"]);
    return 0;
}

и это действительный C ++.

2 голосов
/ 13 декабря 2011

Это может быть решение с использованием полиморфизма RTTI

#include <map>
#include <memory>
#include <iostream>
#include <stdexcept>

namespace dynamic
{

    template<class T, class E>
    T& enforce(T& z, const E& e)
    { if(!z) throw e; return z; }

    template<class T, class E>
    const T& enforce(const T& z, const E& e)
    { if(!z) throw e; return z; }

    template<class Derived>
    class interface;

    class aggregate;

    //polymorphic uncopyable unmovable
    class property
    {
    public:
        property() :pagg() {}
        property(const property&) =delete;
        property& operator=(const property&) =delete;
        virtual ~property() {} //just make it polymorphic

        template<class Interface>
        operator Interface*() const
        {
            if(!pagg) return 0;
            return *pagg; //let the aggregate do the magic!
        }

        aggregate* get_aggregate() const { return pagg; }
    private:

        template<class Derived>
        friend class interface;

        friend class aggregate;

        static unsigned gen_id()
        {
            static unsigned x=0;
            return enforce(++x,std::overflow_error("too many ids"));
        }

        template<class T>
        static unsigned id_of()
        { static unsigned z = gen_id(); return z; }

        aggregate* pagg;
    };

    template<class Derived>
    class interface: public property
    {
    public:
        interface() {}
        virtual ~interface() {}
        unsigned id() const { return property::id_of<Derived>(); }
    };


    //sealed movable
    class aggregate
    {
    public:
        aggregate() {}
        aggregate(const aggregate&) = delete;
        aggregate& operator=(const aggregate&) = delete;

        aggregate(aggregate&& s) :m(std::move(s.m)) {}
        aggregate& operator=(aggregate&& s)
        { if(this!=&s) { m.clear(); std::swap(m, s.m); } return *this; }

        template<class Interface>
        aggregate& add_interface(interface<Interface>* pi)
        {
            m[pi->id()] = std::unique_ptr<property>(pi);
            static_cast<property*>(pi)->pagg = this;
            return *this;
        }

        template<class Inteface>
        aggregate& remove_interface()
        { m.erase[property::id_of<Inteface>()]; return *this; }

        void clear() { m.clear(); }

        bool empty() const { return m.empty(); }

        explicit operator bool() const { return empty(); }

        template<class Interface>
        operator Interface*() const
        {
            auto i = m.find(property::id_of<Interface>());
            if(i==m.end()) return nullptr;
            return dynamic_cast<Interface*>(i->second.get());
        }

        template<class Interface>
        friend aggregate& operator<<(aggregate& s, interface<Interface>* pi)
        { return s.add_interface(pi);  }

    private:
        typedef std::map<unsigned, std::unique_ptr<property> > map_t;
        map_t m;
    };

}


/// this is a sample on how it can workout

class interface_A: public dynamic::interface<interface_A>
{
public:
    virtual void methodA1() =0;
    virtual void methodA2() =0;
};

class impl_A1: public interface_A
{
public:
    impl_A1() { std::cout<<"creating impl_A1["<<this<<"]"<<std::endl; }
    virtual ~impl_A1() { std::cout<<"deleting impl_A1["<<this<<"]"<<std::endl; }
    virtual void methodA1() { std::cout<<"interface_A["<<this<<"]::methodA1 on impl_A1 in aggregate "<<get_aggregate()<<std::endl; }
    virtual void methodA2() { std::cout<<"interface_A["<<this<<"]::methodA2 on impl_A1 in aggregate "<<get_aggregate()<<std::endl; }
};

class impl_A2: public interface_A
{
public:
    impl_A2() { std::cout<<"creating impl_A2["<<this<<"]"<<std::endl; }
    virtual ~impl_A2() { std::cout<<"deleting impl_A2["<<this<<"]"<<std::endl; }
    virtual void methodA1() { std::cout<<"interface_A["<<this<<"]::methodA1 on impl_A2 in aggregate "<<get_aggregate()<<std::endl; }
    virtual void methodA2() { std::cout<<"interface_A["<<this<<"]::methodA2 on impl_A2 in aggregate "<<get_aggregate()<<std::endl; }
};

class interface_B: public dynamic::interface<interface_B>
{
public:
    virtual void methodB1() =0;
    virtual void methodB2() =0;
};

class impl_B1: public interface_B
{
public:
    impl_B1() { std::cout<<"creating impl_B1["<<this<<"]"<<std::endl; }
    virtual ~impl_B1() { std::cout<<"deleting impl_B1["<<this<<"]"<<std::endl; }
    virtual void methodB1() { std::cout<<"interface_B["<<this<<"]::methodB1 on impl_B1 in aggregate "<<get_aggregate()<<std::endl; }
    virtual void methodB2() { std::cout<<"interface_B["<<this<<"]::methodB2 on impl_B1 in aggregate "<<get_aggregate()<<std::endl; }
};

class impl_B2: public interface_B
{
public:
    impl_B2() { std::cout<<"creating impl_B2["<<this<<"]"<<std::endl; }
    virtual ~impl_B2() { std::cout<<"deleting impl_B2["<<this<<"]"<<std::endl; }
    virtual void methodB1() { std::cout<<"interface_B["<<this<<"]::methodB1 on impl_B2 in aggregate "<<get_aggregate()<<std::endl; }
    virtual void methodB2() { std::cout<<"interface_B["<<this<<"]::methodB2 on impl_B2 in aggregate "<<get_aggregate()<<std::endl; }
};


int main()
{

    dynamic::aggregate agg1;
    agg1 << new impl_A1 << new impl_B1;

    dynamic::aggregate agg2;
    agg2 << new impl_A2 << new impl_B2;

    interface_A* pa = 0;
    interface_B* pb = 0;
    pa = agg1; if(pa) { pa->methodA1(); pa->methodA2(); }
    pb = *pa;  if(pb) { pb->methodB1(); pb->methodB2(); }

    pa = agg2; if(pa) { pa->methodA1(); pa->methodA2(); }
    pb = *pa;  if(pb) { pb->methodB1(); pb->methodB2(); }

    agg2 = std::move(agg1);
    pa = agg2; if(pa) { pa->methodA1(); pa->methodA2(); }
    pb = *pa;  if(pb) { pb->methodB1(); pb->methodB2(); }

    return 0;
}

, протестированного с MINGW4.6 на WinXPsp3

2 голосов
/ 10 декабря 2011

Вы можете сделать что-то очень похожее с std::map:

std::map<std::string, std::string> myObject;
myObject["somethingIJustMadeUp"] = myStr;

Теперь, если вам нужны универсальные типы значений, вы можете использовать boost::any как:

std::map<std::string, boost::any> myObject;
myObject["somethingIJustMadeUp"] = myStr;

И вы также можете проверить, существует ли значение:

if(myObject.find ("somethingIJustMadeUp") != myObject.end())
    std::cout << "Exists" << std::endl;

Если вы используете boost::any, то вы можете узнать фактическое тип значения, которое он содержит, вызывая .type() как:

if (myObject.find("Xyz") != myObject.end())
{
  if(myObject["Xyz"].type() == typeid(std::string))
  {
    std::string value = boost::any_cast<std::string>(myObject["Xyz"]);
    std::cout <<"Stored value is string = " << value << std::endl;
  }
}

Это также показывает, как вы можете использовать boost::any_cast, чтобы получить значение, хранящееся в объекте boost::any тип.

1 голос
/ 10 декабря 2011
1 голос
/ 10 декабря 2011

Да, это ужасно.: D

Это было проделано много раз с разными степенями и уровнями успеха.

У QT есть Qobject, от которого зависит все, что с ними связано.

У MFC есть CObject, от которого происходит все, что и C ++. Net

Я не знаю, есть ли способ сделать его менее плохим, я думаю, если вы избежите множественного наследования, такого как чума(что в противном случае является полезной функцией языка) и переопределить stdlib было бы лучше.Но на самом деле, если это то, что вам нужно, вы, вероятно, используете не тот язык для этой задачи.

Java и C # гораздо лучше подходят для этого стиля программирования.

# обратите внимание, если у меня естьпрочитайте ваш вопрос неправильно, просто удалите этот ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...