remove related objects from list c

The setup lends itself to finding a solution where converting a pointer to an iterator is a constant-time operation. Boost.Intrusive offers this feature. This will require changes to your code though; if you were not careful about encapsulation, these changes might be significant. A boost::intrusive::list is functionally similar to a std::list, but requires some changes to your data structure. This option might not be for everyone.

Another feature of Boost.Intrusive is that sometimes you do not need to explicitly convert a pointer to an iterator. If you enable auto-unlinking, then the actual deletion from the list happens behind the scenes in a destructor. This is not a good option if you need to get the size of your list in constant time, though. (Nothing in the question indicates that getting the size of the list is needed, so I’ll go ahead with this approach.)

If you had a container of objects, I might let you work through the documentation for the intrusive list. However, your use of pointers makes the conversion potentially confusing, so I’ll walk through the setup. The setup begins with the following.

#include <boost/intrusive/list.hpp>

// Shorten the needed boost namespace.
namespace bi = boost::intrusive;

Since the list of high-level objects contains pointers, an auxiliary structure is needed. We need what amounts to a pointer that derives from a class provided by Boost. (I will proceed assuming that the objects created in CallbackAttachLowLevelObject() must be destroyed in CallbackDetachLowLevelObject(). Hence, I’ve changed the raw pointer to a smart pointer.)

#include <memory>
#include <utility>

// The auxiliary structure that will be stored in the high level list:
// The hook supplies the intrusive infrastructure.
// The link_mode enables auto-unlinking.
class ListEntry :  public bi::list_base_hook< bi::link_mode<bi::auto_unlink> >
{
public:
    // The expected way to construct this.
    explicit ListEntry(std::unique_ptr<HighLevelObject> && p) : ptr(std::move(p)) {}
    // Another option would be to forward parameters for constructing HighLevelObject,
    // and have the constructor call make_unique. I'll leave that as an exercise.
    
    // Make this class look like a pointer to HighLevelObject.
    const std::unique_ptr<HighLevelObject> & operator->() const { return ptr; }
    HighLevelObject& operator*() const { return *ptr; }

private:
    std::unique_ptr<HighLevelObject> ptr;
};

The definition of the list becomes the following. We need to specify non-constant time size() to allow auto-unlinking.

bi::list<ListEntry, bi::constant_time_size<false>> high_level_objects_list;

These changes require some changes to the “attach” callback. I’ll present them before going on to the “detach” callback.

// Callback that notifies when LowLevelObject* is added to low_level_objects_list.
void CallbackAttachLowLevelObject(LowLevelObject* low_level_object) {
    // Dynamically allocate the entry, in addition to allocating the high level object.
    ListEntry * entry = new ListEntry(std::make_unique<HighLevelObject>());
    (*entry)->low_level_object = low_level_object; // Double indirection needed here.
    low_level_object->variable = entry;
    high_level_objects_list.push_back(*entry);     // Intentional indirection here!
}

With this prep work, the cleanup is in your destructors, as is appropriate for RAII. Your “detach” just has to initiate the process. One line suffices.

void CallbackDetachLowLevelObject(LowLevelObject* low_level_object) {
    delete static_cast<ListEntry *>(low_level_object->variable);
}

There (appropriately) is not enough context in the question to explain why the high level list is of pointers instead of being of objects. One potential reason is that the high-level object is polymorphic, and the use of pointers avoids slicing. If this is the case (or if there is not a good reason for using pointers), an intrusive list could be designed with less impact on existing code. The caveat here is that changes to HighLevelObject are required.

The initial setup is the same as before.

#include <boost/intrusive/list.hpp>

// Shorten the needed boost namespace.
namespace bi = boost::intrusive;

Next, have HighLevelObject derive from the hook.

class HighLevelObject : public bi::list_base_hook< bi::link_mode<bi::auto_unlink> > {
    public:
    LowLevelObject* low_level_object;
};

In this situation, the list is of HighLevelObjects, not of pointers, nor of pointer stand-ins.

bi::list<HighLevelObject, bi::constant_time_size<false>> high_level_objects_list;

The “attach” callback reverts to almost what is in the question. The one change to this function is that the object itself is pushed into the list, not a pointer. This is why slicing is not a problem; it’s not a copy that is added to the list, but the object itself.

    high_level_objects_list.push_back(*high_level_object);  // Intentional indirection!

The rest of your code might work as-is. We just need the “detach” callback, which again is a one-liner.

void CallbackDetachLowLevelObject(LowLevelObject* low_level_object) {
    delete static_cast<HighLevelObject *>(low_level_object->variable);
}

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top