Ferenc Deak
Ferenc Deak

Reputation: 35408

C++ finding the instantiated templates

I have just discovered the following jewel in the code (the example is very simplified, but the logic is the same):

template <class T>
class garbage_bin
{
private:
    garbage_bin<T>(void)
    {
    }
   static garbage_bin<T>* pinstance;
public:
   static garbage_bin<T>& instance()
   {
       if(pinstance == NULL)
       {
           pinstance = new garbage_bin<T>();
       }

       return *pinstance;
   }

   void empty()
   {
       for(size_t i=0; i<items.size(); i++)
       {
           free (items[i]);
       }
   }

   void throwIn(T item)
   {
       items.push_back(item);
   }

   vector<T> items;
};

and then somewhere in the code (this is just ONE example ... there are thousands like this):

char* r = strdup(src);
garbage_bin<char*>::instance().throwIn(r);

and later somewhere in the code, right before the exit ...

garbage_bin<char*>::instance().empty();
garbage_bin<molecules*>::instance().empty();
garbage_bin<CDatabaseUsers*>::instance().empty();

and so on ...

so as we can see, this implements a garbage bin class, in which you can "throw in" all kind of objects, and at a later stage to avoid the memory leaks you "empty" the garbage bin. But here comes the big bottleneck: In order for this to work properly, you need to know all the classes for which this garbage bin was instantiated in order to empty them...

The most direct solution I was thinking of is to create a map of typeid calls for the instantiations, and assign the garbage_bin<T>::instance() to the name however an ancient compiler decided that he does not like this approach.

Obviously, I can make a search through the code to find all the templatizations, but I'm wondering ... is there a simpler way to do this?

Upvotes: 0

Views: 76

Answers (3)

Cassio Neri
Cassio Neri

Reputation: 20503

I would rather replace garbage_bin<T> with std::vector<std::unique_ptr<T>> (or, maybe, std::vector<std::shared_ptr<T>>).

If you want to restrict the interface, then reimplement garbage_bin<T> as a wrapper around std::vector<std::unique_ptr<T>>. In this way, you probably don't need the empty method since the destructors of std::vector and std::unique_ptr will clean-up after themselves. However, if you want to empty the bin before destruction, then implement empty just calling std::vector::clear.

I understand that this is not exactly the simplest thing to do (because the OP says there are "thousands" of places to be changed) but a refactoring of this bad design is clearly needed.

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 476970

You could add a registry for things to be deleted:

Registry registry;

// ...

if (pinstance == NULL)
{
     pinstance = new garbage_bin<T>();
     registry.add<T>();
}

// ...

registry.clear_all();

For example:

class Registry
{
     struct Base
     { 
         virtual ~Base() {}
         virtual void clear() = 0;
     };

     template <typename T> struct Derived : Base
     {
         virtual void clear() { garbage_bin<T*>::instance().empty(); }
     };

     std::vector<std::unique_ptr<Base>> targets;

public:
     void clear_all() { for (auto & p : targets) { p->clear(); } }

     template <typename T> void add()
     {
          targets.emplace_back(new Derived<T>);
     }
};

Upvotes: 0

John Zwinck
John Zwinck

Reputation: 249123

Garbage bin bin!

class garbage_bin_base;

class garbage_bin_bin {
public:
    void throwIn(garbage_bin_base* rubbish) { items.push_back(rubbish); }
    void empty() { for (auto item: items) item->empty(); }
private:
    vector<garbage_bin_base*> items;
};

class garbage_bin_base {
public:
    virtual empty() = 0;
    garbage_bin_base() { garbage_bin_bin::instance().throwIn(this); }
};

template <typename T>
class garbage_bin : public garbage_bin_base {
};

Upvotes: 1

Related Questions