【问题标题】:create a queue with multiple data types创建具有多种数据类型的队列
【发布时间】:2021-08-16 04:37:15
【问题描述】:

我希望能够有一个队列类,它可以接受“任何”首选数据类型,无论是 CURL 类型还是 std::string 类型。如果愿意,可以使用字符串队列或 curl 句柄队列。

#pragma once

#include <iostream>
#include <queue>
#include <string>
#include <mutex>
#include <condition_variable>
#include <chrono>

#include "queue_safe.h"

void SafeQueue::initialize() {

    /* initialize a std::queue */

    safe_queue = {};
    
}

std::string SafeQueue::get() {

    std::unique_lock<std::mutex> condition_lock(queue_lock);

    if (safe_queue.empty()) {

        if (ready.wait_for(condition_lock, std::chrono::seconds(20)) == std::cv_status::timeout) {

            /* timeout was reached, no items left */

            return std::string();
        }

        /* if a timeout was not hit, but the queue was empty previously this will execute */

        std::string element = safe_queue.front();

        safe_queue.pop();

        return element;

        
    }

    else {

        /* not empty, return an element */

        std::string element = safe_queue.front();

        safe_queue.pop();

        return element;
    }

    /* do not need a return value here since empty HAS to be either true/false */

}

void SafeQueue::put(std::string& element) {

    std::lock_guard<std::mutex> put_guard(queue_lock);

    safe_queue.push(element);

    ready.notify_one();

}

uint8_t SafeQueue::empty() {

    std::lock_guard<std::mutex> empty_guard(queue_lock);
    
    if (safe_queue.size() == 0) {

        return 1;
    }

    return 0;

}

队列.cpp

#pragma once

#include <string>
#include <mutex>
#include <queue>
#include <condition_variable>

class SafeQueue {
public:
    std::condition_variable ready;
    std::mutex queue_lock;

    std::queue<std::string> safe_queue;
    uint8_t empty();
    void initialize();
    void put(std::string& element);
    std::string get();
};

队列.h 这适用于单一类型(字符串)。但是我怎样才能调整它以允许上面提到的任何想要的类型呢?

【问题讨论】:

  • 可以使用std::variant作为队列的类型。

标签: c++ queue c++17


【解决方案1】:

如果您事先知道要存储在队列中的类型,可以使用std::variant。作为一个类型安全的联合,std::variant 保证它包含一个类型列表;它还比std::any 具有更好的性能,因为自动存储中存在一个变体。

例如,此变体类型可以存储std::stringintdouble 之一:

using MyVariant = std::variant<std::string, int, double>;

您可以在您的班级SafeQueue 中使用MyVariant 的向量。最后,使用访问者处理队列中的每个项目。

我在这里重做了你的课程:

#include <iostream>
#include <iomanip>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <variant>
#include <vector>

using MyVariant = std::variant<std::string, int, double>;

template<class> inline constexpr bool always_false_v = false;

class SafeQueue {
public:
    std::condition_variable ready;
    std::mutex queue_lock;

    std::queue<MyVariant> safe_queue;
    uint8_t empty();
    void initialize();
    void put(MyVariant element);
    MyVariant get();
};

void SafeQueue::initialize() {

    /* initialize a std::queue */

    safe_queue = {};

}

MyVariant SafeQueue::get() {

    std::unique_lock<std::mutex> condition_lock(queue_lock);

    if (safe_queue.empty()) {

        if (ready.wait_for(condition_lock, std::chrono::seconds(20)) == std::cv_status::timeout) {

            /* timeout was reached, no items left */

            return std::string();
        }

        /* if a timeout was not hit, but the queue was empty previously this will execute */

        auto element = safe_queue.front();

        safe_queue.pop();

        return element;
    }

    else {

        /* not empty, return an element */

        auto element = safe_queue.front();

        safe_queue.pop();

        return element;
    }

    /* do not need a return value here since empty HAS to be either true/false */

}

void SafeQueue::put(MyVariant element) {

    std::lock_guard<std::mutex> put_guard(queue_lock);

    safe_queue.push(std::move(element));

    ready.notify_one();

}

uint8_t SafeQueue::empty() {

    std::lock_guard<std::mutex> empty_guard(queue_lock);

    if (safe_queue.size() == 0) {

        return 1;
    }

    return 0;
}

int main()
{
   SafeQueue q;
    q.put("hello");
    q.put(1);
    q.put(2.3);

    //Use visitor pattern to print elements from queue
    while (!q.empty()) {
        auto elem = q.get();
        std::visit([](auto&& arg) {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, std::string>)
                std::cout << "std::string with value " << std::quoted(arg) << '\n';
            else if constexpr (std::is_same_v<T, int>)
                std::cout << "int with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, double>)
                std::cout << "double with value " << arg << '\n';
            else
                static_assert(always_false_v<T>, "non-exhaustive visitor!");
            }, elem);
    }
}

输出:

std::string with value "hello"
int with value 1
double with value 2.3

Demo

【讨论】:

  • 这不是我要找的,我不需要一个队列/向量中的多种类型
  • @noah 是的。一个变体向量可以在一个队列/向量中保存多种类型。所以在上面的例子中,队列可以容纳ints、std::strings和doubles,而不仅仅是一种类型。
  • 我只需要一个向量中的一种类型而不是混合,所以我只需要一个重载函数我想
  • @noah 那么问题标题具有误导性。它声明了一个包含多种数据类型的队列。如果您只想要一种数据类型,那么您可以使用模板类或简单地使用std::vector。我想既然你在 C++17 下标记了这个,你想在一个队列中存储多种数据类型。
【解决方案2】:

如果你想让一个对象对应一个类型。您必须使用模板类:

template <typename T>
class SafeQueue {
public:
    std::condition_variable ready;
    std::mutex queue_lock;

    std::queue<T> safe_queue;
    uint8_t empty();
    void initialize();
    void put(T& element);
    T get();
};

但是如果你需要一个多层类型的队列,你可以使用std::any:

class SafeQueue {
public:
    std::condition_variable ready;
    std::mutex queue_lock;

    std::queue<std::any> safe_queue;
    uint8_t empty();
    void initialize();
    void put(std::any& element);
    std::any get();
};

但最好有一个结构来存储底层类型,以便与您的类 API 更好地通信。

【讨论】:

  • 模板类是我需要的。
猜你喜欢
  • 2021-12-27
  • 2022-01-10
  • 2017-12-19
  • 1970-01-01
  • 1970-01-01
  • 2014-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多