Tracking objects (distributed over glass)

In a fairly large application, I want to track some statistics about objects of a particular class. In order not to degrade performance, I want the statistics to be updated in the pull configuration. Therefore, I need to have a link to every living object in a specific place. Is there an idiomatic way:

  • Creating, searching, repeating such links
  • Manage it automatically (i.e. remove the link upon destruction)

I mean the set of smart pointers here, but memory management will be somewhat inverted: instead of destroying the object when the smart pointer is destroyed, I would like the smart pointer to be deleted when the object is destroyed. Ideally, I do not want to reinvent the wheel.

I could live with a delay in deleting pointers, I just needed to quickly invalidate them.

edit: Because the paddy asked for this: the reason for the pull-based collection is that retrieving information can be relatively expensive. Clicking is obviously a clean solution, but considered too costly.

+6
source share
2 answers

There is no particular language feature that allows you to do this. Sometimes object tracking is handled by moving your own memory allocator, but it doesn't work as easily on the stack.

But if you use only the stack, it actually simplifies your problem by assuming that the objects being tracked are on the same thread . C ++ gives special guarantees about the order of construction and destruction on the stack. That is, the order of destruction exactly corresponds to the order of construction.

So you can use this to store one pointer in each object, as well as one static pointer to keep track of the very last. Now you have a stack of objects presented as a linked list.

template <typename T> class Trackable { public: Trackable() : previous( current() ) { current() = this; } ~Trackable() { current() = previous; } // External interface static const T *head() const { return dynamic_cast<const T*>( current() ); } const T *next() const { return dynamic_cast<const T*>( previous ); } private: static Trackable * & current() { static Trackable *ptr = nullptr; return ptr; } Trackable *previous; } 

Example:

 struct Foo : Trackable<Foo> {}; struct Bar : Trackable<Bar> {}; // ::: // Walk linked list of Foo objects currently on stack. for( Foo *foo = Foo::head(); foo; foo = foo->next() ) { // Do kung foo } 

Now, admittedly, this is a very simplified solution. In a large application, you may have several stacks using your objects. You can process stacks across multiple threads by making current() use thread_local semantics. Although you need magic to make this work, because head() will need to point to the thread registry, and this will require synchronization.

You definitely do not want to synchronize all the stacks into one list, because it will kill the scalability of your program.

As for your traction requirement, I assume this is a separate thread that wants to go through the list. You will need a synchronization method such that the entire structure or destruction of a new object is locked inside Trackable<T> while the list repeats. Or similar.

But at least you could take this basic idea and expand it to your needs.

Remember that you cannot use this simple approach if you dynamically select your objects. To do this, you will need a bidirectional list.

+2
source

The easiest approach is to have a code inside each object so that it is registered when creating an instance and deleted when destroyed. This code can be easily entered using CRTP:

 template <class T> struct AutoRef { static auto &all() { static std::set<T*> theSet; return theSet; } private: friend T; AutoRef() { all().insert(static_cast<T*>(this)); } ~AutoRef() { all().erase(static_cast<T*>(this)); } }; 

Now the Foo class can inherit from AutoRef<Foo> so that its instances are referenced inside AutoRef<Foo>::all() .

Look at the living at Coliru

+2
source

Source: https://habr.com/ru/post/1012992/


All Articles