1

I realize that I'll most likely get a lot of "you shouldn't do that because..." answers and they are most welcome and I'll probably totally agree with your reasoning, but I'm curious as to whether this is possible (as I envision it).

Is it possible to define a type of dynamic/generic object in C++ where I can dynamically create properties that are stored and retrieved in a key/value type of system? Example:

MyType myObject;

std::string myStr("string1");

myObject.somethingIJustMadeUp = myStr;

Note that obviously, somethingIJustMadeUp is not actually a defined member of MyType but it would be defined dynamically. Then later I could do something like:

if(myObject.somethingIJustMadeUp != NULL);

or

if(myObject["somethingIJustMadeUp"]);

Believe me, I realize just how terrible this is, but I'm still curious as to whether it's possible and if it can be done in a way that minimizes it's terrible-ness.

3
  • 1
    A map with mapped type of boost::variant or boost::any comes to mind; perhaps a boost::optional<boost::variant> even. Commented Dec 10, 2011 at 16:28
  • 1
    object is a very confusing name for a type. Commented Dec 22, 2011 at 14:04
  • Thx, dunno why I keep doing that lol Commented Dec 22, 2011 at 14:05

5 Answers 5

3

C++Script is what you want!

Example:

#include <cppscript>

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

and it's a valid C++.

Sign up to request clarification or add additional context in comments.

9 Comments

Hmmmm this is very interesting. I wonder what other peoples thoughts are on this. Never heard if it and although it does look like it supports all the things I'd like, I also want to release a final product that people are going to use and not go "ewwwww he used C++Script...." or something. Know what I mean? Very cool tho I'll check it out.
Alright, this seems to be a completely different language (a scripting language with its own command prompy utility to execute it), which is written in C++. So can it be used in normal C++ program and then compile into machine code, using say GCC?
People are going to dislike this style of programming if they have chosen c++ as there language, HOWEVER most people would rather you used a good library (which I do not know whether or not this is) then you try and reinvent it yourself just without the documentation.
@Nawaz: it is C++, compiled with GCC.
@ybungalobill: But on the site, it says it requires cppscript utility to execute it from command prompt : calumgrant.net/cppscript/docs/basics.html
|
2

You can do something very similar with std::map:

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

Now if you want generic value types, then you can use boost::any as:

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

And you can also check if a value exists or not:

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

If you use boost::any, then you can know the actual type of value it holds, by calling .type() as:

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

This also shows how you can use boost::any_cast to get the value stored in object of boost::any type.

3 Comments

Very cool thank you. I'm gonna look into this a little further.
Uhhh just one question. What about putting this into a class? I guess I could overload the [] operator of my custom class (which would contain a std::map within) and then delegate such a call to the map object? Just to be seamless.. so I'm not having to say myClass.myMap.find etc.
@AscensionSystems: Yes, wrapping such functionalities in a wrapper class, would help a lot.
2

This can be a solution, using RTTI polymorphism

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

tested with MINGW4.6 on WinXPsp3

1 Comment

Wow very interesting and great answer. Thanks for taking the time to posting this! Wish I could upvote more than once.
1

Yes it is terrible. :D

It had been done numerous times to different extents and success levels.

QT has Qobject from which everything related to them decends.

MFC has CObject from which eveything decends as does C++.net

I don't know if there is a way to make it less bad, I guess if you avoid multiple inheritance like the plague (which is otherwise a useful language feature) and reimplement the stdlib it would be better. But really if that is what you are after you are probably using the wrong language for the task.

Java and C# are much better suited to this style of programming.

#note if I have read your question wrong just delete this answer.

3 Comments

Yeah I'm aware it's the language for the task haha, I'm actually re-writing the core of the flash player in C++... and I'm considering all of these things because in the end I want to be able to have a front end for say LLVM or something that will take in existing actionscript 3 code and convert it to C++ based on my source. Unfortunately features like this are extremely abused in actionscript 3 (as if them existing in the first place isn't already abuse :D) and I have to come up with a way to accommodate them.
Qt's QObject is not dynamic type, neither it simulates dynamic behavior in any way. It is more like C#'s Object. That is, QObect is the ultimate base of all Qt's classes.
Sorry I read it more as though you you were after total polymorphism and less like javascript.
1

Check out Dynamic C++

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.