Proton  1.1.1
Make porting easy from Python to C++11
ref.hpp
Go to the documentation of this file.
1 #ifndef PROTON_REF_HEADER
2 #define PROTON_REF_HEADER
3 
4 /** @file ref.hpp
5  * @brief the core header for reference support.
6  */
7 
8 #include <memory>
9 #include <utility>
10 #include <tuple>
11 #include <type_traits>
12 #include <proton/pool.hpp>
13 #include <initializer_list>
14 
15 #ifndef PROTON_REF_DEBUG
16 #define PROTON_REF_LOG(lvl, out)
17 #else
18 #define PROTON_REF_LOG PROTON_LOG
19 #endif
20 
21 namespace proton{
22 
23 namespace detail{
24 
25 class refc_t {
26 private:
27  long __r;
28 public:
29  refc_t():__r(0)
30  {}
31 
32  refc_t(const refc_t& r):__r(0)
33  {}
34 
35  refc_t& operator=(const refc_t& r)
36  {
37  return *this;
38  }
39 
40  bool operator<(long i)const
41  {
42  return __r < i;
43  }
44 
45  bool operator==(long i)const
46  {
47  return __r == i;
48  }
49 
50  void enter()
51  {
52  ++__r;
53  }
54 
55  long release()
56  {
57  return --__r;
58  }
59 
60  long count() const
61  {
62  return __r;
63  }
64 };
65 
66 } // ns detail
67 
68 /** @addtogroup ref
69  * @{
70  * @addtogroup ref_
71  * @{
72  */
73 
74 class init_alloc{};
75 extern init_alloc alloc; ///< explicitly demand to initialize an object.
76 
77 class init_alloc_inner{};
78 extern init_alloc_inner alloc_inner; //< for inner use of ref_.
79 
80 /** test a ref null or not.
81  * @param x the ref to be checked
82  * @return true: x doesn't refer to any object, false: x refers to an object.
83  */
84 template<typename refT> bool is_null(const refT& x)
85 {
86  return &x.__o()==NULL;
87 }
88 
89 /** test a ref valid or not.
90  * @param x the ref to be checked
91  * @return true: x refers to an object, false: x doesn't refer to any object.
92  */
93 template<typename refT> bool is_valid(const refT& x)
94 {
95  return &x.__o()!=NULL;
96 }
97 
98 /** declare copy_to().
99  * For object classes which need to support copy().
100  */
101 #define PROTON_COPY_DECL(type)\
102  virtual void copy_to(void* p)const\
103  {\
104  new (p) type(*this);\
105  }
106 
107 /** declare copy_to() without virtual.
108  * For object classes which need to support copy(), without inheritance.
109  */
110 #define PROTON_COPY_DECL_NV(type)\
111  void copy_to(void* p)const\
112  {\
113  new (p) type(*this);\
114  }
115 
116 /** Generate a copy of object.
117  * Note: the alloc_t of refT must support duplicate() like smart_allocator.
118  * @param x a ref to an obj supporting the method: void copy_to(void* new_addr)const.
119  * You can use PROTON_COPY_DECL() or PROTON_COPY_DECL_NV() to declare copy_to()
120  * in the obj class.
121  * @return a cloned obj of x
122  */
123 template<typename refT> refT copy(const refT& x)
124 {
125  if(is_null(x))
126  return refT();
127  typedef typename refT::alloc_t alloc_t;
128  detail::refc_t* p=(detail::refc_t*)alloc_t::duplicate(x._rp);
129  new (p) detail::refc_t();
130  typename refT::obj_t* q=(typename refT::obj_t *)(p+1);
131  x->copy_to((void*)q);
132  return refT(alloc_inner,p,q);
133 }
134 
135 /** reset a ref to release its object if any.
136  * @param x the ref to be resetted.
137  */
138 template<typename refT> void reset(refT& x)
139 {
140  x.release();
141 }
142 
143 /** get the reference count of the object.
144  * @param x refers to the object
145  * @return the reference count.
146  */
147 template<typename refT> long ref_count(const refT& x)
148 {
149  if(x._rp)
150  return x._rp->count();
151  else
152  return 0;
153 }
154 
155 /** cast from a ref type to another.
156  * if casting fails, throw std::bad_cast().
157  * @param x the original ref
158  * @return the casted one
159  */
160 template<typename T, typename refT> T cast(const refT& x)
161 {
162  static_assert(std::is_class<typename T::proton_ref_self_t>(), "The target type is not a ref_ type");
163  if(is_null(x))
164  return T();
165  typedef typename T::obj_t target_t;
166  target_t* p=dynamic_cast<target_t*>(x._p);
167  if(p)
168  return T(alloc_inner, x._rp, p);
169  throw std::bad_cast();
170 }
171 
172 /** The core reference support template.
173  * @param allocator It must support confiscate(), and allocator::allocate() must be static.
174  * @see smart_allocator in <proton/pool.hpp>
175  */
176 template<typename objT, typename allocator=smart_allocator<objT> > struct ref_ {
177 friend void reset<ref_>(ref_& x);
178 friend ref_ copy<ref_>(const ref_& x);
179 friend long ref_count<ref_>(const ref_& x);
180 template<typename T, typename ref_>friend T cast(const ref_& x);
181 
182 public:
183  typedef ref_ proton_ref_self_t;
184  typedef std::ostream proton_ostream_t;
185  typedef objT obj_t;
186  typedef allocator alloc_t;
187 
188 protected:
189  detail::refc_t * _rp;
190  objT* _p;
191 
192 protected:
193  void enter(detail::refc_t* rp)
194  {
195  _rp=rp;
196  if(_rp)
197  _rp->enter();
198  }
199 
200  void release()
201  {
202  if(_rp){
203  long r=_rp->release();
204  if(!r){
205  _p->~objT();
206  alloc_t::confiscate(_rp);
207  }
208  _rp=NULL;
209  _p=NULL;
210  }
211  }
212 
213 public:
214  /** default ctor.
215  * Doesn't refer to any object.
216  */
217  ref_():_rp(NULL), _p(NULL)
218  {
219  PROTON_REF_LOG(9,"default ctor");
220  }
221 
222  // inner use
223  ref_(init_alloc_inner, detail::refc_t* rp, objT* p):_rp(rp), _p(p)
224  {
225  PROTON_REF_LOG(9,"alloc_inner ctor");
226  if(_rp)
227  _rp->enter();
228  }
229 
230  /** explicit forwarding ctor.
231  * Construct an obj_t using give args.
232  */
233  template<typename ...argT> explicit ref_(init_alloc, argT&& ...a)
234  {
235  PROTON_REF_LOG(9,"alloc fwd ctor");
236  struct ref_obj_t{
237  detail::refc_t r;
238  obj_t o;
239  };
240  typedef typename alloc_t::template rebind<ref_obj_t>::other real_alloc;
241  ref_obj_t* p=real_alloc::allocate(1);
242  if(p){
243  new (&(p->r)) detail::refc_t();
244  new (&p->o) obj_t(a...);
245  _p=&(p->o);
246  enter(&(p->r));
247  }
248  }
249 
250  /** implicit forwarding ctor.
251  * Construct an obj_t using give args.
252  * Note: don't conflict with copy ctors. Use the explicit fwd ctor in that case.
253  */
254  template<typename ...argT> explicit ref_(argT&& ...a):ref_(alloc, a...)
255  {}
256 
257  /** explicit initializer_list forwarding ctor.
258  * Construct an obj_t using give args.
259  */
260  template<typename T> explicit ref_(init_alloc, std::initializer_list<T> a)
261  {
262  PROTON_REF_LOG(9,"alloc initializer_list fwd ctor");
263  struct ref_obj_t{
264  detail::refc_t r;
265  obj_t o;
266  };
267  typedef typename alloc_t::template rebind<ref_obj_t>::other real_alloc;
268  ref_obj_t* p=real_alloc::allocate(1);
269  if(p){
270  new (&(p->r)) detail::refc_t();
271  new (&p->o) obj_t(a);
272  _p=&(p->o);
273  enter(&(p->r));
274  }
275  }
276 
277  /** implicit initializer_list forwarding ctor.
278  * Construct an obj_t using give args.
279  */
280  template<typename T> explicit ref_(std::initializer_list<T> a):ref_(alloc,a)
281  {}
282 
283  /** copy ctor.
284  */
285  ref_(const ref_& r):_p(r._p)
286  {
287  PROTON_REF_LOG(9,"const copy ctor");
288  enter(r._rp);
289  }
290 
291  ref_(ref_& r):_p(r._p)
292  {
293  PROTON_REF_LOG(9,"copy ctor");
294  enter(r._rp);
295  }
296 
297  ref_(const ref_&& r):_p(r._p)
298  {
299  PROTON_REF_LOG(9,"copy rvalue ctor");
300  enter(r._rp);
301  }
302 
303  /** move ctor.
304  */
305  ref_(ref_&& r)noexcept:_rp(r._rp),_p(r._p)
306  {
307  PROTON_REF_LOG(9,"move ctor");
308  r._rp=NULL;
309  r._p=NULL;
310  }
311 
312  /** assign operator.
313  */
314  ref_& operator=(const ref_& r)
315  {
316  PROTON_REF_LOG(9,"assign lvalue");
317  if(r._rp!=_rp){
318  detail::refc_t* rp_old=_rp;
319  objT* p_old=_p;
320 
321  enter(r._rp);
322  _p=r._p;
323 
324  if(rp_old){
325  long r=rp_old->release();
326  if(!r){
327  p_old->~objT(); // may throw
328  alloc_t::confiscate(rp_old);
329  }
330  }
331  }
332  return *this;
333  }
334 
335  /** assign move operator.
336  */
337  ref_& operator=(ref_&& r)noexcept(noexcept(_p->~objT()))
338  {
339  PROTON_REF_LOG(9,"assign rvalue");
340  if(r._rp!=_rp){
341  detail::refc_t* rp_old=_rp;
342  objT* p_old=_p;
343 
344  _rp=r._rp;
345  _p=r._p;
346  r._rp=NULL;
347  r._p=NULL;
348 
349  if(rp_old){
350  long r=rp_old->release();
351  if(!r){
352  p_old->~objT(); // may throw
353  alloc_t::confiscate(rp_old);
354  }
355  }
356  }
357  return *this;
358  }
359 
360  /** dtor.
361  */
362  ~ref_()noexcept(noexcept(_p->~objT()))
363  {
364  release();
365  }
366 
367 public:
368 
369 #if 0
370  /* conversion to const baseT&.
371  * Notice! NEVER convert to a non-const ref from here!
372  * Due to the optimizing ability of c++11, in most situations this method is not needed.
373  * Use the next method for casting.
374  */
375  template<typename baseT> operator const baseT& () const
376  {
377  PROTON_REF_LOG(9,"const baseT&()");
378  static_assert(std::is_class<typename baseT::proton_ref_self_t>(), "The target type is not a ref_ type");
379  static_assert(std::is_base_of<typename baseT::obj_t, obj_t>(), "The target type is not a base type of obj_t");
380  static_assert(static_cast<typename baseT::obj_t*>((obj_t*)4096)==(typename baseT::obj_t*)4096, "can not convert to a non-first-base ref_");
381  return reinterpret_cast<const baseT&>(*this);
382  }
383 #endif
384 
385 #if 0
386  template<typename derivedT,
387  typename=typename std::enable_if<
388  std::is_base_of<obj_t, derivedT>::value
389  >::type
390  >
391  ref_(const ref_<derivedT>& x)noexcept:_rp(x._rp), _p(static_cast<obj_t*>(x._p))
392  {
393  if(_rp)
394  _rp->enter();
395  }
396 #endif
397 
398 #if 0
399  template<typename baseT,
400  typename=typename std::enable_if<
401  std::is_base_of<baseT, obj_t>::value
402  && static_cast<baseT*>((obj_t*)4096)==(baseT*)4096
403  >::type
404  >
405  operator const ref_<baseT>& ()const noexcept
406  {
407  PROTON_REF_LOG(9,"const baseT&()");
408  return reinterpret_cast<const ref_<baseT>&>(*this);
409  }
410 #endif
411 
412 #if 1
413  /** conversion to baseT.
414  */
415  template<typename baseT,
416  typename=typename std::enable_if<
417  std::is_base_of<baseT, obj_t>::value
418 // && static_cast<baseT*>((obj_t*)4096)!=(baseT*)4096
419  >::type
420  >
421  operator ref_<baseT> ()const noexcept
422  {
423  PROTON_REF_LOG(9,"baseT()");
424  return ref_<baseT>(alloc_inner, _rp, static_cast<baseT*>(_p));
425  }
426 #endif
427 
428 public:
429  const objT& __o()const
430  {
431  return *_p;
432  }
433 
434  objT& __o()
435  {
436  return *_p;
437  }
438 
439 
440  objT& operator *()
441  {
442  return __o();
443  }
444 
445  const objT& operator *()const
446  {
447  return __o();
448  }
449 
450  /** operator-> points to the object refered.
451  */
452  objT* operator->()
453  {
454  return &__o();
455  }
456 
457  /** operator-> points to the object refered.
458  */
459  const objT* operator->()const
460  {
461  return &__o();
462  }
463 
464  /** general operator== for refs.
465  * Need to implement obj_t == T::obj_t.
466  */
467  template<typename T> bool operator==(const T& x)const
468  {
469  static_assert(std::is_class<typename T::proton_ref_self_t>(),
470  "The target type is not a ref_ type");
471  if((void*)&(__o())==(void*)&(x.__o()))
472  return true;
473  if(is_null(*this)||is_null(x))
474  return false;
475  return __o() == x.__o();
476  }
477 
478  template<typename T> bool operator!=(const T& x)const
479  {
480  return !(*this==x);
481  }
482 
483  /** general operator< for refs.
484  * Need to implement obj_t < T::obj_t.
485  */
486  template<typename T> bool operator<(const T& x)const
487  {
488  static_assert(std::is_class<typename T::proton_ref_self_t>(),
489  "The target type is not a ref_ type");
490  if((void*)&(x.__o())==(void*)&(__o()))
491  return false;
492  if(is_null(*this))
493  return true;
494  if(is_null(x))
495  return false;
496  return __o() < x.__o();
497  }
498 
499  template<typename T> bool operator>=(const T& x)const
500  {
501  return !(*this < x);
502  }
503 
504  template<typename T> bool operator>(const T& x)const
505  {
506  return (x < *this);
507  }
508 
509  template<typename T> bool operator<=(const T& x)const
510  {
511  return !(x < *this);
512  }
513 
514  /** general operator() const for refs.
515  * Need to implement obj_t() const.
516  */
517  template<typename ...T> auto operator()(T&& ...x)const -> decltype((*_p)(x...))
518  {
519  PROTON_THROW_IF(is_null(*this), "nullptr for ()");
520  return __o()(x...);
521  }
522 
523  /** general operator() for refs.
524  * Need to implement obj_t().
525  */
526  template<typename ...T> auto operator()(T&& ...x) -> decltype((*_p)(x...))
527  {
528  PROTON_THROW_IF(is_null(*this), "nullptr for ()");
529  return __o()(x...);
530  }
531 
532  /** general operator[] const for refs.
533  * Need to implement obj_t[] const.
534  */
535  template<typename T> auto operator[](T&& x)const -> decltype((*_p)[x])
536  {
537  PROTON_THROW_IF(is_null(*this), "nullptr for []");
538  return __o()[x];
539  }
540 
541  /** general operator[] for refs.
542  * Need to implement obj_t[].
543  */
544  template<typename T> auto operator[](T&& x) -> decltype((*_p)[x])
545  {
546  PROTON_THROW_IF(is_null(*this), "nullptr for []");
547  return __o()[x];
548  }
549 };
550 
551 /** general output for refs.
552  * Need T::obj_t to implenment the method: void output(std::ostream& s)const.
553  * Don't forget virtual when needed.
554  */
555 template<typename T>std::ostream& operator<<(typename T::proton_ostream_t& s,
556  const T& y)
557 {
558  if(is_null(y)){
559  s << "<>" ;
560  return s;
561  }
562  y->output(s);
563  return s;
564 }
565 
566 /** general operator< & operator== for objects.
567  * Need obj_t to implenment: T1 key()const, and T1 should be comparable.
568  * Don't forget virtual when needed.
569  * [TODO] need an example.
570  */
571 #define PROTON_KEY_DECL(type)\
572  bool operator<(const type& y)const\
573  {\
574  return key()<y.key();\
575  }\
576  \
577  bool operator==(const type& y)const\
578  {\
579  return key()==y.key();\
580  }\
581 
582 /** general key_hash for refs.
583  * Need T::obj_t to implenment T1 key()const, and T1 must support std::hash.
584  * Don't forget virtual when needed.
585  * [TODO] need an example.
586  */
587 template<typename T>struct key_hash{
588 public:
589  size_t operator()(const T& x)const
590  {
591  if(is_null(x))
592  return 0;
593  else{
594  typedef decltype(x->key()) ref_t;
595  typedef typename std::remove_reference<ref_t>::type const_key_t;
596  typedef typename std::remove_cv<const_key_t>::type key_t;
597  return std::hash<key_t>()(x->key());
598  }
599  }
600 };
601 
602 /** general subkey_hash for refs.
603  * Need T::obj_t to implenment T1 key()const, and T1 is a tuple,
604  * and the key_seq item of T1 must support std::hash.
605  * Don't forget virtual when needed.
606  * [TODO] need an example.
607  */
608 template<typename T, int key_seq=0>struct subkey_hash{
609 public:
610  size_t operator()(const T& x)const
611  {
612  typedef decltype(std::get<key_seq>(x->key())) ref_t;
613  typedef typename std::remove_reference<ref_t>::type const_key_t;
614  typedef typename std::remove_cv<const_key_t>::type key_t;
615  if(is_null(x))
616  return 0;
617  else
618  return std::hash<key_t>()(std::get<key_seq>(x->key()));
619  }
620 };
621 
622 /**
623  * @}
624  * @}
625  */
626 };
627 
628 #endif // PROTON_REF_HEADER