ponder
ponder copied to clipboard
Consistent behaviour regarding const objects
Consider a simple class with a getter and a setter
class MyClass
{
public:
MyClass() : i_(0) {};
int Getter() const { return i_; };
MyClass& Setter(int i) { i_ = i; return *this; };
private:
int i_;
};
that is declared to ponder
PONDER_TYPE(MyClass)
ponder::Class::declare<MyClass>("MyClass")
.constructor()
.property("Object", &MyClass::Getter, &MyClass::Setter);
If I have a const reference to an object of this class I am not allowed to modify it. With a const ponder::UserObject, on the other hand, modifying this object is possible:
const MyClass myClassObject;
const MyClass& refToMyClassObject = myClassObject;
refToMyClassObject.Setter(41); // COMPILATION ERROR! Not allowed to modify a const object
const ponder::UserObject myClassUserObject(refToMyClassObject);
myClassUserObject.set("Object", 42); // No compilation error, const object gets modified
assert(refToMyClassObject.Getter() == 42);
With all that const around that behaviour may be counterintuitive.
From my point of view, what possibilities would exist to accomplish a consistent behaviour:
1. Do not allow to make a ponder::UserObject from a const object
Reasoning: WYSIWYG (= what you see [in the interface] is what you get). This would mean that in ponder e.g.
template <typename T>
UserObject(const T& object);
would have to be changed to
template <typename T>
UserObject(T& object);
I guess this is not a very attractive option. If you like to reflect on a const object (e.g. to serialize it) and plan to only read its properties, you would have to apply a const_cast which is always something you like to avoid.
2. Allow a ponder::UserObject to be made from and to modify a const object consequently
Reasoning: Reflection is something that exceeds the normal possibilities of a language.
In this case, ponder's interface could essentially stay as it is now. To be consequent, though,
inline UserObject Class::getUserObjectFromPointer(void* ptr) const
{
return m_userObjectCreator(ptr);
}
should in my opinion then be changed to
inline UserObject Class::getUserObjectFromPointer(const void* ptr) const
{
return m_userObjectCreator(ptr);
}
Of course, it would also have to be emphasized in the documentation that with ponder's reflection you are able to modify const objects.
3. Have a ponder::UserObject support a read-write as well as a read-only mode
Reasoning: Once againg WYSIWYG. With this option, if you make a ponder::UserObject from some const object this would then be the read-only mode where you would not be allowed to modify this object.
I guess this would be the best option, yet associated with the most effort.
What do you think? Was ponder intentionally designed for option 2? In any case, option 2 is what ponder now supports. Hence I would be happy if you would approve the modification I suggest in option 2 (which would avoid the const_casts that I now need to reflect on objects for which I have a const void*.)
It's a tough one and perhaps needs a different angle. I did think about refactoring UserObject and making it policy based. I.e. you would pass it the traits that you would like it to have, like class member declarations can currently.
A UserObject currently owns a data holder, so two objects are allocated. This might be collapsed into one for some purposes. I never really got past that stage though.
In the case of const, as you mention, because of language constraints, perhaps opaque handles need to be used and a similar API to the runtime calling.