billyquith / ponder

C++ reflection library with Lua binding, and JSON and XML serialisation.
http://billyquith.github.io/ponder/
Other
642 stars 95 forks source link

Extend objects in runtime #9

Closed dasloop closed 8 years ago

dasloop commented 8 years ago

Hi,

How difficult will be to add to ponder the possibility to expand objects with new attributes in runtime? For example:

const ponder::Class& metaclass = ponder:: classByName( "Person" );

metaclass.extension_property( "salary", double  );

ponder::UserObject john = metaclass.construct(ponder::Args("John"));
john.set( "salary", 100000 );
billyquith commented 8 years ago

So this is to make Ponder dynamic? E.g. in Python:

class Person:
    def __init__(self, name):
        self.name = name
        self.age = 0

metacls = Person
john = metacls("John")

john.salary = 0.0  # 1) per instance

def PersonFactory(name, salary=0.0):
    p = Person(name)
    p.salary = salary    # 2) add to all instances
    return p

john2 = PersonFactory("John")

In a dynamic language this is pretty easy as the instances and meta-classes can be manipulated at runtime. In the first example above we add salary "per instance". Second example and if we use a factory to create all of the instances we can manipulate them before returning them.

This is not the same in C++/Ponder as we are exposing concrete classes, all under the same template (Person). The values are contained in the class instance (i.e. a newd instance of the class). Where would the extension property value be stored?

To add arbitrary data we could do something like use a dictionary of variants? This would be pretty inefficient in C++ but pretty much what Python is doing when you instance a class, or use a dictionary. E.g. std::map<std::string, variant>

So is what you want a variant dictionary?

dasloop commented 8 years ago

In some applications both things are required (reflection and runtime extension). For example for a GIS application.

The extensibility can be provided by an external lib, using something a bit more sophisticated than a variant map. In any case the idea is that, you are correct.

I will request then two changes: 1) Add attributes at any time after setting up the Ponder Class (and not only when calling ponder::Class::declare for the first time) 2) For external attributes (can be even functions) call a external function with enough context (object, attribute id for gets, objects attribute id and variant for set).

Maybe 2 is better than include all the code in Ponder.

billyquith commented 8 years ago

Currently Ponder is a reflection library, which is different from making C++ dynamic (like Python). C# has reflection but is also not dynamic, you cannot add extra members to classes at runtime. If you want a dynamic language then you may be better off using Python or Lua. If you have C++ code you could expose to the dynamic language so they can talk to each other.

It's not that it isn't impossible to do but I don't really have a need for this functionality. I have other work. You are welcome to get latest, branch the code, alter it, and submit a pull request. Then we can discuss what you have done and if/how it will make it into Ponder.

dasloop commented 8 years ago

I will try then but, can you make a favour, can you implement the first request or let me know how to do it? To add attributes later on to an already defined class. Something like:

void declare() // register details (once)
{
    ponder::Class::declare<Person>("Person")
        .constructor<std::string>()
        .property("name", &Person::name)
        .function("speak", &Person::speak)
        ;
}

void declareAfterSomethingElse()
{
    const ponder::Class& metaclass = ponder::classByName("Person");
    metaclass.property("age", &Person::age, &Person::setAge);
}