
JOAQUIN LOPEZ MU?Z ha scritto:
OK, I'd classify these two examples as scenarios where K and T contain esentially the same info but the translation function f() is computationally expensive. This is a valid context, just not the one I deem the most common. Maybe the particular case K!=T could be solved by a different class keyed_flyweight used like this (the example is similar to that of GoF you referred to):
struct character { character(char c):c(c){} char c; };
int main() { // fw_t objects are constructed from chars but are convertible // to character. typedef keyed_flyweight<char,character> fw_t;
// creates an internal character('a') fw_t x1('a');
// same shared value as x1, zero temporary character's fw_t x2('a');
// creates an internal character('b') fw_t x3('b'); }
Is this approach more convincing to you? It turns out it can be implemented with exactly the same machinery as flyweight (i.e. holders, factories, locking and tracking policies), so it could be provided as a companion to classic flyweight<> if there is interest in it. The attached code shows the idea is viable, the prototype keyed_flyweight implemented there has all their aspects (holder, factory, etc.) fixed for simplicity, and lacks some boilerplate features, but it proves its point. The idea is that the factory keeps elements of type similar to
entry = pair<K,T*>
So, only when a new entry is created need we equip it with a new T, hence completely avoding T temporaries. This incurs a cost in the form of additional storage and a slightly more expensive creation process (when no duplicates are found), so we shouldn't take flyweight<T> as shorthand for keyed_flyweight<T,T>, both classes serve different scenarios and ought to be provided separately.
Well, if you think this line of research is interesting I can pursue it.
First of all, thanks for having considered my feedback. I was tempted to say that I liked the keyed_flyweight approach, but I preferred reading the rest of the thread before replying. When I reached the post of yours where you mention the "third" case where the keys are stored inside or can otherwise be computed from values, I realized that maybe we could not only get that case, but let it all happen (including the non-keyed case) with just one flyweight class template. Here's how I see it: 1) we have two types: key_type for keys and value_type for values. The two types can be coincident (as in your non-keyed proposal) or be different (keyed case) 2) the constructor of flyweight semantically takes keys, not values 3) keys (not values) are fed as-is to the factory insert() method: here's the key point, it's up to the factory to determine whether the key is already present and if it's not present to construct the corresponding value. There's actually no need for a find() method. The flyweight template need not know how to compare keys with values, actually it does not even need to know that it is happening. Let me say it again with different words: the flyweight/factory interface has only one method, namely insert() as in the current design. A possible map-base factory might implement the factory::insert() method as a double call map::find() and map::insert(), this would just be an implementation detail of the factory, the flyweight machinery needs not be aware of that. 4) the insert() method returns a handle 5) the factory provides a way to extract values from handles The rest of the design would more or less stays exactly the same. The "third" case can easily be implemented in this framework using Boost.MultiIndex. Does it make sense? Is there something obvious (o even less obvious :) that I'm missing?
Well, thank you for giving me plenty of food for thought :)
You're welcome! :-) Ganesh