Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

instantiation of static std::map inside a c++ template class

I’m having trouble getting some c++ templates to work. Here’s what I want to do:

template <class T, const int I = 16, const int N = 65536, const int M = 2>
class pool
{
public:
  pool();
  ~pool();

  void clear();

  T * get();
  void put(void * p);
};


template<class T, const int I = 16, const int N = 65536, const int M = 2>
class pool_of_pools {
private:
  static std::map<pthread_t, pool<T,I,N,M>*> pools;
public:
  typedef typename std::map<pthread_t, pool<T,I,N,M>*>::iterator iterator_type;

  static void registerThread(pthread_t id) {
    if (pools.find(id) == pools.end())
      pools[id] = new pool<T,I,N,M>();
  }

  static void registerThread() {
    registerThread(pthread_self());
  }

  static void clear() {
    for (iterator_type it = pools.begin(); it != pools.end(); ++it)
      it->second->clear();
  }

  static pool<T,I,N,M>* getPool() {
    if (pools.find(pthread_self()) == pools.end())  // <-- warning here
      registerThread();
            
    return pools[pthread_self()];
  }
};

The idea is to have a pool of pools where the current thread always allocates from it’s own pool
but any thread can release the objects and they will go back to the correct pool. A pointer to their origin pool is stored within the object itself. When it runs, worker threads will allocate objects but a single collector thread will free them all when done.

Notice the static std::map. When I try to instantiate that as:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

template<typename T, int I, int N, int M> std::map<pthread_t, pool<T,I,N,M>*> pool_of_pools<T,I,N,M>::pools;

this gives me a warning of instantiation of variable 'pool_of_pools<int>::pools' required here, but no definition is available. It compiles and runs, but when different threads call getPool, they seem to register with their own thread-local(?) versions of pools. I create a thread, then call registerThread(id) with the thread id, and also registerThread() to register the main thread. But when I call getPool from the created thread, the address of pools (&pools) is different than what it was when I registered the two threads from the main thread. I’m not sure how that’s possible since it’s static and a global.

When I try to instantiate the map as:

template<> std::map<pthread_t, void*> pool_of_pools<int,512*1024,1024*1024,2>::pools;

it won’t compile. I get a linker error undefined reference to pool_of_pools<int, 52488, 1048576, 2>::pools

I’ve done single templated classes before but never a templated class that has a static templated class instance in it.

How do I do this? Is it even possible?

>Solution :

The static needs to be defined somewhere for any instantiation of the class template. A way around this is to encapsulate your static data in a function. This does mean that it’s not constructed until something uses it, but that probably isn’t a big issue for you.

So, instead of using pools everywhere, turn that into the function pools() which owns the static. Provided you’re using at least C++11, the standard guarantees thread-safety which protects initialization of the static data.

static std::map<pthread_t, pool<T,I,N,M>*>& pools()
{
  static std::map<pthread_t, pool<T,I,N,M>*> s_pools;
  return s_pools;
}

Then it’s just a matter of changing everywhere that you use the identifier pools to instead call the function pools().

Full code with these changes:

template<class T, const int I = 16, const int N = 65536, const int M = 2>
class pool_of_pools {
private:
  static std::map<pthread_t, pool<T,I,N,M>*>& pools()
  {
    static std::map<pthread_t, pool<T,I,N,M>*> s_pools;
    return s_pools;
  }

public:
  typedef typename std::map<pthread_t, pool<T,I,N,M>*>::iterator iterator_type;

  static void registerThread(pthread_t id) {
    if (pools().find(id) == pools().end())
      pools()[id] = new pool<T,I,N,M>();
  }

  static void registerThread() {
    registerThread(pthread_self());
  }

  static void clear() {
    for (iterator_type it = pools().begin(); it != pools().end(); ++it)
      it->second->clear();
  }

  static pool<T,I,N,M>* getPool() {
    if (pools().find(pthread_self()) == pools().end())  // <-- warning here
      registerThread();
            
    return pools()[pthread_self()];
  }
};
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading