在C++模板类内部实例化静态std::map

huangapple go评论101阅读模式
英文:

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

问题

我遇到了一些让C++模板正常工作的问题。以下是我想要做的事情:

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()];
  }
};

这个想法是有一个池的池,其中当前线程总是从它自己的池中分配,但任何线程都可以释放对象,并且它们将返回到正确的池中。对象自身内部存储了它们的原始池的指针。当运行时,工作线程将分配对象,但单个收集器线程在完成时会释放它们。

请注意static std::map。当我尝试将其实例化为:

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;

这会导致警告:要求实例化变量'pool_of_pools<int>::pools',但没有可用的定义。它编译并运行,但当不同的线程调用getPool时,它们似乎会注册到自己线程本地的pools版本。我创建一个线程,然后从主线程注册两个线程,但当我从创建的线程调用getPool时,pools的地址(&pools)与我从主线程注册两个线程时的地址不同。我不确定如何可能发生这种情况,因为它是静态的和全局的。

当我尝试将映射实例化为:

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

它无法编译。我得到一个链接器错误undefined reference to pool_of_pools<int, 52488, 1048576, 2>::pools

我以前做过单一模板类,但从未在其中有一个静态的模板类实例。我该如何做?这个问题是否可能解决?

英文:

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

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

  void clear();

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


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

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

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

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

  static pool&lt;T,I,N,M&gt;* getPool() {
    if (pools.find(pthread_self()) == pools.end())  // &lt;-- 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:

template&lt;typename T, int I, int N, int M&gt; std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt; pool_of_pools&lt;T,I,N,M&gt;::pools;

this gives me a warning of instantiation of variable &#39;pool_of_pools&lt;int&gt;::pools&#39; 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 (&amp;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&lt;&gt; std::map&lt;pthread_t, void*&gt; pool_of_pools&lt;int,512*1024,1024*1024,2&gt;::pools;

it won't compile. I get a linker error undefined reference to pool_of_pools&lt;int, 52488, 1048576, 2&gt;::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?

答案1

得分: 3

以下是您要翻译的内容:

"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&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt;&amp; pools()
{
  static std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt; 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&lt;class T, const int I = 16, const int N = 65536, const int M = 2&gt;
class pool_of_pools {
private:
  static std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt;&amp; pools()
  {
    static std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt; s_pools;
    return s_pools;
  }

public:
  typedef typename std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt;::iterator iterator_type;

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

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

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

  static pool&lt;T,I,N,M&gt;* getPool() {
    if (pools().find(pthread_self()) == pools().end())  // &lt;-- warning here
      registerThread();
            
    return pools()[pthread_self()];
  }
};
英文:

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&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt;&amp; pools()
{
  static std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt; 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&lt;class T, const int I = 16, const int N = 65536, const int M = 2&gt;
class pool_of_pools {
private:
  static std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt;&amp; pools()
  {
    static std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt; s_pools;
    return s_pools;
  }

public:
  typedef typename std::map&lt;pthread_t, pool&lt;T,I,N,M&gt;*&gt;::iterator iterator_type;

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

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

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

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

huangapple
  • 本文由 发表于 2023年7月17日 11:02:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76701262.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定