Empirical
Ptr.h
Go to the documentation of this file.
1 
24 #ifndef EMP_PTR_H
25 #define EMP_PTR_H
26 
27 #include <unordered_map>
28 
29 #include "assert.h"
30 #include "vector.h"
31 
32 namespace emp {
33 
34  namespace internal {
36  static constexpr size_t Log2(size_t x) { return x <= 1 ? 0 : (Log2(x/2) + 1); }
37 
38  static bool ptr_debug = false;
39  }
40  void SetPtrDebug(bool _d = true) { internal::ptr_debug = _d; }
41  bool GetPtrDebug() { return internal::ptr_debug; }
42 
43  enum class PtrStatus { DELETED=0, ACTIVE, ARRAY };
44 
45  class PtrInfo {
46  private:
47  const void * ptr;
48  int count;
49  PtrStatus status;
50  size_t array_bytes;
51 
52  public:
53  PtrInfo(const void * _ptr) : ptr(_ptr), count(1), status(PtrStatus::ACTIVE), array_bytes(0) {
54  if (internal::ptr_debug) std::cout << "Created info for pointer: " << ptr << std::endl;
55  }
56  PtrInfo(const void * _ptr, size_t _array_bytes)
57  : ptr(_ptr), count(1), status(PtrStatus::ARRAY), array_bytes(_array_bytes)
58  {
59  emp_assert(_array_bytes >= 1);
60  if (internal::ptr_debug) {
61  std::cout << "Created info for array pointer (bytes=" << array_bytes << "): "
62  << ptr << std::endl;
63  }
64  }
65  PtrInfo(const PtrInfo &) = default;
66  PtrInfo(PtrInfo &&) = default;
67  PtrInfo & operator=(const PtrInfo &) = default;
68  PtrInfo & operator=(PtrInfo &&) = default;
69 
71  if (internal::ptr_debug) std::cout << "Deleted info for pointer " << ptr << std::endl;
72  }
73 
75  const void * GetPtr() const noexcept { return ptr; }
76 
78  int GetCount() const noexcept { return count; }
79 
81  size_t GetArrayBytes() const noexcept { return array_bytes; }
82 
84  bool IsActive() const noexcept { return (bool) status; }
85 
87  bool IsArray() const noexcept { return status == PtrStatus::ARRAY; }
88 
90  void SetArray(size_t bytes) noexcept { array_bytes = bytes; status = PtrStatus::ARRAY; }
91 
93  void Inc(size_t id) {
94  if (internal::ptr_debug) std::cout << "Inc info for pointer " << ptr << std::endl;
95  emp_assert(status != PtrStatus::DELETED, "Incrementing deleted pointer!", id);
96  count++;
97  }
98 
100  void Dec(size_t id) {
101  if (internal::ptr_debug) std::cout << "Dec info for pointer " << ptr << std::endl;
102 
103  // Make sure that we have more than one copy, -or- we've already deleted this pointer
104  emp_assert(count > 1 || status == PtrStatus::DELETED, "Removing last reference to owned Ptr!", id);
105  count--;
106  }
107 
109  void MarkDeleted() {
110  if (internal::ptr_debug) std::cout << "Marked deleted for pointer " << ptr << std::endl;
111  status = PtrStatus::DELETED;
112  }
113 
115  bool OK() const noexcept {
116  if (ptr == nullptr) return false; // Should not have info for a null pointer.
117  if (status == PtrStatus::ARRAY) {
118  if (array_bytes == 0) return false; // Arrays cannot be size 0.
119  if (count == 0) return false; // Active arrays must have pointers to them.
120  }
121  if (status == PtrStatus::ACTIVE) {
122  if (array_bytes > 0) return false; // non-arrays must be array size 0.
123  if (count == 0) return false; // Active pointers must have references to them.
124  }
125  return true;
126  }
127  };
128 
129 
131  class PtrTracker {
132  private:
133  std::unordered_map<const void *, size_t> ptr_id;
134  emp::vector<PtrInfo> id_info;
135  static constexpr size_t UNTRACKED_ID = (size_t) -1;
136 
137  // Make PtrTracker a singleton.
138  PtrTracker() : ptr_id(), id_info() {
139  std::cout << "EMP_TRACK_MEM: Pointer tracking is active!\n";
140  }
141  PtrTracker(const PtrTracker &) = delete;
142  PtrTracker(PtrTracker &&) = delete;
143  PtrTracker & operator=(const PtrTracker &) = delete;
144  PtrTracker & operator=(PtrTracker &&) = delete;
145 
146  public:
148  // Track stats about pointer record.
149  size_t total = 0;
150  size_t remain = 0;
151 
152  // Scan through live pointers and make sure all have been deleted.
153  for (const auto & info : id_info) {
154  total++;
155  if (info.GetCount()) remain++;
156 
157  emp_assert(info.IsActive() == false, info.GetPtr(), info.GetCount(), info.IsActive());
158  }
159 
160  std::cout << "EMP_TRACK_MEM: No memory leaks found!\n "
161  << total << " pointers found; "
162  << remain << " still exist with a non-null value (but have been properly deleted)"
163  << std::endl;
164  }
165 
167  static PtrTracker & Get() { static PtrTracker tracker; return tracker; }
168 
170  PtrInfo & GetInfo(const void * ptr) { return id_info[ptr_id[ptr]]; }
171  PtrInfo & GetInfo(size_t id) { return id_info[id]; }
172 
174  bool HasPtr(const void * ptr) const {
175  if (internal::ptr_debug) std::cout << "HasPtr: " << ptr << std::endl;
176  return ptr_id.find(ptr) != ptr_id.end();
177  }
178 
180  size_t GetCurID(const void * ptr) { emp_assert(HasPtr(ptr)); return ptr_id[ptr]; }
181 
183  size_t GetNumIDs() const { return id_info.size(); }
184 
186  size_t GetArrayBytes(size_t id) const { return id_info[id].GetArrayBytes(); }
187 
189  bool IsDeleted(size_t id) const {
190  if (id == UNTRACKED_ID) return false; // Not tracked!
191  if (internal::ptr_debug) std::cout << "IsDeleted: " << id << std::endl;
192  return !id_info[id].IsActive();
193  }
194 
196  bool IsActive(const void * ptr) {
197  if (internal::ptr_debug) std::cout << "IsActive: " << ptr << std::endl;
198  if (ptr_id.find(ptr) == ptr_id.end()) return false; // Not in database.
199  return GetInfo(ptr).IsActive();
200  }
201 
203  bool IsActiveID(size_t id) {
204  if (id == UNTRACKED_ID) return false;
205  if (id >= id_info.size()) return false;
206  return id_info[id].IsActive();
207  }
208 
210  bool IsArrayID(size_t id) {
211  if (internal::ptr_debug) std::cout << "IsArrayID: " << id << std::endl;
212  return id_info[id].IsArray();
213  }
214 
216  int GetIDCount(size_t id) const {
217  if (internal::ptr_debug) std::cout << "Count: " << id << std::endl;
218  return id_info[id].GetCount();
219  }
220 
222  size_t New(const void * ptr) {
223  emp_assert(ptr); // Cannot track a null pointer.
224  size_t id = id_info.size();
225 #ifdef EMP_ABORT_PTR_NEW
226  if (id == EMP_ABORT_PTR_NEW) {
227  std::cerr << "Aborting at creation of Ptr id " << id << std::endl;
228  abort();
229  }
230 #endif
231  if (internal::ptr_debug) std::cout << "New: " << id << " (" << ptr << ")" << std::endl;
232  // Make sure pointer is not already stored -OR- hase been deleted (since re-use is possible).
233  emp_assert(!HasPtr(ptr) || IsDeleted(GetCurID(ptr)), id);
234  id_info.emplace_back(ptr);
235  ptr_id[ptr] = id;
236  return id;
237  }
238 
240  size_t NewArray(const void * ptr, size_t array_bytes) {
241  size_t id = New(ptr); // Build the new pointer.
242  if (internal::ptr_debug) std::cout << " ...Array of size " << array_bytes << std::endl;
243  id_info[id].SetArray(array_bytes);
244  return id;
245  }
246 
248  void IncID(size_t id) {
249  if (id == UNTRACKED_ID) return; // Not tracked!
250  if (internal::ptr_debug) std::cout << "Inc: " << id << std::endl;
251  id_info[id].Inc(id);
252  }
253 
255  void DecID(size_t id) {
256  if (id == UNTRACKED_ID) return; // Not tracked!
257  auto & info = id_info[id];
258  if (internal::ptr_debug) std::cout << "Dec: " << id << "(" << info.GetPtr() << ")" << std::endl;
259  emp_assert(info.GetCount() > 0, "Decrementing Ptr, but already zero!",
260  id, info.GetPtr(), info.IsActive());
261  info.Dec(id);
262  }
263 
265  void MarkDeleted(size_t id) {
266 #ifdef EMP_ABORT_PTR_DELETE
267  if (id == EMP_ABORT_PTR_DELETE) {
268  std::cerr << "Aborting at deletion of Ptr id " << id << std::endl;
269  abort();
270  }
271 #endif
272  if (internal::ptr_debug) std::cout << "Delete: " << id << std::endl;
273  emp_assert(id_info[id].IsActive(), "Deleting same emp::Ptr a second time!", id);
274  id_info[id].MarkDeleted();
275  }
276  };
277 
278 
280 //
281 // --- Ptr implementation ---
282 //
284 
285 #ifdef EMP_TRACK_MEM
286 
287  namespace {
288  // @CAO: Build this for real!
289  template <typename FROM, typename TO>
290  bool PtrIsConvertable(FROM * ptr) { return true; }
291  // emp_assert( (std::is_same<TYPE,T2>() || dynamic_cast<TYPE*>(in_ptr)) );
292 
293  // Debug information provided for each pointer type.
294  struct PtrDebug {
295  size_t current;
296  size_t total;
297  PtrDebug() : current(0), total(0) { ; }
298  void AddPtr() { current++; total++; }
299  void RemovePtr() { current--; }
300  };
301  }
302 
303  template <typename TYPE>
304  class Ptr {
305  public:
306  TYPE * ptr;
307  size_t id;
308  using element_type = TYPE;
309 
310  static constexpr size_t UNTRACKED_ID = (size_t) -1;
311 
312  static PtrDebug & DebugInfo() { static PtrDebug info; return info; } // Debug info for each type
313  static PtrTracker & Tracker() { return PtrTracker::Get(); } // Single tracker for al Ptr types
314 
316  Ptr() : ptr(nullptr), id(UNTRACKED_ID) {
317  if (internal::ptr_debug) std::cout << "null construct: " << ptr << std::endl;
318  }
319 
321  Ptr(const Ptr<TYPE> & _in) : ptr(_in.ptr), id(_in.id) {
322  if (internal::ptr_debug) std::cout << "copy construct: " << ptr << std::endl;
323  Tracker().IncID(id);
324  }
325 
327  Ptr(Ptr<TYPE> && _in) : ptr(_in.ptr), id(_in.id) {
328  if (internal::ptr_debug) std::cout << "move construct: " << ptr << std::endl;
329  _in.ptr = nullptr;
330  _in.id = UNTRACKED_ID;
331  // No IncID or DecID in Tracker since we just move the id.
332  }
333 
335  template <typename T2>
336  Ptr(T2 * in_ptr, bool track=false) : ptr(in_ptr), id(UNTRACKED_ID)
337  {
338  if (internal::ptr_debug) std::cout << "raw construct: " << ptr << ". track=" << track << std::endl;
339  emp_assert( (PtrIsConvertable<T2, TYPE>(in_ptr)) );
340 
341  // If this pointer is already active, link to it.
342  if (Tracker().IsActive(ptr)) {
343  id = Tracker().GetCurID(ptr);
344  Tracker().IncID(id);
345  }
346  // If we are not already tracking this pointer, but should be, add it.
347  else if (track) {
348  id = Tracker().New(ptr);
349  DebugInfo().AddPtr();
350  }
351  }
352 
354  template <typename T2>
355  Ptr(T2 * _ptr, size_t array_size, bool track) : ptr(_ptr), id(UNTRACKED_ID)
356  {
357  const size_t array_bytes = array_size * sizeof(T2);
358  if (internal::ptr_debug) std::cout << "raw ARRAY construct: " << ptr
359  << ". size=" << array_size << "(" << array_bytes
360  << " bytes); track=" << track << std::endl;
361  emp_assert( (PtrIsConvertable<T2, TYPE>(_ptr)) );
362 
363  // If this pointer is already active, link to it.
364  if (Tracker().IsActive(ptr)) {
365  id = Tracker().GetCurID(ptr);
366  Tracker().IncID(id);
367  emp_assert(Tracker().GetArrayBytes(id) == array_bytes); // Make sure pointer is consistent.
368  }
369  // If we are not already tracking this pointer, but should be, add it.
370  else if (track) {
371  id = Tracker().NewArray(ptr, array_bytes);
372  DebugInfo().AddPtr();
373  }
374  }
375 
377  template <typename T2>
378  Ptr(Ptr<T2> _in) : ptr(_in.Raw()), id(_in.GetID()) {
379  if (internal::ptr_debug) std::cout << "inexact copy construct: " << ptr << std::endl;
380  emp_assert( (PtrIsConvertable<T2, TYPE>(_in.Raw())), id );
381  Tracker().IncID(id);
382  }
383 
385  Ptr(std::nullptr_t) : Ptr() {
386  if (internal::ptr_debug) std::cout << "null construct 2." << std::endl;
387  }
388 
390  ~Ptr() {
391  if (internal::ptr_debug) {
392  std::cout << "destructing Ptr instance ";
393  if (ptr) std::cout << id << " (" << ptr << ")\n";
394  else std::cout << "(nullptr)\n";
395  }
396  Tracker().DecID(id);
397  }
398 
400  bool IsNull() const { return ptr == nullptr; }
401 
403  TYPE * Raw() {
404  emp_assert(Tracker().IsDeleted(id) == false, "Do not convert deleted Ptr to raw.", id);
405  return ptr;
406  }
407 
409  const TYPE * const Raw() const {
410  emp_assert(Tracker().IsDeleted(id) == false, "Do not convert deleted Ptr to raw.", id);
411  return ptr;
412  }
413 
415  template <typename T2> Ptr<T2> Cast() {
416  emp_assert(Tracker().IsDeleted(id) == false, "Do not cast deleted pointers.", id);
417  return (T2*) ptr;
418  }
419 
421  template <typename T2> const Ptr<const T2> Cast() const {
422  emp_assert(Tracker().IsDeleted(id) == false, "Do not cast deleted pointers.", id);
423  return (T2*) ptr;
424  }
425 
427  template <typename T2> Ptr<T2> DynamicCast() {
428  emp_assert(dynamic_cast<T2*>(ptr) != nullptr);
429  emp_assert(Tracker().IsDeleted(id) == false, "Do not cast deleted pointers.", id);
430  return (T2*) ptr;
431  }
432 
434  size_t GetID() const { return id; }
435 
437  template <typename... T>
438  void New(T &&... args) {
439  Tracker().DecID(id); // Remove a pointer to any old memory...
440 
441  ptr = new TYPE(std::forward<T>(args)...); // Special new that uses allocated space.
442  // ptr = (TYPE*) malloc (sizeof(TYPE)); // Build a new raw pointer.
443  // emp_emscripten_assert(ptr); // No exceptions in emscripten; assert alloc!
444  // ptr = new (ptr) TYPE(std::forward<T>(args)...); // Special new that uses allocated space.
445 
446  if (internal::ptr_debug) std::cout << "Ptr::New() : " << ptr << std::endl;
447  id = Tracker().New(ptr); // And track it!
448  DebugInfo().AddPtr();
449  }
450 
452  template <typename... T>
453  void NewArray(size_t array_size, T &&... args) {
454  Tracker().DecID(id); // Remove a pointer to any old memory...
455 
456  // @CAO: This next portion of code is allocating an array of the appropriate type.
457  // We are currently using "new", but should shift over to malloc since new throws an
458  // exception when there's a problem, which will trigger an abort in Emscripten mode.
459  // We'd rather be able to identify a more specific problem.
460  ptr = new TYPE[array_size]; // Build a new raw pointer to an array.
461  // ptr = (TYPE*) malloc (array_size * sizeof(TYPE)); // Build a new raw pointer.
462  // emp_emscripten_assert(ptr, array_size); // No exceptions in emscripten; assert alloc!
463  // for (size_t i = 0; i < array_size; i++) {
464  // new (ptr + i*sizeof(TYPE)) TYPE(args...);
465  // }
466 
467  if (internal::ptr_debug) std::cout << "Ptr::NewArray() : " << ptr << std::endl;
468  id = Tracker().NewArray(ptr, array_size * sizeof(TYPE)); // And track it!
469  DebugInfo().AddPtr();
470  }
471 
473  void Delete() {
474  emp_assert(id < Tracker().GetNumIDs(), id, "Deleting Ptr that we are not resposible for.");
475  emp_assert(ptr, "Deleting null Ptr.");
476  emp_assert(Tracker().IsArrayID(id) == false, id, "Trying to delete array pointer as non-array.");
477  if (internal::ptr_debug) std::cout << "Ptr::Delete() : " << ptr << std::endl;
478  delete ptr;
479  Tracker().MarkDeleted(id);
480  DebugInfo().RemovePtr();
481  }
482 
484  void DeleteArray() {
485  emp_assert(id < Tracker().GetNumIDs(), id, "Deleting Ptr that we are not resposible for.");
486  emp_assert(ptr, "Deleting null Ptr.");
487  emp_assert(Tracker().IsArrayID(id), id, "Trying to delete non-array pointer as array.");
488  if (internal::ptr_debug) std::cout << "Ptr::DeleteArray() : " << ptr << std::endl;
489  delete [] ptr;
490  Tracker().MarkDeleted(id);
491  DebugInfo().RemovePtr();
492  }
493 
495  size_t Hash() const {
496  // Chop off useless bits of pointer...
497  static constexpr size_t shift = internal::Log2(1 + sizeof(TYPE));
498  return (size_t)(ptr) >> shift;
499  }
500  struct hash_t { size_t operator()(const Ptr<TYPE> & t) const { return t.Hash(); } };
501 
503  Ptr<TYPE> & operator=(const Ptr<TYPE> & _in) {
504  if (internal::ptr_debug) std::cout << "copy assignment" << std::endl;
505  emp_assert(Tracker().IsDeleted(_in.id) == false, _in.id, "Do not copy deleted pointers.");
506  if (id != _in.id) { // Assignments only need to happen if ptrs are different.
507  Tracker().DecID(id);
508  ptr = _in.ptr;
509  id = _in.id;
510  Tracker().IncID(id);
511  }
512  return *this;
513  }
514 
516  Ptr<TYPE> & operator=(Ptr<TYPE> && _in) {
517  if (internal::ptr_debug) std::cout << "move assignment: " << _in.ptr << std::endl;
518  emp_assert(Tracker().IsDeleted(_in.id) == false, _in.id, "Do not move deleted pointers.");
519  if (ptr != _in.ptr) {
520  Tracker().DecID(id); // Decrement references to former pointer at this position.
521  ptr = _in.ptr;
522  id = _in.id;
523  _in.ptr = nullptr;
524  _in.id = UNTRACKED_ID;
525  }
526  return *this;
527  }
528 
531  template <typename T2>
532  Ptr<TYPE> & operator=(T2 * _in) {
533  if (internal::ptr_debug) std::cout << "raw assignment" << std::endl;
534  emp_assert( (PtrIsConvertable<T2, TYPE>(_in)) );
535 
536  Tracker().DecID(id); // Decrement references to former pointer at this position.
537  ptr = _in; // Update to new pointer.
538 
539  // If this pointer is already active, link to it.
540  if (Tracker().IsActive(ptr)) {
541  id = Tracker().GetCurID(ptr);
542  Tracker().IncID(id);
543  }
544  // Otherwise, since this ptr was passed in as a raw pointer, we do not manage it.
545  else {
546  id = UNTRACKED_ID;
547  }
548 
549  return *this;
550  }
551 
553  template <typename T2>
554  Ptr<TYPE> & operator=(Ptr<T2> _in) {
555  if (internal::ptr_debug) std::cout << "convert-copy assignment" << std::endl;
556  emp_assert( (PtrIsConvertable<T2, TYPE>(_in.Raw())), _in.id );
557  emp_assert(Tracker().IsDeleted(_in.id) == false, _in.id, "Do not copy deleted pointers.");
558  Tracker().DecID(id);
559  ptr = _in.Raw();
560  id = _in.GetID();
561  Tracker().IncID(id);
562  return *this;
563  }
564 
566  TYPE & operator*() {
567  // Make sure a pointer is active and non-null before we dereference it.
568  emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id);
569  emp_assert(ptr != nullptr, "Do not dereference a null pointer!");
570  return *ptr;
571  }
572 
574  const TYPE & operator*() const {
575  // Make sure a pointer is active before we dereference it.
576  emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id);
577  emp_assert(ptr != nullptr, "Do not dereference a null pointer!");
578  return *ptr;
579  }
580 
582  TYPE * operator->() {
583  // Make sure a pointer is active before we follow it.
584  emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id);
585  emp_assert(ptr != nullptr, "Do not follow a null pointer!");
586  return ptr;
587  }
588 
590  TYPE * const operator->() const {
591  // Make sure a pointer is active before we follow it.
592  emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id);
593  emp_assert(ptr != nullptr, "Do not follow a null pointer!");
594  return ptr;
595  }
596 
598  TYPE & operator[](size_t pos) {
599  emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id);
600  emp_assert(Tracker().IsArrayID(id), "Only arrays can be indexed into.", id);
601  emp_assert(Tracker().GetArrayBytes(id) > (pos*sizeof(TYPE)),
602  "Indexing out of range.", id, ptr, pos, sizeof(TYPE), Tracker().GetArrayBytes(id));
603  emp_assert(ptr != nullptr, "Do not follow a null pointer!");
604  return ptr[pos];
605  }
606 
608  const TYPE & operator[](size_t pos) const {
609  emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id);
610  emp_assert(Tracker().IsArrayID(id), "Only arrays can be indexed into.", id);
611  emp_assert(Tracker().GetArrayBytes(id) > (pos*sizeof(TYPE)),
612  "Indexing out of range.", id, ptr, pos, sizeof(TYPE), Tracker().GetArrayBytes(id));
613  emp_assert(ptr != nullptr, "Do not follow a null pointer!");
614  return ptr[pos];
615  }
616 
618  operator TYPE *() {
619  // Make sure a pointer is active before we convert it.
620  emp_assert(Tracker().IsDeleted(id) == false /*, typeid(TYPE).name() */, id);
621 
622  // We should not automatically convert managed pointers to raw pointers; use .Raw()
623  emp_assert(id == UNTRACKED_ID /*, typeid(TYPE).name() */, id);
624  return ptr;
625  }
626 
628  operator bool() { return ptr != nullptr; }
629 
631  operator bool() const { return ptr != nullptr; }
632 
634  bool operator==(const Ptr<TYPE> & in_ptr) const { return ptr == in_ptr.ptr; }
635 
637  bool operator!=(const Ptr<TYPE> & in_ptr) const { return ptr != in_ptr.ptr; }
638 
640  bool operator<(const Ptr<TYPE> & in_ptr) const { return ptr < in_ptr.ptr; }
641 
643  bool operator<=(const Ptr<TYPE> & in_ptr) const { return ptr <= in_ptr.ptr; }
644 
646  bool operator>(const Ptr<TYPE> & in_ptr) const { return ptr > in_ptr.ptr; }
647 
649  bool operator>=(const Ptr<TYPE> & in_ptr) const { return ptr >= in_ptr.ptr; }
650 
651 
653  bool operator==(const TYPE * in_ptr) const { return ptr == in_ptr; }
654 
656  bool operator!=(const TYPE * in_ptr) const { return ptr != in_ptr; }
657 
659  bool operator<(const TYPE * in_ptr) const { return ptr < in_ptr; }
660 
662  bool operator<=(const TYPE * in_ptr) const { return ptr <= in_ptr; }
663 
665  bool operator>(const TYPE * in_ptr) const { return ptr > in_ptr; }
666 
668  bool operator>=(const TYPE * in_ptr) const { return ptr >= in_ptr; }
669 
670 
672  int DebugGetCount() const { return Tracker().GetIDCount(id); }
673  bool DebugIsArray() const { return Tracker().IsArrayID(id); }
674  size_t DebugGetArrayBytes() const { return Tracker().GetArrayBytes(id); }
675  bool DebugIsActive() const { return Tracker().IsActiveID(id); }
676 
677  bool OK() const {
678  // Untracked ID's should not have pointers in the Tracker.
679  if (id == UNTRACKED_ID) return !Tracker().HasPtr(ptr);
680 
681  // Make sure this pointer is linked to the correct info.
682  if (Tracker().GetInfo(id).GetPtr() != ptr) return false;
683 
684  // And make sure that info itself is okay.
685  return Tracker().GetInfo(id).OK();
686  }
687 
688  // Prevent use of new and delete on Ptr
689  // static void* operator new(std::size_t) noexcept {
690  // emp_assert(false, "No Ptr::operator new; use emp::NewPtr for clarity.");
691  // return nullptr;
692  // }
693  // static void* operator new[](std::size_t sz) noexcept {
694  // emp_assert(false, "No Ptr::operator new[]; use emp::NewPtrArray for clarity.");
695  // return nullptr;
696  // }
697  //
698  // static void operator delete(void* ptr, std::size_t sz) {
699  // emp_assert(false, "No Ptr::operator delete; use Delete() member function for clarity.");
700  // }
701  // static void operator delete[](void* ptr, std::size_t sz) {
702  // emp_assert(false, "No Ptr::operator delete[]; use DeleteArray() member function for clarity.");
703  // }
704 
705  };
706 
707 #else
708 
709 
710  template <typename TYPE>
711  class Ptr {
712  private:
713  TYPE * ptr;
714 
715  public:
716  using element_type = TYPE;
717 
718  Ptr() : ptr(nullptr) {}
719  Ptr(const Ptr<TYPE> & _in) : ptr(_in.ptr) {}
720  Ptr(Ptr<TYPE> && _in) : ptr(_in.ptr) {}
721  template <typename T2> Ptr(T2 * in_ptr, bool=false) : ptr(in_ptr) {}
722  template <typename T2> Ptr(T2 * _ptr, size_t, bool) : ptr(_ptr) {}
723  template <typename T2> Ptr(Ptr<T2> _in) : ptr(_in.Raw()) {}
724  Ptr(std::nullptr_t) : Ptr() {}
725  ~Ptr() { ; }
726 
727  bool IsNull() const { return ptr == nullptr; }
728  TYPE * Raw() { return ptr; }
729  const TYPE * const Raw() const { return ptr; }
730  template <typename T2> Ptr<T2> Cast() { return (T2*) ptr; }
731  template <typename T2> const Ptr<const T2> Cast() const { return (T2*) ptr; }
732  template <typename T2> Ptr<T2> DynamicCast() { return dynamic_cast<T2*>(ptr); }
733 
734  template <typename... T>
735  void New(T &&... args) { ptr = new TYPE(std::forward<T>(args)...); } // New raw pointer.
736  void NewArray(size_t array_size) { ptr = new TYPE[array_size]; }
737  void Delete() { delete ptr; }
738  void DeleteArray() { delete [] ptr; }
739 
740  size_t Hash() const {
741  static constexpr size_t shift = internal::Log2(1 + sizeof(TYPE)); // Chop off useless bits...
742  return (size_t)(ptr) >> shift;
743  }
744  struct hash_t { size_t operator()(const Ptr<TYPE> & t) const { return t.Hash(); } };
745 
746  // Copy/Move assignments
747  Ptr<TYPE> & operator=(const Ptr<TYPE> & _in) { ptr = _in.ptr; return *this; }
748  Ptr<TYPE> & operator=(Ptr<TYPE> && _in) { ptr = _in.ptr; _in.ptr = nullptr; return *this; }
749 
750  // Assign to compatible Ptr or raw (non-managed) pointer.
751  template <typename T2> Ptr<TYPE> & operator=(T2 * _in) { ptr = _in; return *this; }
752  template <typename T2> Ptr<TYPE> & operator=(Ptr<T2> _in) { ptr = _in.Raw(); return *this; }
753 
754  // Dereference a pointer.
755  TYPE & operator*() { return *ptr; }
756  const TYPE & operator*() const { return *ptr; }
757 
758  // Follow a pointer.
759  TYPE * operator->() { return ptr; }
760  TYPE * const operator->() const { return ptr; }
761 
762  // Indexing into array
763  TYPE & operator[](size_t pos) { return ptr[pos]; }
764  const TYPE & operator[](size_t pos) const { return ptr[pos]; }
765 
766  // Auto-case to raw pointer type.
767  operator TYPE *() { return ptr; }
768 
769  operator bool() { return ptr != nullptr; }
770  operator bool() const { return ptr != nullptr; }
771 
772  // Comparisons to other Ptr objects
773  bool operator==(const Ptr<TYPE> & in_ptr) const { return ptr == in_ptr.ptr; }
774  bool operator!=(const Ptr<TYPE> & in_ptr) const { return ptr != in_ptr.ptr; }
775  bool operator<(const Ptr<TYPE> & in_ptr) const { return ptr < in_ptr.ptr; }
776  bool operator<=(const Ptr<TYPE> & in_ptr) const { return ptr <= in_ptr.ptr; }
777  bool operator>(const Ptr<TYPE> & in_ptr) const { return ptr > in_ptr.ptr; }
778  bool operator>=(const Ptr<TYPE> & in_ptr) const { return ptr >= in_ptr.ptr; }
779 
780  // Comparisons to raw pointers.
781  bool operator==(const TYPE * in_ptr) const { return ptr == in_ptr; }
782  bool operator!=(const TYPE * in_ptr) const { return ptr != in_ptr; }
783  bool operator<(const TYPE * in_ptr) const { return ptr < in_ptr; }
784  bool operator<=(const TYPE * in_ptr) const { return ptr <= in_ptr; }
785  bool operator>(const TYPE * in_ptr) const { return ptr > in_ptr; }
786  bool operator>=(const TYPE * in_ptr) const { return ptr >= in_ptr; }
787 
788  // Stubs for debug-related functions when outside debug mode.
789  int DebugGetCount() const { return -1; }
790  bool DebugIsArray() const { emp_assert(false); return false; }
791  size_t DebugGetArrayBytes() const { return 0; }
792  bool DebugIsActive() const { return true; }
793  bool OK() const { return true; }
794  };
795 
796 #endif
797 
798  // IO
799  template <typename T>
800  std::ostream & operator<<(std::ostream & out, const emp::Ptr<T> & ptr) {
801  out << ptr.Raw();
802  return out;
803  }
804 
805  // @CAO: Reading a pointer from a stream seems like a terrible idea in most situations, but I
806  // can imagine limited circumstances where it would be needed.
807  template <typename T, typename... Ts>
808  std::istream & operator>>(std::istream & is, emp::Ptr<T> & ptr) {
809  T * val;
810  is >> val;
811  ptr = val;
812  return is;
813  }
814 
816  template <typename T> Ptr<T> ToPtr(T * _in, bool own=false) { return Ptr<T>(_in, own); }
817 
819  template <typename T> Ptr<T> TrackPtr(T * _in, bool own=true) { return Ptr<T>(_in, own); }
820 
822  template <typename T, typename... ARGS> Ptr<T> NewPtr(ARGS &&... args) {
823  auto ptr = new T(std::forward<ARGS>(args)...);
824  // auto ptr = (T*) malloc (sizeof(T)); // Build a new raw pointer.
825  // emp_assert(ptr); // No exceptions in emscripten; assert alloc!
826  // new (ptr) T(std::forward<ARGS>(args)...); // Special new that uses allocated space.
827  return Ptr<T>(ptr, true);
828  }
829 
831  template <typename T> Ptr<T> CopyPtr(Ptr<T> in) { return NewPtr<T>(*in); }
832 
834  template <typename T> emp::vector<Ptr<T>> CopyPtrs(const emp::vector<Ptr<T>> & in) {
835  emp::vector<Ptr<T>> out_ptrs(in.size());
836  for (size_t i = 0; i < in.size(); i++) out_ptrs[i] = CopyPtr(in[i]);
837  return out_ptrs;
838  }
839 
841  template <typename T> emp::vector<Ptr<T>> ClonePtrs(const emp::vector<Ptr<T>> & in) {
842  emp::vector<Ptr<T>> out_ptrs(in.size());
843  for (size_t i = 0; i < in.size(); i++) out_ptrs[i] = in[i]->Clone();
844  return out_ptrs;
845  }
846 
848  template <typename T, typename... ARGS> Ptr<T> NewArrayPtr(size_t array_size, ARGS &&... args) {
849  auto ptr = new T[array_size]; // Build a new raw pointer.
850  // const size_t alloc_size = array_size * sizeof(T);
851  // auto ptr = (T*) malloc (alloc_size);
852  emp_assert(ptr, array_size); // No exceptions in emscripten; assert alloc!
853  // for (size_t i = 0; i < array_size; i++) { // Loop through all array elements.
854  // new (ptr + i*sizeof(T)) T(args...); // ...and initialize them.
855  // }
856  return Ptr<T>(ptr, array_size, true);
857  }
858 
859 
860 }
861 
862 #endif // EMP_PTR_H
static constexpr size_t Log2(size_t x)
An anonymous log2 calculator for hashing below.
Definition: Ptr.h:36
Ptr< T > ToPtr(T *_in, bool own=false)
Convert a T* to a Ptr<T>. By default, don&#39;t track.
Definition: Ptr.h:816
size_t GetNumIDs() const
Lookup how many pointers are being tracked.
Definition: Ptr.h:183
int GetIDCount(size_t id) const
How many Ptr objects are associated with an ID?
Definition: Ptr.h:216
void Dec(size_t id)
Remove a pointer.
Definition: Ptr.h:100
bool operator>=(const Ptr< TYPE > &in_ptr) const
Definition: Ptr.h:778
static PtrTracker & Get()
Treat this class as a singleton with a single Get() method to retrieve it.
Definition: Ptr.h:167
PtrStatus
Definition: Ptr.h:43
emp::vector< Ptr< T > > CopyPtrs(const emp::vector< Ptr< T >> &in)
Copy a vector of objects pointed to; return a vector of Ptrs to the new copies.
Definition: Ptr.h:834
Ptr(T2 *_ptr, size_t, bool)
Construct from array.
Definition: Ptr.h:722
size_t GetCurID(const void *ptr)
Retrive the ID associated with a pointer.
Definition: Ptr.h:180
size_t DebugGetArrayBytes() const
Definition: Ptr.h:791
void Delete()
Definition: Ptr.h:737
bool DebugIsArray() const
Definition: Ptr.h:790
TYPE * Raw()
Definition: Ptr.h:728
size_t GetArrayBytes() const noexcept
If this ptr is to an array, how many bytes large is the array (may be different from size!) ...
Definition: Ptr.h:81
Ptr< TYPE > & operator=(const Ptr< TYPE > &_in)
Definition: Ptr.h:747
PtrInfo(const void *_ptr)
Definition: Ptr.h:53
const TYPE & operator*() const
Definition: Ptr.h:756
size_t Hash() const
Definition: Ptr.h:740
size_t New(const void *ptr)
This pointer was just created as a Ptr!
Definition: Ptr.h:222
std::istream & operator>>(std::istream &is, emp::Ptr< T > &ptr)
Definition: Ptr.h:808
Ptr< T > NewPtr(ARGS &&...args)
Create a new Ptr of the target type; use the args in the constructor.
Definition: Ptr.h:822
int GetCount() const noexcept
How many Ptr objects point to the associated position?
Definition: Ptr.h:78
int DebugGetCount() const
Definition: Ptr.h:789
bool IsArrayID(size_t id)
Is an ID associated with an array?
Definition: Ptr.h:210
Ptr()
Default constructor.
Definition: Ptr.h:718
~PtrTracker()
Definition: Ptr.h:147
Ptr< T > NewArrayPtr(size_t array_size, ARGS &&...args)
Create a pointer to an array of objects.
Definition: Ptr.h:848
const TYPE *const Raw() const
Definition: Ptr.h:729
Ptr< TYPE > & operator=(Ptr< TYPE > &&_in)
Definition: Ptr.h:748
Definition: Ptr.h:744
size_t size() const
Definition: vector.h:151
emp::vector< Ptr< T > > ClonePtrs(const emp::vector< Ptr< T >> &in)
Copy a vector of objects pointed to by using their Clone() member function; return vector...
Definition: Ptr.h:841
void emplace_back(ARGS &&...args)
Definition: vector.h:219
bool operator<=(const TYPE *in_ptr) const
Definition: Ptr.h:784
PtrInfo & GetInfo(const void *ptr)
Get the info associated with an existing pointer.
Definition: Ptr.h:170
Ptr(std::nullptr_t)
From nullptr.
Definition: Ptr.h:724
A more dynamic replacement for standard library asserts.
bool OK() const
Definition: Ptr.h:793
PtrInfo & GetInfo(size_t id)
Definition: Ptr.h:171
bool operator!=(const Ptr< TYPE > &in_ptr) const
Definition: Ptr.h:774
bool operator>=(const TYPE *in_ptr) const
Definition: Ptr.h:786
size_t GetArrayBytes(size_t id) const
How big is an array associated with an ID?
Definition: Ptr.h:186
Ptr< TYPE > & operator=(T2 *_in)
Definition: Ptr.h:751
void DeleteArray()
Definition: Ptr.h:738
Ptr< T2 > DynamicCast()
Definition: Ptr.h:732
Ptr< T > TrackPtr(T *_in, bool own=true)
Convert a T* to a Ptr<T> that we DO track.
Definition: Ptr.h:819
static const PrintStr endl("<br>")
Pre-define emp::endl to insert a "<br>" and thus acting like a newline.
~Ptr()
Destructor.
Definition: Ptr.h:725
const Ptr< const T2 > Cast() const
Definition: Ptr.h:731
Definition: Ptr.h:45
TYPE *const operator->() const
Definition: Ptr.h:760
bool IsActiveID(size_t id)
Is a pointer id associated with a pointer that&#39;s active and ready to be used?
Definition: Ptr.h:203
bool GetPtrDebug()
Definition: Ptr.h:41
void SetPtrDebug(bool _d=true)
Definition: Ptr.h:40
Ptr(const Ptr< TYPE > &_in)
Copy constructor.
Definition: Ptr.h:719
Ptr< T > CopyPtr(Ptr< T > in)
Copy an object pointed to and return a Ptr to the copy.
Definition: Ptr.h:831
std::size_t Hash(const T &x)
Definition: meta.h:202
Ptr< T2 > Cast()
Definition: Ptr.h:730
bool operator>(const TYPE *in_ptr) const
Definition: Ptr.h:785
bool operator==(const Ptr< TYPE > &in_ptr) const
Definition: Ptr.h:773
const TYPE & operator[](size_t pos) const
Definition: Ptr.h:764
Ptr< TYPE > & operator=(Ptr< T2 > _in)
Definition: Ptr.h:752
bool DebugIsActive() const
Definition: Ptr.h:792
world_t element_type
Definition: Ptr.h:716
Ptr(Ptr< T2 > _in)
From compatible Ptr.
Definition: Ptr.h:723
Ptr(Ptr< TYPE > &&_in)
Move constructor.
Definition: Ptr.h:720
void IncID(size_t id)
Increment the nuber of Pointers associated with an ID.
Definition: Ptr.h:248
void DecID(size_t id)
Decrement the nuber of Pointers associated with an ID.
Definition: Ptr.h:255
A drop-in wrapper for std::vector; adds on bounds checking in debug mode.
If we are in emscripten, make sure to include the header.
Definition: array.h:37
bool HasPtr(const void *ptr) const
Determine if a pointer is being tracked.
Definition: Ptr.h:174
Facilitate tracking of all Ptr objects in this run.
Definition: Ptr.h:131
bool operator==(const TYPE *in_ptr) const
Definition: Ptr.h:781
Definition: Ptr.h:711
Build a debug wrapper emp::vector around std::vector.
Definition: vector.h:42
bool IsActive(const void *ptr)
Is a pointer active and ready to be used?
Definition: Ptr.h:196
void SetArray(size_t bytes) noexcept
Denote that this pointer is an array.
Definition: Ptr.h:90
void NewArray(size_t array_size)
Definition: Ptr.h:736
#define emp_assert(...)
Definition: assert.h:199
bool operator>(const Ptr< TYPE > &in_ptr) const
Definition: Ptr.h:777
TYPE & operator*()
Definition: Ptr.h:755
void New(T &&...args)
Definition: Ptr.h:735
TYPE & operator[](size_t pos)
Definition: Ptr.h:763
size_t NewArray(const void *ptr, size_t array_bytes)
This pointer was just created as a Ptr ARRAY!
Definition: Ptr.h:240
~PtrInfo()
Definition: Ptr.h:70
void MarkDeleted()
Indicate that the associated position has been deleted.
Definition: Ptr.h:109
static bool ptr_debug
Definition: Ptr.h:38
PtrInfo(const void *_ptr, size_t _array_bytes)
Definition: Ptr.h:56
Ptr(T2 *in_ptr, bool=false)
Construct from raw ptr.
Definition: Ptr.h:721
void Inc(size_t id)
Add one more pointer.
Definition: Ptr.h:93
void MarkDeleted(size_t id)
Mark the pointers associated with this ID as deleted.
Definition: Ptr.h:265
bool IsNull() const
Definition: Ptr.h:727
TYPE * operator->()
Definition: Ptr.h:759
const void * GetPtr() const noexcept
What pointer does this one hold information about?
Definition: Ptr.h:75
bool operator<(const TYPE *in_ptr) const
Definition: Ptr.h:783
size_t operator()(const Ptr< TYPE > &t) const
Definition: Ptr.h:744
bool IsArray() const noexcept
Is this pointer pointing to an array?
Definition: Ptr.h:87
bool operator!=(const TYPE *in_ptr) const
Definition: Ptr.h:782
bool IsActive() const noexcept
Is this pointer currently valid to access?
Definition: Ptr.h:84
bool OK() const noexcept
Debug utility to determine if everything looks okay with this pointer&#39;s information.
Definition: Ptr.h:115
bool IsDeleted(size_t id) const
Check if an ID is for a pointer that has been deleted.
Definition: Ptr.h:189