1. C++ / Говнокод #14232

    +10

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    76. 76
    77. 77
    78. 78
    79. 79
    80. 80
    81. 81
    82. 82
    83. 83
    84. 84
    85. 85
    86. 86
    87. 87
    88. 88
    89. 89
    90. 90
    91. 91
    92. 92
    93. 93
    94. 94
    95. 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);

    http://ideone.com/qdsWJi
    Немного глупый вопрос, почему в 90 строчке не получаем ошибку компиляции если закомментировать 87-ую строку и разкомментировать 88-ую?

    Запостил: laMer007, 16 Декабря 2013

    Комментарии (15) RSS

    • Похоже на багу компилятора?
      Ответить
    • почему должна быть ошибка компиляции?
      Ответить
    • Если смущает move указателя с const int, то /me thinks что конструктор перемещения исключается из overload resolution из-за несовместимости типов указателей, и срабатывает конструктор от константной ссылки, которых хранит неуправляемый указатель.
      http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
      Ответить
      • Тут резолвиться перегрузка push. Один вариант от rvalue, а второй от не константной ссылки на lvalue.

        "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 нет.
        Ответить
        • > Как эту проблему обойти не смотря на баг в компиляторе?
          Попробовать явно указать тип? push((Data&)a), или какой вы там тип ожидаете.
          Ответить
        • void push(Data&&) напрямую не вызывается, все объекты lvalue. Вызывается void push(Data&).
          Осталось понять, как работает конструктор shared_ptr<int>(shared_ptr<const int>&&)
          Ответить
    • template<class Data>
      using UnboundedQueueForSharedPtr = UnboundedQueueForNonThrowMovable<std::shared_ptr<const Data>>;
      Очередь с константными указателями, всё норм.
      Ответить
      • Спасибо. Я обыскался. Похоже оно. :) Интересно, как исправить?
        Ответить
        • Неужели запретить вариант очереди с std::shared_ptr единственный выход?
          Ответить
          • Вообще говоря, push, который берёт обычную ссылку и делает на неё move - это п*****ц. Если клиент хочет отдать владение, пусть явно сделает move на своём lvalue.
            Рекомендую воспользоваться блокирующей очередью из C++ Concurrency in Action 2012
            Ответить
            • http://stackoverflow.com/questions/17984552/fine-grained-locking-queue-in-c
              ?
              Ответить
    • Вот коллега меня просит, чтобы во время вызова ~UnboundedQueueForNonThrowMovable заблокированные в клинче потоки на _lockQueue или _pushToQueue оба метода pop кидали исключения для прерывания ожидания. Удовлетворить эту просьбу? Насколько это по вашему адекватно? Есть подводные камни?
      Ответить
      • удовлетвори его.
        а перед этим попроси спецификацией завернуть детородный орган.
        Ответить
    • > std::unique_lock
      Ох лол. Юзай 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();
      Ответить

    Добавить комментарий