Boost logo

Boost Users :

Subject: Re: [Boost-users] Flyweight: wrapping shared_ptr
From: Joaquin M Lopez Munoz (joaquin_at_[hidden])
Date: 2014-10-07 14:41:15


> De: Boost-users [mailto:boost-users-bounces_at_[hidden]]
> En nombre de Akim Demaille
> Enviado el: martes, 07 de octubre de 2014 18:27
> Para: Boost Users List
> Asunto: Re: [Boost-users] Flyweight: wrapping shared_ptr
>
> Le 7 oct. 2014 à 18:18, Joaquín Mª López Muñoz <joaquin_at_[hidden]> a écrit :
>
> > An alternative would be to use some sort of cloning smart pointer to
> > spare the reference count, but it's not clear to me this would be
> > faster than
> > shared_ptr:
> >
> > * element exists: shared_ptr creation is slower than clone_ptr
> > creation
> > * element does not exist: shared_ptr creation and copy is probably
> > faster than clone_ptr creation and cloning.
>
> Yes, indeed.

A slightly smarter approach involves a cloning class that accepts a const
Base& and does only clone on copy, thus avoiding dynamic memory allocation
when the element already exists.
This can be nicely wrapped up as follows:

template<typename Base>
class poly_holder
{
public:
  poly_holder(const Base& x):p(&x),dlt(false){}
  poly_holder(const poly_holder& x):p(x.p->clone()),dlt(true){}
  poly_holder& operator=(const poly_holder& x)
  {
    Base* q=x.p->clone();
    if(dlt)delete p;
    p=q;
    dlt=true;
    return *this;
  }
  ~poly_holder(){if(dlt)delete p;}
  
  operator const Base&()const {return *p;}

private:
  const Base* p;
  bool dlt;
};

template<typename Base>
class poly_flyweight:public boost::flyweight<poly_holder<Base>>
{
public:
  using super=boost::flyweight<poly_holder<Base>>;
  using super::super;
  
  const Base& operator*() const{return base();}
  const Base* operator->()const{return &base();}
  
private:
 const Base& base()const{return this->get();}
};

template<typename Base>
std::size_t hash_value(const poly_flyweight<Base>& x)
{
  return boost::hash<const Base*>()(&*x);
}

template<typename Base>
bool operator==(
  const poly_flyweight<Base>& l,const poly_flyweight<Base>& r)
{
  return
    static_cast<boost::flyweight<poly_holder<Base>>>(l)==
    static_cast<boost::flyweight<poly_holder<Base>>>(r);
}

template<typename Base>
bool operator!=(
  const poly_flyweight<Base>& l,const poly_flyweight<Base>& r)
{
  return !(l==r);
}

I've rewritten your test case to take advantage of this poly_flyweight
thing, cleaning up some unnecessary hash and operator== overloads along the
way and without resorting to the intermediate Exp_ type; see:

http://coliru.stacked-crooked.com/a/76e87531b65d44f9

> Is there anyway Flyweight could have supported the operator-> natively?
> It seems that with some SFINAE under the hood, it would work, don't you think?

Yep, that would be possible, the only reason I didn't do it is to not
clutter the interface.
Also, some std classes such as (C++17) std::optional implement operator*
differently, without the extra dereference required by smart pointer semantics.

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