Boost logo

Boost Users :

Subject: Re: [Boost-users] Flyweight: wrapping shared_ptr
From: Joaquin M Lopez Munoz (joaquin_at_[hidden])
Date: 2014-10-08 09:33:23


Akim Demaille <akim <at> lrde.epita.fr> writes:

>
>
> Le 8 oct. 2014 à 14:32, Joaquin M Lopez Munoz <joaquin <at> tid.es> a
> écrit :
>
> > Ah, you should add a move ctor and assignment operator for better
> > performance. Revised example:
> >
> > http://coliru.stacked-crooked.com/a/2ab8d00da3f71c84
>
> Great :) Finally, the free-lunch is not over, I should just let
> time pass and have my implementation improve all by itself :)

Anyway, please profile against shared_ptr, I'm not claiming this is
necessarily faster.

> > Do you mean having
> >
> > using ExpBin=poly_flyweight<Bin>;
> > using ExpNum=poly_flyweight<Num>;
> >
> > rather than a single using ExpBin=poly_flyweight<Bin>? I think
> > this is very hard to manage: to begin with, Bin has two Exp members,
> > with this type splitting it is not even clear how you would
> > manage the different cases where the operands to Bin are compound
> > expressions or Num's (and the combinations thereof).
>
> I expect the flyweight implementation to support inheritance, just
> as
>
> std::shared_ptr<base> p = std::make_shared<derived>();

This is alas not the case for flyweight<base>/flyweight<derived>. You
can simulate something like that behavior in poly_flyweight:

template<typename Derived>
class poly_flyweight
{
  ...
  operator flyweight<Base>()const;
  ...
};

but invoking this operator for a poly_flyweight<Derived> object x
would imply creating a clone of x.get() (upcasted to const Base*) in
poly_flyweight<Base> internal factory, which is a waste of space and
brings you to the original situation where all objects are stored in
the same factory. A less ambitious approach is

template<typename Derived,typename Base>
class derived_poly_flyweight:poly_flyweight<Base>
{
  ...
  const Derived& operator()const;
  // similar for operator* and operator->
  ...
};

which gives you direct conversion to const Derived&, but still
stores everything in the same factory.

> > So, there is a choice between having -> behave with "pointer semantics"
> > (std::optional) or "pass-thru" semantics, for want of a better name.
>
> Ouhh, I clearly prefer the pass-thru semantics here. The Flyweight
> is similar (to my eyes) to a proxy to an object that does all it can to
> have the rest of the code believe it is the real object. I'm very happy
> that I can enable/disable flyweight'ing via a simple typedef, and using
> a pointer-like semantics would break everything: the real object
> and the flyweight'ed one would have different interfaces.

I understand your rationale, but pass-thru semantics is only applicable
to pointer-like elements.

>
> > If we choose pass-thru semantics, then we're ruling out the
> > possibility of having operator-> in instantiations such as, say,
> > flyweight<int>, where the flyweight'd element is not a (smart) pointer.
>
> I'm not sure I understand what you mean here. I can't imagine
> what 'flyweight<int>(42)->...' would mean. I'd sfinae it out of
> the picture.

int is a poorly chosen example. Take instead

struct foo
{
  int x;
};

With pointer-like semantics, you have

flyweight<foo> f;
flyweight<foo*> g;

f->x=0; // OK
g->x=0; // error

with pass-thru semantics, it is the other way around:

f->x=0; // error
g->x=0; // OK

The case with poly_flyweight is different. As poly_flyweight<Base>
is expected to act as a replacement for const Base*, pass-thru
semantics makes a lot of sense.

Joaquín M López Muñoz
Telefónica


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net