[flyweight] some minor comments

Hi Joaquín, Your docs looks real nice. But I have the following suggestion: - make it possible to use boost::flyweight<T> without the use of factories. The number of elements I want to create is fairly low, and so does not justify having a seperate container "flying" around. -Thorsten PS: what a shame we can't overload operator.() in C++

----- Mensaje original ----- De: Thorsten Ottosen <thorsten.ottosen@dezide.com> Fecha: Jueves, Diciembre 13, 2007 9:55 pm Asunto: [boost] [flyweight] some minor comments Para: boost@lists.boost.org
Hi Joaquín,
Your docs looks real nice.
Thank you! I hope you can participate in the upcoming review.
But I have the following suggestion:
- make it possible to use boost::flyweight<T> without the use of factories. The number of elements I want to create is fairly low, and so does not justify having a seperate container "flying" around.
I'n not sure how flyweight could possibly work without a factory to keep the common values. Can you elaborate the scenario you have in mind?
PS: what a shame we can't overload operator.() in C++
A pity indeed. Stroustrup discusses the matter in his "The Design and Evolution of C++" and he himself does not have a strong opinion against/in favor of such feature. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

JOAQUIN LOPEZ MU?Z skrev:
----- Mensaje original ----- De: Thorsten Ottosen <thorsten.ottosen@dezide.com> Fecha: Jueves, Diciembre 13, 2007 9:55 pm Asunto: [boost] [flyweight] some minor comments Para: boost@lists.boost.org
Hi Joaquín,
Your docs looks real nice.
Thank you! I hope you can participate in the upcoming review.
Could be.
But I have the following suggestion:
- make it possible to use boost::flyweight<T> without the use of factories. The number of elements I want to create is fairly low, and so does not justify having a seperate container "flying" around.
I'n not sure how flyweight could possibly work without a factory to keep the common values. Can you elaborate the scenario you have in mind?
I would use it as a lightweight and more convenient replacement for boost::shared_ptr<const T>. In my application I might have < 50 objects that needs to by flyweights. They are almost never identical, and if they are, the memory occupied by the factory would probably be much larger than that of duplication. Also, the forced use of a factory places many extra requirements on T, operator== and hash function, which I consider limiting for the immediate usability of the library.
PS: what a shame we can't overload operator.() in C++
A pity indeed. Stroustrup discusses the matter in his "The Design and Evolution of C++" and he himself does not have a strong opinion against/in favor of such feature.
The current C++0x effort has been discussing it briefly, but it never got much attention AFAICT, sadly. A few papers discuss them: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2200.pdf "With one notable exception for the two dot operators which we currently believe cause more trouble than they fix." http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1671.pdf has a longer discussion. Btw, I couldn't remember from the code if you provided operator->() in this manner: const T* operator->() const; ? If not, maybe it would be worth adding. -Thorsten

