Reputation: 5552
Sorry that the question of this problem might be a bit vague. I'm trying to port this ObjectPool code from C# into C++ but seems there are some parts where I don't know how I should proceed. Codes are as follows:
using System;
namespace FastRank
{
public class ObjectPool<T> where T : class, new()
{
private int _count;
private T[] _pool;
public ObjectPool(int initSize)
{
_pool = new T[initSize];
}
public T Get()
{
lock (_pool)
{
if (_count > 0)
{
--_count;
T item = _pool[_count];
_pool[_count] = null;
return item;
}
}
return new T();
}
public void Return(T item)
{
lock (_pool)
{
if (_count == _pool.Length)
Array.Resize(ref _pool, _pool.Length*2 + 1);
_pool[_count++] = item;
}
}
}
}
My questions are:
1) How should I implement that constraint on generic parameter T in C++? (class, new())
2) Is there a simple way to implement the mutex lock part?
3) Will it be more efficient to define _pool as vector instead of T[] in C++?
edit -> Implemented something as:
#include "object_pool.h"
#include <boost/thread.hpp>
#include <vector>
using namespace std;
template <class T>
ObjectPool<T>::ObjectPool(int init_size) {
pool_.reserve(init_size);
}
template <class T>
T ObjectPool<T>::Get() {
boost::lock_guard<boost::mutex> lock(guard_);
int sz = (int) pool_.size();
if (sz == 0) {
throw "Object pool size is now zero.";
}
else {
T item = pool_[sz-1];
pool_.pop_back();
return item;
}
}
template <class T>
void ObjectPool<T>::Return(T item) {
boost::lock_guard<boost::mutex> lock(guard_);
pool_.push_back(item);
}
Wondering if there's any problem with this code...
Upvotes: 1
Views: 691
Reputation: 51263
This is how I would have implemented it. You could replace tbb::concurrenct_queue
with a std::mutex
guarded std::queue
, although that would be less efficient. With this implementation you do need to worry about "returning" objects back to the pool, it's handled automatically.
#include <memory>
#include <tbb/concurrent_queue.h>
namespace FastRank
{
template<typename T>
class object_pool
{
typedef tbb::concurrent_bounded_queue<std::shared_ptr<T>> pool_t;
std::shared_ptr<pool_t> pool_;
public:
object_pool() : pool_(new pool_t())
{
}
std::shared_ptr<T> get()
{
std::shared_ptr<T> ptr;
if(!pool_.try_pop(ptr))
ptr = std::make_shared<T>();
auto pool = pool_;
return std::shared_ptr<T>(ptr.get(), [pool, ptr](T*){pool->push(ptr);});
}
}
}
Without concurrent_queue
#include <memory>
#include <queue>
#include <boost/mutex.hpp>
namespace FastRank
{
template<typename T>
class object_pool
{
typedef std::pair<std::queue<std::shared_ptr<T>>, boost::mutex> pool_t;
std::shared_ptr<pool_t> pool_;
public:
object_pool() : pool_(new pool_t())
{
}
std::shared_ptr<T> get()
{
std::shared_ptr<T> ptr;
{
boost::scoped_lock<boost::mutex> lock(pool_->second);
if(!pool_->first.empty())
{
ptr = std::move(pool->first.front());
pool->first.pop()
}
}
if(!ptr)
ptr = std::make_shared<T>();
auto pool = pool_;
return std::shared_ptr<T>(ptr.get(), [pool, ptr](T*)
{
boost::scoped_lock<boost::mutex> lock(pool->second);
pool->push(ptr);
});
}
}
}
Upvotes: 0
Reputation: 477358
Here's a naive snippet that illustrates one possible approach:
#include <mutex>
template <typename T>
class SyncStack
{
T * m_data;
std::size_t m_size;
std::size_t m_count;
std::mutex m_lock;
public:
T get()
{
std::lock_guard<std::mutex> lock(m_lock);
if (m_count == 0) { throw UnderrunException; }
--m_count;
T x(m_data[m_count]);
m_data[m_count].~T();
return x;
}
void put(T x)
{
std::lock_guard<std::mutex> lock(m_lock);
::new (m_data + m_count) T(std::move(x));
++m_count;
}
};
This example assumes that m_data
points to infinite memory. Reallocation is a bit tricky and involves making lots of copies.
A simpler approach would be to wrap your synchronized structure around another, existing standard container such as std::vector<T>
.
Upvotes: 1
Reputation: 66951
1) How should I implement that constraint on generic parameter T in C++? (class, new())
In general, don't. If it fails to meet the constraints, it will fail to compile. Simple enough. There are tricky ways to get better error messages, but I've forgotten them offhand because I never bothered.
2) Is there a simple way to implement the mutex lock part?
Use a boost::mutex.
3) Will it be more efficient to define _pool as vector instead of T[] in C++?
Considering that you can't have a local T[]
without a size, yes. Use a std::vector
. (You can have it as a parameter, but not at the variable definition.)
Upvotes: 2