- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
#include <iostream>
#include <list>
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>
#include <type_traits>
#include <assert.h>
using namespace std;
template<class Data>
class UnboundedQueueForNonThrowMovable
{
static_assert(std::is_nothrow_move_constructible<Data>::value, "Data must be nonthrow movable type.");
static_assert(!std::is_array<Data>::value, "Data must not be c-array.");
public:
typedef Data value_type;
private:
typedef std::queue<Data, std::list<Data>> Queue;
typedef std::unique_lock<std::mutex> Lock;
Queue _queue;
std::mutex _lockQueue;
std::condition_variable _pushToQueue;
UnboundedQueueForNonThrowMovable(const UnboundedQueueForNonThrowMovable&) = delete;
UnboundedQueueForNonThrowMovable(UnboundedQueueForNonThrowMovable&&) = delete;
UnboundedQueueForNonThrowMovable& operator=(const UnboundedQueueForNonThrowMovable&) = delete;
UnboundedQueueForNonThrowMovable& operator=(UnboundedQueueForNonThrowMovable&&) = delete;
public:
UnboundedQueueForNonThrowMovable(void){}
void push(Data&& data)
{
Lock lockerQueue(this->_lockQueue);
this->_queue.push(std::move(data));
this->_pushToQueue.notify_all();//_condition.notify_one(); most optimal, but can cause deadlock.
}
void push(Data& data)
{
this->push(std::move(data));
}
bool emptyUnstable(void) const
{
Lock lockerQueue(this->_lockQueue);
return this->_queue.empty();
}
Data pop(void)
{
Lock lockerQueue(this->_lockQueue);
this->_pushToQueue.wait(lockerQueue, [this](void){return !this->_queue.empty();});
assert(!this->_queue.empty());
Data result = std::move(this->_queue.front());
this->_queue.pop();
return result;
}
template< class Rep, class Period>
Data pop(const std::chrono::duration<Rep, Period> WaitDuration)
{
Lock lockerQueue(this->_lockQueue);
if(!this->_pushToQueue.wait(lockerQueue, WaitDuration, [this](void){return !this->_queue.empty();}))
return Data();
assert(!this->_queue.empty());
Data result = std::move(this->_queue.front());
this->_queue.pop();
return result;
}
};
template<class Data>
using UnboundedQueueForUniquePtr = UnboundedQueueForNonThrowMovable<std::unique_ptr<Data>>;
template<class Data>
using UnboundedQueueForSharedPtr = UnboundedQueueForNonThrowMovable<std::shared_ptr<const Data>>;
template<class Data>
using UnboundedQueue = UnboundedQueueForUniquePtr<Data>;
int main() {
cout<<"ok"<<endl;
{
//UnboundedQueueForSharedPtr<int>::value_type == std::shared_ptr<const int>
UnboundedQueueForSharedPtr<int> queueSharedPtr;
//auto a = std::make_shared<const int>(45);
auto a = std::make_shared<int>(45);
assert(a);
queueSharedPtr.push(a);
assert(!a);//Fired if you use "auto a = std::make_shared<int>(45)" below. It is compiler bug, I think, because previus code line must cause compile error.
auto b = queueSharedPtr.pop();//std::shared_ptr<const int>
assert(b);
cout<<*b<<endl;
assert(*b==45);
laMer007 16.12.2013 15:49 # 0
roman-kashitsyn 16.12.2013 15:58 # 0
roman-kashitsyn 16.12.2013 16:26 # 0
laMer007 16.12.2013 17:26 # 0
"a" (которая lvalue) магически превратиться в rvalue не может, поэтому выбираться перегрузка в queueSharedPtr.push(a); как void push(Data&& data) по идеи не должна выбираться.
std::shared_ptr<const int> a - это не константная lvalue. У нас принимается не константная ссылка на std::shared_ptr<int>.
1) Внутри неявный или явный через конструктор std::shared_ptr каст из std::shared_ptr<const T> в std::shared_ptr<T> (но не наоборот) без std::const_pointer_cast должен быть запрещен.
2) Допустим 1 провалено из-за ошибки в библиотеке в std::shared_ptr, тогда произойти каст в не константную ссылку на указатель не может и перегрузка void push(Data& data) уже выбраться не может. Похоже выбирается вместо неё void push(Data&& data) и она уже утягивает из это временной копии объект. Как эту проблему обойти не смотря на баг в компиляторе? Если он конечно имеет место. А если не имеет, то как тем более обойти эту проблему, если хочется гарантированного утягивания указателя?
Я не пробовал, но наверное если оставить только void push(Data&& data), то бага все равно останется. То есть это не поможет. Как же быть? Бага ли это вообще?
Похожих проблем с тем же std::unique_ptr нет.
krypt 16.12.2013 17:45 # −1
Попробовать явно указать тип? push((Data&)a), или какой вы там тип ожидаете.
roman-kashitsyn 16.12.2013 17:50 # +1
Осталось понять, как работает конструктор shared_ptr<int>(shared_ptr<const int>&&)
roman-kashitsyn 16.12.2013 18:09 # +1
laMer007 16.12.2013 18:35 # 0
laMer007 16.12.2013 18:40 # 0
roman-kashitsyn 16.12.2013 20:03 # +4
Рекомендую воспользоваться блокирующей очередью из C++ Concurrency in Action 2012
laMer007 16.12.2013 20:31 # 0
?
laMer007 16.12.2013 18:54 # −1
blackhearted 16.12.2013 19:55 # 0
а перед этим попроси спецификацией завернуть детородный орган.
laMer007 16.12.2013 20:26 # 0
LispGovno 16.12.2013 20:43 # 0
Ох лол. Юзай std::lock_guard.
>std::queue<Data, std::list<Data>>
Юзай std::deque. А лучше пиши BoundedQueue вместо UnboundedQueue и внутри используй boost::circular_buffer или его аналог, тк он по моему не умеет в push_back от rvalue.
>this->_pushToQueue.notify_all();
Юзай notify_one();