Thorsten Ottosen <thorsten.ottosen <at> dezide.com> writes:
JOAQUIN LOPEZ MU?Z skrev:
----- Mensaje original ----- De: Thorsten Ottosen <thorsten.ottosen <at> dezide.com> Fecha: Jueves, Diciembre 13, 2007 9:55 pm Asunto: [boost] [flyweight] some minor comments Para: boost <at> lists.boost.org
[...]
- make it possible to use boost::flyweight<T> without the use of factories. The number of elements I want to create is fairly low, and so does not justify having a seperate container "flying" around.
I'n not sure how flyweight could possibly work without a factory to keep the common values. Can you elaborate the scenario you have in mind?
I would use it as a lightweight and more convenient replacement for boost::shared_ptr<const T>.
In my application I might have < 50 objects that needs to by flyweights. They are almost never identical, and if they are, the memory occupied by the factory would probably be much larger than that of duplication.
Well, then you might not need using the flyweight idiom after all... Not that it can't be done (it could with a custom "null" factory and a minor technical change), but it seems to defeat the whole point of the library. Note that not all value duplications would be detected and taken advantage of: no_factory_flyweight<std::string> str1="goddag"; no_factory_flyweight<std::string> str2="goddag"; no_factory_flyweight<std::string> str3=str2; str1 and str2 would hold pointers to different values, since you've got no factory to look for duplicates; str1 and str2 are completely disconnected, so to say. str2 and str3, on the other hand, would share their value.
Also, the forced use of a factory places many extra requirements on T, operator== and hash function, which I consider limiting for the immediate usability of the library.
These requirements are imposed by the special kind of factory used by default--a hashed one. Using some other factory, like for instance set factory (http://tinyurl.com/2oxpvr ), calls for different requirements, in the case of set_factory that T have operator<. One thing's true, no matter what kind of factory you use T has to have some way to be compared for equivalence of values.
Btw, I couldn't remember from the code if you provided operator->() in this manner:
const T* operator->() const;
? If not, maybe it would be worth adding.
No, I don't provide that, and I also think it's something worth adding. Tobias Schwinger also suggested this thing on offlist discussions with me. The only thing that puts me off a little is that this would clash, in a conceptual sense, with the current interface for getting a ref to T. In all, we'd have const T& get()const; operator const T&()const; const T* operator->()const; which is like a mix of Boost.Ref and Boost.Optional access semantics. It looks odd that a class behaves as a value and as a pointer at the same time. But of course there's nothing wrong about having both except the unusualness of it. Anyway, this is something I defintely will ask to be discussed upon during the review. Thank you, Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

Joaquin M Lopez Munoz skrev:
Thorsten Ottosen <thorsten.ottosen <at> dezide.com> writes:
I would use it as a lightweight and more convenient replacement for boost::shared_ptr<const T>.
In my application I might have < 50 objects that needs to by flyweights. They are almost never identical, and if they are, the memory occupied by the factory would probably be much larger than that of duplication.
Well, then you might not need using the flyweight idiom after all...
Well, it's lower overhead (if no factory and forwarding constructors/operations makes it a better choice than shared_ptrconst T>. -Thorsten

Thorsten Ottosen:
Joaquin M Lopez Munoz skrev:
Thorsten Ottosen <thorsten.ottosen <at> dezide.com> writes:
I would use it as a lightweight and more convenient replacement for boost::shared_ptr<const T>.
In my application I might have < 50 objects that needs to by flyweights. They are almost never identical, and if they are, the memory occupied by the factory would probably be much larger than that of duplication.
Well, then you might not need using the flyweight idiom after all...
Well, it's lower overhead (if no factory and forwarding constructors/operations makes it a better choice than shared_ptrconst T>.
It might be possible to do this using a custom factory along the lines of: template<class T> struct sp_factory { typedef boost::shared_ptr<T const> handle_type; handle_type insert( T const & t ) { return handle_type( new T( t ) ); } void erase( handle_type ) {} T const & entry( handle_type p ) { return *p; } }; but there's no way to tell from the documentation; it never describes how the Factory template parameter is actually used. http://svn.boost.org/svn/boost/sandbox/flyweight/libs/flyweight/doc/referenc... As I read it, a flyweight<> can ignore all of its template parameters beyond T and still conform to its spec, which is probably not what's intended.

More about http://svn.boost.org/svn/boost/sandbox/flyweight/libs/flyweight/doc/referenc... I don't understand the source of the complexity at the flyweight<> level. A "naive" flyweight<> could look like: template<class T, class F = default_factory> class flyweight { typedef typename F::handle_type handle_type; handle_type handle_; public: flyweight( ... args ): handle_( F::insert( T( args... ) ) ) {} T const& get() const { return F::value( handle_ ); } }; It seems to me that this provides the same amount of expressive power as the current interface. Locking/tracking/holding can be made parameters of the Factory if so desired, and a factory_core<F',L,T,H> template may be provided as a convenience in a separate header, but in the majority of the cases the user will never need it. In fact flyweight<> is already implemented in a similar way, it uses a flyweight_core<> class as F. Note that the above simpler flyweight<> allows me to pass the aforementioned template<class T> struct sp_factory { typedef boost::shared_ptr<T const> handle_type; static handle_type insert( T const & t ) { return handle_type( new T( t ) ); } static T const & entry( handle_type p ) { return *p; } }; as F; there's no need to invent a locking policy, a tracking policy, a holder, or a stateful factory.

----- Mensaje original ----- De: Peter Dimov <pdimov@pdimov.com> Fecha: Domingo, Diciembre 16, 2007 10:09 pm Asunto: Re: [boost] [flyweight] some minor comments Para: boost@lists.boost.org
More about
http://svn.boost.org/svn/boost/sandbox/flyweight/libs/ flyweight/doc/reference/flyweight.html#flyweight
I don't understand the source of the complexity at the flyweight<> level. A "naive" flyweight<> could look like:
template<class T, class F = default_factory> class flyweight { typedef typename F::handle_type handle_type; handle_type handle_;
public:
flyweight( ... args ): handle_( F::insert( T( args... ) ) ) {} T const& get() const { return F::value( handle_ ); } };
It seems to me that this provides the same amount of expressive power as the current interface. Locking/tracking/holding can be made parameters of the Factory if so desired, and a factory_core<F',L,T,H> template may be provided as a convenience in a separate header, but in the majority of the cases the user will never need it.
Well, the very fine-grained policy decomposition of flyweight<> relies on the assumption that users will need to control the aspects exposed. With the predefined components already provided by the lib, there are 24 different configurations, each one meaningful: an user might want to use a set-based factory for types with problematic hashing, another is happy with hashed-based lookup but does not want locking (she works in a single-threaded environment), etc. If the user doesn't need to change any of the defaults, then she can include boost/flyweight.hpp and use flyweight without even needing to know about factories, tracking and such --this is in fact what the tutorial recommends.
In fact flyweight<> is already implemented in a similar way, it uses a flyweight_core<> class as F.
Note that the above simpler flyweight<> allows me to pass the aforementioned sp_factory[...] as F; there's no need to invent a locking policy, a tracking policy, a holder, or a stateful factory.
If I understand your point correctly, you suggest turning flyweight into a single-policy class: template<typename T,typename F=flyweight_core<...> > class flyweight; and moving the fine-grained policies into flyweight_core, right? Well, this is certainly feasible and nicely accommodates global core replacements as the one you provide, but to me this is not a simplification of the design, it's merely allowing for an additional customization point. It can be included if deemed interesting, of course. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

----- Mensaje original ----- De: Peter Dimov <pdimov@pdimov.com> Fecha: Domingo, Diciembre 16, 2007 8:27 pm Asunto: Re: [boost] [flyweight] some minor comments Para: boost@lists.boost.org
Thorsten Ottosen:
Well, it's lower overhead (if no factory and forwarding constructors/operations makes it a better choice than shared_ptrconst T>.
It might be possible to do this using a custom factory along the lines of:
template<class T> struct sp_factory { typedef boost::shared_ptr<T const> handle_type;
handle_type insert( T const & t ) { return handle_type( new T( t ) ); } void erase( handle_type ) {} T const & entry( handle_type p ) { return *p; } };
This approach would work (except for a technical issue I comment later) but it's overkill because it provides its own tracking, which is already taken care of by the Tracking policy. One would use then in conjunction with the no_tracking and no_locking policies, sort of like this: template<class T> using no_factory_flyweight= flyweight<T,sp_factory<_1,_2>,no_tracking,no_locking>; An alternative would be to provide an even simpler factory: template<typename Entry,typename Value> struct trivial_factory { typedef const Entry* handle_type; handle_type insert(const Entry& x){return new Entry(x);} void erase(handle_type p){delete p;} const Entry& entry(handle_type p){delete p;} }; // default tracking is ref_counted template<class T> using no_factory_flyweight= flyweight<T,trivial_factory<_1,_2>,no_locking>; Either of these two alternatives *almost* works, but for a detail: equality of flyweight objects is defined in terms of equality of reference, i.e. x==y iff &x.get()==&y.get(). sp_factory (and trivial_factory) interpret every value as distinct for any other: no_factory_flyweight<std:string> x("hello"), y("hello"); assert(x!=y); //assertion passes which results in no value sharing, except when a flyweight is copied from another. Nothing wrong with this as long as it is known and accepted, of course.
but there's no way to tell from the documentation; it never describes how the Factory template parameter is actually used. [...] As I read it, a flyweight<> can ignore all of its template parameters beyond T and still conform to its spec, which is probably not what's intended.
I tried to be more descriptive than prescriptive so as to not overspecifiy the internals of flyweight<>, but you're right the formal connection between flyweight<> and its template arguments has to be given. I'll try to improve this part of the reference. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

JOAQUIN LOPEZ MU?Z <joaquin <at> tid.es> writes:
----- Mensaje original ----- De: Peter Dimov <pdimov <at> pdimov.com> Fecha: Domingo, Diciembre 16, 2007 8:27 pm Asunto: Re: [boost] [flyweight] some minor comments Para: boost <at> lists.boost.org
[...]
As I read it, a flyweight<> can ignore all of its template parameters beyond T and still conform to its spec, which is probably not what's intended.
I tried to be more descriptive than prescriptive so as to not overspecifiy the internals of flyweight<>, but you're right the formal connection between flyweight<> and its template arguments has to be given. I'll try to improve this part of the reference.
I've tried to improve on that point, please see http://tinyurl.com/345s5w and http://tinyurl.com/2s3plu . Does it look better now to you? Joaquín M López Muñoz Telefónica, Investigación y Desarrollo

Peter,
Thorsten Ottosen:
Joaquin M Lopez Munoz skrev:
Thorsten Ottosen <thorsten.ottosen <at> dezide.com> writes:
I would use it as a lightweight and more convenient replacement for boost::shared_ptr<const T>.
In my application I might have < 50 objects that needs to by flyweights. They are almost never identical, and if they are, the memory occupied by the factory would probably be much larger than that of duplication. Well, then you might not need using the flyweight idiom after all... Well, it's lower overhead (if no factory and forwarding constructors/operations makes it a better choice than shared_ptrconst T>.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2351.htm describes make_shared(). Any reason you haven't added a version to trunk? -Thorsten

Sorry for this off-topic message, but I thought discussing overloading of operator. could be interesting. Thorsten Ottosen wrote:
PS: what a shame we can't overload operator.() in C++
I think most of the proposals that have been made are quite wrong in their approach. They usually simply forward operator. to a reference. While this is enough for things that only require forwarding, (like flyweight does) it is not a generic solution. It cannot be used with variant, for example. First, what is the type of the right operand of operator. ? A name, a compile-time string, plus some eventual arguments if it's a member function call. mpl::vector_c<char, ....> is quite what we need. Then, we need the ability to call a member from its name, and that requires some kind of compile-time reflection. Here again, mpl helps to build the structure of the reflection information. template <typename T> struct reflection { }; template<> struct reflection<MyClass> { typedef mpl::map< mpl::pair< mpl::vector_c<char, 'm', 'e', 'm', 'b', 'e', 'r', '1'>, a_function_object_type /* the type could contain additional information about the available signatures */ >, .... > members; typedef .... typedefs; typedef .... static_members; }; (all public members could be listed, including inherited ones) This solution would be best, but since it is meta-programming intensive, requires usage of type containers, and is not easy to code with, I doubt the standard will want to adopt it.

Hi, When ignoring the metaprogramming part, an operator. is also interesting for generic proxies and mocks. It does give a few bumps along the road in terms of "what if operator. invokes operator." or "what if the returned function + object again have an operator.", but I think that's possible. The main problems are 1. people assume you want an operator. that does what operator-> does but with a reference 2. people stubbornly insist this isn't practical or needed so I doubt you'll be able to get anywhere. I hope so though, as I would like a generic mock / proxy class. Regards and good luck, Peter On 17/12/2007, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
Sorry for this off-topic message, but I thought discussing overloading of operator. could be interesting.
Thorsten Ottosen wrote:
PS: what a shame we can't overload operator.() in C++
I think most of the proposals that have been made are quite wrong in their approach. They usually simply forward operator. to a reference.
While this is enough for things that only require forwarding, (like flyweight does) it is not a generic solution. It cannot be used with variant, for example.
First, what is the type of the right operand of operator. ? A name, a compile-time string, plus some eventual arguments if it's a member function call. mpl::vector_c<char, ....> is quite what we need.
Then, we need the ability to call a member from its name, and that requires some kind of compile-time reflection. Here again, mpl helps to build the structure of the reflection information.
template <typename T> struct reflection { };
template<> struct reflection<MyClass> { typedef mpl::map< mpl::pair< mpl::vector_c<char, 'm', 'e', 'm', 'b', 'e', 'r', '1'>, a_function_object_type /* the type could contain additional information about the available signatures */ >, .... > members;
typedef .... typedefs; typedef .... static_members; };
(all public members could be listed, including inherited ones)
This solution would be best, but since it is meta-programming intensive, requires usage of type containers, and is not easy to code with, I doubt the standard will want to adopt it.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (6)
-
"JOAQUIN LOPEZ MU?Z"
-
Joaquin M Lopez Munoz
-
Mathias Gaunard
-
Peter Bindels
-
Peter Dimov
-
Thorsten Ottosen