Nope. That’s not how it works at all, I’m afraid! Like I said, the only way to do it is to return a special intermediary object with cast and assignment operators that do the right thing. A lot of the well-known C++ books talk about this in more detail.
Thanks jules for the feedback
Although technically possible, I understand that the solution is not a good one. I had that feeling myself when copying one method to non const after another, but feeling and science is not the same.
I hoped there was just something I didn’t know, to make it a good solution…
But anyway, I give up…
I recently learned that std::map has the ability to do the following:
mymap["new key"] = foo;
as per http://en.cppreference.com/w/cpp/container/map/operator_at
- Inserts value_type(key, T()) if the key does not exist. This function is equivalent to return insert(std::make_pair(key, T())).first->second;
-key_type must meet the requirements of CopyConstructible.
-mapped_type must meet the requirements of CopyConstructible and DefaultConstructible.
If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.
Can we talk about updating the JSON/var classes to just be a wrapper for std::map? minimum requirement: C++11!!
Use this and never look back:
Single header file to include. Super easy to use. Using it right now and it works like a charm.
wow, that has some cool features in it.
Cool, thanks for posting.
It even supports a standard binary representation (CBOR). I wondered, why not BSON though…
Ah, I wish I could have used some of those C++11 tricks when I wrote my JSON parser. Should go back and throw some variadic template stuff in there for easy creation of structures…
Why reinvent the wheel? This works and is maintained. Why add to your considerable workload?
yes you should! but for v4.3.1 lol
@reFX probably because it needs to work with the juce::var and juce::ValueTree classes? or maybe the license agreement? iono
MIT license is VERY permissive. Adding support for some JUCE objects should be trivial.
Even just pointing to it, as an alternative, in the documentation would help a lot.
@jules did you get a chance to take a look at adding mymap["new key"] = foo;
functionality to the JSON/VAR classes?
No, haven’t looked at that yet.
can we get a bump bump (woot woot) for looking at adding mymap["new key"] = foo;
functionality to the JSON/VAR classes, by way of how std::map
lets you insert keys into the map via that same syntax?
@jules is there a reason that var::operator[](int)
returns a var&
, but var::operator[](const Identifier&)
returns a const var&
?
Also, why do you have var::convertToArray()
but not var::convertToObject()
?
maybe something like this?
var& var::convertToObject()
{
if( !isObject() )
{
var tempVar( new DynamicObject() );
tempVar.getDynamicObject()->setProperty("var", *this);
*this = v;
}
return *this;
}
Well if you look at the implementation:
const var& var::operator[] (const Identifier& propertyName) const
{
if (auto* o = getDynamicObject())
return o->getProperty (propertyName);
return getNullVarRef();
}
…it can’t return a writable var because if you give it a non-existent identifier, it returns a global placeholder null.
The other one that takes an int has a precondition that asserts if the value is out of range, so it can always return a reference to an existing var, so it can be writable.
Your explanation doesn’t really explain why, if the dynamic object HAS the property with name propertyName
, the returned value is const ref
.
And why can’t you have this:
var& var::operator[] (const Identifier& propertyName) const
{
if (auto* o = getDynamicObject())
{
if( !o->hasProperty(propertyName) )
o->setProperty(propertyName, {});
return o->getProperty (propertyName);
}
convertToObject();
return operator[](propertyName);
}
I get that there needs to be some decision made about what happens to the current var
value when you convert a var
to a DynamicObject
, which probably the reason that this ability doesn’t exist for the class. But this really seems like a constraint was placed on the class’s abilities that never needed to exist. Maybe the class should just always be a wrapper around a DynamicObject, and when you write code like var v = 2.5;
, internally you’re doing
var::var(double d)
{
var tempVar( new DynamicObject() );
tempVar.getDynamicObject()->setProperty("_double_", d);
*this = tempVar;
}
and then the operator double()
just looks for the _double_
property…
std::map() seems to have solved this already:
http://www.cplusplus.com/reference/map/map/operator[]/
Note that std::map::operator[]
is not const, because it modifies the map if the given key does not exist.
The equivalent in stl is std::map::at()
, which is const but which similarly cannot be used to insert new keys.
Yes, the whole point is to be able to modify the var’s DynamicObject
via var::operator[](const Identifier&)
, which may or may not involve converting the var
to a var( new DynamicObject() );
as opposed to whatever type of var
it currently is.
How could it just silently convert a var to a dynamic object when you call its operator ?
Converting a var to an array is a sensible thing to do, because the current value just becomes the first item in the new array. But think about all the ambiguities and edge-cases in converting an arbitrary var to an object: would the old value just be thrown away? It’d be an awful piece of UX if trying to read a property from it could end up modifying the original var and losing its value!
I agree. there would need to be some standardized interface that would allow var
's to always be DynamicObject
's that implicitly convert to whatever type they’re holding, if they only hold one value. once you add via operator[](const Identifier&)
, the implicit conversion would need to still work somehow, maybe via some kind of .first()
internally. It’s an ugly problem with no clean solutions. I can see why you never made it happen for the var
class. It would be sweet if the functionality existed, though…