From: Andrew G. <ag...@em...> - 2003-02-28 02:37:38
|
I'm going to be checking in changes to ZTuple. This message explains the motivation for the changes, and a little of how to take advantage of them. In many situations no application changes will be necessary. If you use AddXXX, or GetXXX with an index, or if you have serialized tuples then you'll need to read this in more detail. I put together ZTuple when I was living on Downey Street in San Francisco, so that must have been 1996. That version had a rather neat API that overloaded operator[] to return a value reference, which had assignment and conversion operators for each of the supported types. To exactly specify the type to be stored or retrieved one simply used appropriate casts. Very neat, but actually quite unpleasant to use because we'd end up using casts all the time just be sure of what was happening: theTuple["fred"] = 1; is a bit ambiguous to the reader -- which of the standard int types will the compiler map an unadorned '1' to? To be sure we'd have to write: theTuple["fred"] = int32(1); Ugh! Porting ZooLib to BeOS a couple of years later inspired the revamping of ZTuple to use an API similar to BeOS's BMessage, which is the API we have now. It's less sophisticated, but much more predictable in day to day use. My original idea was that ZTuple/ZMessage would have a swappable underlying representation, and that on BeOS we'd be able to use BMessage as the actual storage medium (on the basis that the OS vendor will have optimized the performance more than I'm capable of). So having an exactly equivalent API was useful. BeOS did not have a system-wide representation of lists of things, at least as far as BMessage and its supported types were concerned. So to allow the storage of lists of values under a single property name their API has an additional index parameter on getter methods. The normal setter method was AddXXX, which would add the passed in value to the list stored under a particular name, establishing the name if it wasn't already there. ReplaceXXX and RemoveXXX filled out the list-like API. However, it's still an API that's not as rigorous as I'd like. There's no difference between a property with a single value and a property with a list of values containing a single item, they're represented identically. In some situations that's actually a nice thing. But there's no simple way to establish a property with an empty list of values, so code tends to treat the absence of a property as equivalent to an empty list, which may not always be what's wanted. For example if you want to communicate that something was looked for but nothing was found, vs not having looked for the thing at all. In addition, calling AddXXX(theName, someXXX) on a tuple which already contains theName results in someXXX getting appended to the values already there. How can you be sure that what you're putting in place is what you'll later retrieve? Well, on BeOS, you have to mess about with code that invokes FindXXX, then calls ReplaceXXX or AddXXX appropriately. To ameliorate this particular weakness I enhanced the API with SetXXX methods that unconditionally overwrite whatever may already be stored under a name with a single value -- SetXXX guarantees that after it's called there will be a property with that single value. And that's the API I've tried to use unless I'm really expecting to be building a list. I've recently gone back through ZooLib and ensured that I'm not doing an AddXXX when I really mean SetXXX. Although ZTuple can store and work with lists of values, *we* can't treat lists of values generically as we do with the other types ZTuple supports. So this: theTuple.SetValue("theName", otherTuple.GetValue("otherName")); doesn't transfer every value stored by otherTuple under "otherName" to theTuple under "theName" -- in fact it copies only the first value to theTuple. The reason for this is that it's ZTuple that's doing the vector stuff rather than ZTupleValue, and ZTupleValue is what's returned by ZTuple::GetValue. The original (circa 1996) ZTupleValue implementation *was* able to store various primitive types, strings, tuples and vectors of ZTupleValues. So treating lists polymorphically with other types was easy, and ZTuple didn't get involved. With the impending profusion of ZTuple-based code it seemed time to clear up all these things that have been bugging me. So I've revamped ZTupleValue and ZTuple, maintaining the parts of the Get/Set API that's proven effective, having ZTupleValue do the work of storing vectors of other ZTupleValues, and losing the AddXXX API in favor of a more or less equivalent suite of AppendXXX methods. AppendXXX will establish a name if it's not already present. If it is present and the stored value is already a vector then the passed in value is simply appended to it. If it's not a vector, or doesn't exist at all, then we establish a value which is a vector with a single entry, the value that was passed in. So where you might have had a loop: for (int x = 0; x < 10; ++x) theTuple.AddInt32("aName", x); you can do this: for (int x = 0; x < 10; ++x) theTuple.AppendInt32("aName", x); and get exactly the same effect, unless the property "aName" already had a non-vector value in it. In actual use this doesn't happen. Or at least it won't if the informal protocol by which tuples are build up and consumed is sufficiently well defined. It really should be the case that a particular property is expected to have multiple values, or its expected to have exactly one. For pre-existing code that wants to be able to deal with vectors of values and single values identically, perhaps because the tuples are coming from permanent storage and contain vectors of values that just happened to have only a single value, we have a handful of GetXXXAt methods. We might need to expand them out to include all data types, but for now I've put in place only the ones I've seen used. The companion method to GetXXXAt is CountValuesAt: for (int x = 0; x < theTuple.CountValuesAt("aName"); ++x) { int32 anInt32 = theTuple.GetInt32At("aName", x); ... } [Of course it's better to get the count once at the beginning, and to convert the name into a property number, but this is just for expositional purposes.] The 'more modern' equivalent, where we're being more rigorous in constructing tuples and extracting data from them, we'd do something like this: theTuple.SetEmptyVector("aName"); for (int x = 0; x < 10; ++x) theTuple.GetVectorMutable("aName").push_back(x); or vector<ZTupleValue>& theVector = theTuple.SetEmptyVector("aName"); for (int x = 0; x < 10; ++x) theVector.push_back(x); or indeed any STL container-stuffing algorithm could be used to populate theVector. To extract data: const vector<ZTupleValue>& theVector = theTuple.GetVector("aName"); for (vector<ZTupleValue>::const_iterator i = theVector.begin(); i != theVector.end(); ++i) { int32 theInt = (*i).GetInt32(); ... } NOTE: GetVector and GetVectorMutable return const and non-const *references*, so you must not make mutating changes to the enclosing tuple that could invalidate the references. To support reading of tuples and tuplevalues that are already in backing store ZTuple and ZTupleValue have FromStreamOld methods that will do the right thing with old tuples -- singleton values come in as singleton values, multiple values come in as vectors. I've extended the ZType enum. The new values are eZType_Vector (of course), eZType_ID and eZType_Type. eZType_ID holds a 64 bit value, and can be distinguished from a regular 64 bit integer (necessary for tuplestores). eZType_Type is a confusing name, but is used to indicate that the content of a ZTupleValue is a ZType enum. With this we're able to describe the structure of a tuple using a tuple. In summary, we've still got the same functionality as before, albeit with name changes to make sure existing code fails to compile and can be updated to use the new API if appropriate. We're also able to apply STL algorithms to tuple contents in a way that wasn't possible before. The specification of a tuple now more closely matches associative arrays in other languages, in particular Cocoa's dictionaries. A+ -- Andrew Green mailto:ag...@em... Electric Magic Co. Vox/Fax: +1 (408) 907 2101 |