Я хочу реализовать класс Queue
, который будет обрабатывать только подвижные объекты:
class Object {
public:
Object(Object&& other);
Object(const Object&) = delete;
private:
Object() = default;
};
И у меня есть проблема проектирования Pop
метод атомным способом. Он должен атомарно (1) вытолкнуть объект из очереди и (2) каким-то образом указать результат операции. Результат необходим в случае, если очередь закрыта и пуста — для предотвращения зависания. Если очередь пуста, но не закрыта — вызов Pop
должен заблокировать.
Мои подходы.
Если метод принимает ссылку: bool Pop(Object& obj);
— тогда у кого-то уже должен быть неиспользованный экземпляр Object
и это не возможно все время.
Если метод возвращает временное значение: Object Pop(bool& result);
— тогда я не знаю, что вернуть в случае «закрытого и пустого».
Замечания: не разрешается заводить друзей Object
с Queue
,
У кого-нибудь есть предложения о том, как правильно спроектировать поп-операцию?
Как я вижу, у вас есть несколько вариантов, когда кто-то звонит pop
и очередь пуста.
Один на самом деле блокирует. Это, конечно, работает только тогда, когда у вас есть надежда, что кто-то еще поместит что-то в очередь, пока вы заблокированы и ожидаете (то есть многопоточный код). Как вы уже упоминали, существуют также проблемы, когда очередь закрывается.
Другой бросает исключение, когда нечего хлопать. Однако некоторые люди имеют законные (а иногда и необоснованные) опасения по поводу использования исключений в C ++.
Другой метод, который я люблю и рекомендую (если вы не хотите бросать), это возврат optional<T>
объект, то есть обертка для T
тот Можно быть пустым
Предполагалось, что он будет частью C ++ 14, но, похоже, он был добавлен в собственную техническую спецификацию, что означает, что я не знаю, когда он будет стандартизирован. Однако давайте забудем этот факт и посмотрим, как его можно использовать:
#include <optional>
// I've omitted proper "queue" class stuff for brevity,
// and assumed the underlying queue has an STL-like interface
std::optional<Object> pop ()
{
std::optional<Object> ret;
if (!internal_queue.empty())
{
ret.emplace (std::move(internal_queue.front()));
internal_queue.pop_front();
}
return ret;
}
Есть конечно блокировка и другие детали, которые опущены. Вы бы использовали выше, как это:
auto x = q.pop ();
if (!x)
LOG ("Failed to pop an item.");
else
USE (*x); // *x is an Object
Поскольку ваши объекты поддерживают перемещение, это будет довольно эффективной реализацией. Кроме того, я бы предложил назвать метод выше try_pop
или что-то подобное, чтобы лучше указать, что он на самом деле делает.
поскольку optional
пока не является частью языка C ++, вы можете увидеть, реализован ли уже ваш компилятор (это не сложный кусок кода!) Или вы можете использовать Boost.Optional. Или вы можете реализовать это самостоятельно, или использовать другие реализации, которые должны быть доступны где-нибудь в Интернете.
В общем, если бы это был мой код, я бы написал pop
метод, который блокирует при необходимости и возвращает Object
и генерирует только когда очередь закрывается (то есть больше не будет поступающих объектов). И я бы также реализовал try_pop
который возвращает optional<Object>
когда вы не хотите, чтобы ваш код блокировал или имел дело с исключениями.
Просто верните Object
в нормальном случае и выдать исключение, если очередь остановлена и пуста, когда pop()
называется (или pop()
активен, когда очередь останавливается).
Ответ Дитмара Кюля (Object pop()
который выдает исключение) кажется мне хорошим выбором, но другой подход, который, в зависимости от того, как вы собираетесь его использовать, может быть полезным, это иметь pop()
вызовите какую-нибудь другую функцию, если что-нибудь всплывет. Как упоминает YZT, это не просто всплывающее окно, поэтому, возможно, не следует называть pop
,
void pop_apply(std::function<void(Object)>);
Назовите это как
queue.pop_apply([](Object obj)
{
// do something with obj here
});
Это может быть потенциально улучшено путем возвращения bool
для удобства вызывающего абонента, чтобы узнать, если что-то всплыло, или путем pop_apply
шаблонная функция, которая позволяет передавать все вызываемые объекты без переноса в std::function<>
,