创建具有多种数据类型的队列

create a queue with multiple data types

我希望能够有一个队列 class 可以接受“任何”首选数据类型,无论是 CURL 类型还是 std::string 类型。如果需要,字符串队列或卷曲句柄队列。

#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;

}

queue.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();
};

queue.h 这适用于单一类型(字符串)。但是我怎样才能使它适应上面提到的任何想要的类型呢?

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

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();
};

但是最好有一个结构来存储基础类型,以便更好地与您的 class API 进行通信。

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

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

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

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

我在此处对您进行了修改 class:

#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