Skip to content

Instantly share code, notes, and snippets.

@BrianOfrim
Last active May 19, 2023 03:09
Show Gist options
  • Save BrianOfrim/55c6c49a773361d0458d7f8d2cddd858 to your computer and use it in GitHub Desktop.
Save BrianOfrim/55c6c49a773361d0458d7f8d2cddd858 to your computer and use it in GitHub Desktop.
Brian's Template Library

Brian's Template Library

containerPayload.h

Create a container of payloads where payloads are themselves containers of a certain size

removeFromStart.h

Remove all leading occurences of a specified value from a container
https://godbolt.org/z/3of84rKjd

fixedVector.h

std::vector like containter with a static size
https://godbolt.org/z/aGneaf1qc

tempfile.h

file that only exists in a scope https://godbolt.org/z/zYqY6eoT6

thread_pool.h

generic threadpool https://godbolt.org/z/r3aqfGovx

extension with notify on all task completion instead of waiting on futures https://godbolt.org/z/ozG5vT6hj

added a threadsafe output queue to get output values on the calling thread via queue instead of future https://godbolt.org/z/Eheb4be47

same as previous but w/ simpler thread pool https://godbolt.org/z/e1Pa6sKxq

added cancelation to thead pool https://godbolt.org/z/xcjqsjcaK

#include <fmt/core.h>
#include <fmt/ranges.h>
#include <vector>
#include <algorithm>
#include <numeric>
#include <type_traits>
template<typename T>
concept integral_type = std::is_integral_v<T>;
auto ceilDiv(const integral_type auto numerator, const integral_type auto denominator){
return (numerator + denominator - 1) / denominator;
}
template <typename T, template <typename, typename...> class C>
auto makePayloads(const C<T> & data, const size_t maxSize){
const auto numPayloads = ceilDiv(data.size(), maxSize);
auto copyFrom = std::begin(data);
C<C<T>> payloads;
payloads.reserve(numPayloads);
for(size_t i = 0; i < numPayloads; i++){
const auto payloadSize = std::min<size_t>(maxSize, std::distance(copyFrom, std::end(data)));
payloads.emplace(std::end(payloads), copyFrom, copyFrom + payloadSize);
std::advance(copyFrom, payloadSize);
}
return payloads;
}
int main() {
std::vector<int> nums(30);
std::iota(std::begin(nums), std::end(nums), 1);
fmt::print("data: {}\n", fmt::join(nums, ", "));
auto payloadedData = makePayloads(nums , 5);
for(int i = 0; i < payloadedData.size(); i++){
fmt::print("payload: {} = {}\n",i , fmt::join(payloadedData[i], ", "));
}
}
#include <fmt/core.h>
#include "catch2/catch_test_macros.hpp"
#include <algorithm>
#include <array>
#include <cstdlib>
#include <exception>
#include <iterator>
#include <stdexcept>
#include <string.h>
template <typename T, size_t N>
class fixed_vector {
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
public:
constexpr fixed_vector() noexcept {};
constexpr fixed_vector(size_type count, const_reference init_val)
: m_size(count) {
if (m_size > N) {
throw std::out_of_range("count exceeds capacity");
}
std::fill(std::begin(m_data), std::begin(m_data) + count, init_val);
}
template <std::input_iterator Iter>
constexpr fixed_vector(Iter first, Iter last)
: m_size(std::distance(first, last)) {
if (m_size > N) {
throw std::out_of_range(
"std::distance(first,last) exceeds capacity");
}
std::copy(first, last, std::begin(m_data));
}
constexpr fixed_vector(std::initializer_list<value_type> init_list)
: m_size(init_list.size()) {
if (init_list.size() > N) {
throw std::out_of_range(
"size of initializer_list exceeds capacity");
}
std::copy(std::begin(init_list), std::end(init_list),
std::begin(m_data));
}
// emement access
constexpr reference at(size_type pos) {
if (pos >= m_size) {
throw std::out_of_range("pos out of range");
} else {
return m_data.at(pos);
}
}
constexpr const_reference at(size_type pos) const {
if (pos >= m_size) {
throw std::out_of_range("pos out of range");
} else {
return m_data.at(pos);
}
}
constexpr reference operator[](size_type pos) { return m_data[pos]; }
constexpr const_reference operator[](size_type pos) const {
return m_data[pos];
}
constexpr reference front() { return m_data.front(); }
constexpr const_reference front() const { return m_data.front(); }
constexpr reference back() { return m_data[m_size - 1]; }
constexpr const_reference back() const { return m_data[m_size - 1]; }
constexpr T* data() noexcept { return m_data.data(); }
constexpr const T* data() const noexcept { return m_data.data(); }
// iterators
constexpr iterator begin() noexcept { return &(*m_data.begin()); }
constexpr const_iterator begin() const noexcept { return &(*m_data.begin()); }
constexpr const_iterator cbegin() const noexcept { return &(*m_data.cbegin()); }
constexpr iterator end() noexcept { return &(*(m_data.begin())) + m_size; }
constexpr const_iterator end() const noexcept { return &(*(m_data.begin())) + m_size; }
constexpr const_iterator cend() const noexcept { return &(*(m_data.begin())) + m_size; }
constexpr iterator rbegin() noexcept { return &(*(m_data.begin())) + m_size; }
constexpr const_iterator rbegin() const noexcept { return &(*(m_data.begin())) + m_size; }
constexpr const_iterator crbegin() const noexcept { return &(*(m_data.begin())) + m_size; }
constexpr iterator rend() noexcept { return &(*m_data.begin()); }
constexpr const_iterator rend() const noexcept { return &(*m_data.begin()); }
constexpr const_iterator crend() const noexcept { return &(*m_data.begin()); }
// capacity
[[nodiscard]] constexpr bool empty() const noexcept { return m_size == 0; }
[[nodiscard]] constexpr bool full() const noexcept { return m_size == N; }
constexpr size_type size() const noexcept { return m_size; }
constexpr size_type max_size() const noexcept { return N; }
constexpr size_type capacity() const noexcept { return N; }
// modifiers
constexpr void clear() noexcept {
// if (!std::is_trivially_destructible<value_type>::value) {
// std::for_each(begin(), end(), [](reference r) { r.~value_type(); });
// }
m_size = 0;
}
constexpr void push_back(const T& value) {
if (full()) {
throw std::out_of_range("can't push_back already full");
}
m_size++;
at(m_size - 1) = value;
}
constexpr void push_back(T&& value) {
if (full()) {
throw std::out_of_range("can't push_back already full");
}
m_size++;
at(m_size - 1) = std::move(value);
}
template <class... Args>
constexpr reference emplace_back(Args&&... args) {
if (full()) {
throw std::out_of_range("can't emplace_back already full");
}
m_size++;
reference ref = at(m_size - 1);
ref = value_type(std::forward<Args>(args)...);
return ref;
}
constexpr void pop_back() {
if (empty()) {
throw std::out_of_range("can't pop_back already empty");
}
m_size--;
}
constexpr void swap(fixed_vector& other) {
std::swap(m_data, other.m_data);
std::swap(m_size, other.m_size);
}
constexpr void resize( size_type count, const value_type& value ){
if(count > N){
throw std::out_of_range("count exceeds capacity");
}else if(m_size > count){
while(m_size > count){
pop_back();
}
}else if(m_size < count){
while(m_size < count){
push_back(value);
}
}
// else count is equal to m_size, do nothing
}
constexpr void resize( size_type count ){
if(count > N){
throw std::out_of_range("count exceeds capacity");
}else if(m_size > count){
while(m_size > count){
pop_back();
}
}else{
m_size = count;
}
}
private:
std::array<T, N> m_data;
size_t m_size{0};
};
namespace FixedVectorUtils {
template <typename T, size_t N>
constexpr std::vector<T> fixed_vector_to_vector(const fixed_vector<T, N>& fv) {
return {std::begin(fv), std::end(fv)};
}
template <typename T, size_t N>
constexpr fixed_vector<T, N> vector_to_fixed_vector(const std::vector<T>& v) {
return fixed_vector<T, N>(std::begin(v),
std::begin(v) + std::min(N, v.size()));
}
template <typename T, size_t N>
constexpr size_t copy_vector_to_fixed_vector(const std::vector<T>& src,
fixed_vector<T, N>& dest) {
const size_t numToCopy = std::min(N, src.size());
dest.resize(numToCopy);
std::copy(std::begin(src), std::end(src), std::begin(dest));
return numToCopy;
}
template<size_t N, size_t CStrLen>
constexpr size_t copy_string_vector_to_cstr_fixed_vector(const std::vector<std::string>& src,
fixed_vector<char[CStrLen], N>& dest){
const size_t numToCopy = std::min(N, src.size());
dest.resize(numToCopy);
for(auto i = 0; i < numToCopy; ++i){
strncpy(dest[i], src[i].c_str(), CStrLen);
}
return numToCopy;
}
} // namespace FixedVectorUtils
TEST_CASE("fixed_vector()", "fixed_vector") {
fixed_vector<int, 10> fv;
REQUIRE(fv.size() == 0);
REQUIRE(fv.empty());
REQUIRE(fv.max_size() == 10);
REQUIRE(fv.capacity() == 10);
}
TEST_CASE("fixed_vector(size_type count, const_reference init_val) 1",
"fixed_vector") {
fixed_vector<int, 10> fv(5, 33);
REQUIRE(fv.size() == 5);
REQUIRE(fv.max_size() == 10);
REQUIRE(fv.capacity() == 10);
REQUIRE(std::all_of(std::begin(fv), std::end(fv),
[](const auto& v) { return v == 33; }));
REQUIRE_THROWS(fv.at(5));
}
TEST_CASE("fixed_vector(size_type count, const_reference init_val) 2",
"fixed_vector") {
REQUIRE_THROWS(fixed_vector<int, 10>(11, 33));
}
TEST_CASE("fixed_vector(std::initializer_list<value_type> init_list) 1",
"fixed_vector") {
fixed_vector<int, 10> fv({1, 2, 3});
REQUIRE(fv.size() == 3);
REQUIRE(fv.max_size() == 10);
REQUIRE(fv.capacity() == 10);
REQUIRE(fv.at(0) == 1);
REQUIRE(fv.at(1) == 2);
REQUIRE(fv.at(2) == 3);
}
TEST_CASE("fixed_vector(std::initializer_list<value_type> init_list) 2",
"fixed_vector") {
fixed_vector<int, 10> fv{10, 20, 30};
REQUIRE(fv.size() == 3);
REQUIRE(fv.max_size() == 10);
REQUIRE(fv.capacity() == 10);
REQUIRE(fv.at(0) == 10);
REQUIRE(fv.at(1) == 20);
REQUIRE(fv.at(2) == 30);
}
TEST_CASE("fixed_vector(Iter first, Iter last) 1", "fixed_vector") {
std::vector vec{1, 2, 3, 4};
fixed_vector<int, 4> fv(vec.begin(), vec.end());
REQUIRE(fv.size() == 4);
REQUIRE(fv.max_size() == 4);
REQUIRE(fv.full());
REQUIRE(fv[0] == 1);
REQUIRE(fv[1] == 2);
REQUIRE(fv[2] == 3);
REQUIRE(fv[3] == 4);
}
TEST_CASE("fixed_vector(Iter first, Iter last) 2", "fixed_vector") {
std::vector vec{1, 2, 3, 4, 5};
REQUIRE_THROWS(fixed_vector<int, 4>(vec.begin(), vec.end()));
}
TEST_CASE("sizeof test 1", "fixed_vector") {
fixed_vector<int, 10> fv;
REQUIRE(sizeof(fv) == (10 * sizeof(int) + sizeof(size_t)));
}
TEST_CASE("sizeof test 2", "fixed_vector") {
fixed_vector<int, 10> fv;
std::array<int, 10> arr;
REQUIRE(sizeof(fv) == (sizeof(arr) + sizeof(size_t)));
}
TEST_CASE("test at throw", "fixed_vector") {
fixed_vector<int, 10> fv({1, 2, 3});
REQUIRE_THROWS(fv.at(4));
}
TEST_CASE("test front/last", "fixed_vector") {
fixed_vector<int, 10> fv({1, 2, 3});
REQUIRE(fv.front() == 1);
REQUIRE(fv.back() == 3);
}
TEST_CASE("test data", "fixed_vector") {
fixed_vector<int, 10> fv({1, 2, 3});
int* rawData = fv.data();
REQUIRE(rawData[0] == 1);
REQUIRE(rawData[2] == 3);
}
TEST_CASE("const test data", "fixed_vector") {
fixed_vector<int, 10> fv({4, 5, 6});
const int* rawData = fv.data();
REQUIRE(rawData[0] == 4);
REQUIRE(rawData[2] == 6);
}
TEST_CASE("clear", "fixed_vector") {
fixed_vector<int, 10> fv({4, 5, 6});
REQUIRE(fv.size() == 3);
REQUIRE(fv.max_size() == 10);
fv.clear();
REQUIRE(fv.size() == 0);
REQUIRE(fv.max_size() == 10);
}
TEST_CASE("begin/end", "fixed_vector") {
fixed_vector<int, 10> fv({4, 5, 6});
auto startIt = fv.begin();
auto endIt = fv.end();
REQUIRE(*startIt == 4);
REQUIRE(*std::next(startIt) == 5);
REQUIRE(*std::prev(endIt) == 6);
}
TEST_CASE("cbegin/cend", "fixed_vector") {
fixed_vector<int, 10> fv({4, 5, 6});
auto startIt = fv.cbegin();
auto endIt = fv.cend();
REQUIRE(*startIt == 4);
REQUIRE(*std::next(startIt) == 5);
REQUIRE(*std::prev(endIt) == 6);
}
TEST_CASE("const begin/end", "fixed_vector") {
const fixed_vector<int, 10> fv({4, 5, 6});
const auto startIt = fv.begin();
const auto endIt = fv.end();
REQUIRE(*startIt == 4);
REQUIRE(*std::next(startIt) == 5);
REQUIRE(*std::prev(endIt) == 6);
}
TEST_CASE("push_back element 1", "fixed_vector") {
fixed_vector<int, 10> fv;
fv.push_back(33);
REQUIRE(fv.back() == 33);
REQUIRE(fv.size() == 1);
fv.push_back(66);
REQUIRE(fv.back() == 66);
REQUIRE(fv.size() == 2);
fv.push_back(99);
REQUIRE(fv.back() == 99);
REQUIRE(fv.size() == 3);
}
TEST_CASE("push_back element 2", "fixed_vector") {
fixed_vector<int, 2> fv;
fv.push_back(33);
fv.push_back(66);
REQUIRE_THROWS(fv.push_back(99));
}
TEST_CASE("emplace_back element", "fixed_vector") {
fixed_vector<int, 10> fv;
const int n0 = 33;
REQUIRE(fv.emplace_back(n0) == n0);
REQUIRE(fv.back() == n0);
REQUIRE(fv.size() == 1);
const int n1 = 66;
REQUIRE(fv.emplace_back(n1) == n1);
REQUIRE(fv.back() == n1);
REQUIRE(fv.size() == 2);
const int n2 = 99;
REQUIRE(fv.emplace_back(n2) == n2);
REQUIRE(fv.back() == n2);
REQUIRE(fv.size() == 3);
}
TEST_CASE("FixedVectorUtils::fixed_vector_to_vector", "fixed_vector") {
const fixed_vector<int, 10> fv({4, 5, 6});
std::vector<int> v = FixedVectorUtils::fixed_vector_to_vector(fv);
REQUIRE(v.size() == 3);
REQUIRE(v.at(0) == 4);
REQUIRE(v.at(1) == 5);
REQUIRE(v.at(2) == 6);
}
TEST_CASE("FixedVectorUtils::vector_to_fixedvector", "fixed_vector") {
std::vector<int> v{4, 5, 6};
auto fv = FixedVectorUtils::vector_to_fixed_vector<int, 10>(v);
REQUIRE(fv.size() == 3);
REQUIRE(fv.capacity() == 10);
REQUIRE(fv.at(0) == 4);
REQUIRE(fv.at(1) == 5);
REQUIRE(fv.at(2) == 6);
}
TEST_CASE("FixedVectorUtils::copy_vector_to_fixed_vector", "fixed_vector") {
const std::vector<int> v{4, 5, 6};
fixed_vector<int, 10> fv{10, 11};
FixedVectorUtils::copy_vector_to_fixed_vector(v, fv);
REQUIRE(fv.size() == 3);
REQUIRE(fv.capacity() == 10);
REQUIRE(fv.at(0) == 4);
REQUIRE(fv.at(1) == 5);
REQUIRE(fv.at(2) == 6);
}
TEST_CASE("FixedVectorUtils::copy_string_vector_to_cstr_fixed_vector", "fixed_vector") {
const std::vector<std::string> v{"Hello", "world", "string"};
fixed_vector<char[10], 10> fv;
FixedVectorUtils::copy_string_vector_to_cstr_fixed_vector(v, fv);
REQUIRE(fv.size() == 3);
REQUIRE(fv.capacity() == 10);
REQUIRE(strcmp(fv.at(0), "Hello") == 0);
REQUIRE(strcmp(fv.at(1), "world") == 0);
REQUIRE(strcmp(fv.at(2), "string") == 0);
}
template <typename T>
using fixed_vec_of_five = fixed_vector< T, 5>;
TEST_CASE("Derived Type with fixed length", "fixed_vector"){
fixed_vec_of_five<int> fv5{1,2,3,4,5};
REQUIRE(fv5.capacity() == 5);
REQUIRE(fv5.size() == 5);
const std::vector<int> vec = FixedVectorUtils::fixed_vector_to_vector(fv5);
REQUIRE(vec.size() == 5);
}
template <typename T, size_t size>
void appendArrayToVector(std::vector<T> &vec, const std::array<T, size> & arr, const size_t toAppend = size){
vec.insert(std::end(vec), std::cbegin(arr), std::cbegin(arr) + std::min(toAppend, size));
}
#include "catch2/catch_test_macros.hpp"
#include <algorithm>
#include <string>
template <typename Container, typename Val>
Container removeFromStart(const Container &cont, const Val &val) {
const auto firstAfter = std::find_if(
std::begin(cont), std::end(cont),
[&val](const auto &v) { return v != val; });
return {firstAfter, std::end(cont)};
}
TEST_CASE("no dot", "removeFromStart"){
REQUIRE(removeFromStart(std::string("txt"), '.') == "txt");
}
TEST_CASE("one dot", "removeFromStart"){
REQUIRE(removeFromStart(std::string(".txt"), '.') == "txt");
}
TEST_CASE("three dot", "removeFromStart"){
REQUIRE(removeFromStart(std::string("...txt"), '.') == "txt");
}
TEST_CASE("all dot", "removeFromStart"){
REQUIRE(removeFromStart(std::string("............"), '.').empty());
}
TEST_CASE("no 7", "removeFromStart"){
REQUIRE(removeFromStart(std::vector<int>({1,2,3}), 7) == std::vector<int>({1,2,3}));
}
TEST_CASE("one 7", "removeFromStart"){
REQUIRE(removeFromStart(std::vector<int>({7,1,2,3}), 7) == std::vector<int>({1,2,3}));
}
TEST_CASE("three 7s", "removeFromStart"){
REQUIRE(removeFromStart(std::vector<int>({7,7,7,1,2,3}), 7) == std::vector<int>({1,2,3}));
}
TEST_CASE("all 7s", "removeFromStart"){
REQUIRE(removeFromStart(std::vector<int>({7,7,7,7}), 7).empty());
}
#include "catch2/catch_test_macros.hpp"
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <string>
#include <fmt/core.h>
// file only exists in a scope
class TempFile {
public:
TempFile(const std::string& fileExt, const std::string& content) {
std::string tmpFile = std::tmpnam(nullptr);
tmpFile += ".";
tmpFile += fileExt;
m_filePath = tmpFile;
std::ofstream out(m_filePath);
out << content;
out.close();
}
~TempFile() { std::filesystem::remove(m_filePath); }
std::string content() {
std::ifstream in(m_filePath);
std::stringstream buffer;
buffer << in.rdbuf();
return buffer.str();
}
std::filesystem::path path() const{
return m_filePath;
}
private:
std::filesystem::path m_filePath;
};
TEST_CASE("TempFile()", "TempFile") {
std::filesystem::path tmpPath;
{
std::string content = "Hello World";
TempFile tmpFile("txt", "Hello World");
tmpPath = tmpFile.path();
fmt::print("Filename: {}, content: {}\n", tmpPath.string(), tmpFile.content());
REQUIRE(std::filesystem::exists(tmpPath));
REQUIRE(content == tmpFile.content());
}
REQUIRE(!std::filesystem::exists(tmpPath));
}
#pragma once
// based on https://github.com/anthonywilliams/ccia_code_samples
// compile with -std=c++20 -pthread
#include "threadsafequeue.h"
#include <atomic>
#include <functional>
#include <future>
#include <thread>
class function_wrapper {
struct impl_base {
virtual void call() = 0;
virtual ~impl_base() {}
};
std::unique_ptr<impl_base> impl;
template <typename F> struct impl_type : impl_base {
F f;
impl_type(F &&f_) : f(std::move(f_)) {}
void call() { f(); }
};
public:
template <typename F> function_wrapper(F &&f) : impl(new impl_type<F>(std::move(f))) {}
void operator()() { impl->call(); }
function_wrapper(function_wrapper &&other) : impl(std::move(other.impl)) {}
function_wrapper &operator=(function_wrapper &&other) {
impl = std::move(other.impl);
return *this;
}
function_wrapper() = default;
function_wrapper(const function_wrapper &) = delete;
function_wrapper(function_wrapper &) = delete;
function_wrapper &operator=(const function_wrapper &) = delete;
};
class thread_pool {
threadsafe_queue<function_wrapper> work_queue;
std::vector<std::jthread> threads;
void worker_thread(std::stop_token stoken) {
while (!stoken.stop_requested()) {
function_wrapper task;
if (work_queue.try_pop(task)) {
task();
} else {
std::this_thread::yield();
}
}
}
public:
thread_pool() {
unsigned const thread_count = std::thread::hardware_concurrency();
for (unsigned i = 0; i < thread_count; ++i) {
threads.push_back(std::jthread(std::bind_front(&thread_pool::worker_thread, this)));
}
}
template <typename FunctionType>
std::future<typename std::result_of<FunctionType()>::type> submit(FunctionType f) {
typedef typename std::result_of<FunctionType()>::type result_type;
std::packaged_task<result_type()> task(std::move(f));
std::future<result_type> res(task.get_future());
work_queue.push(std::move(task));
return res;
}
};
// based off of https://www.youtube.com/watch?v=iUKxvEg0zdk
struct event_handler {
private:
thread_pool &m_tp;
std::atomic<uint32_t> m_pending_tasks{0};
public:
event_handler(thread_pool &tp) : m_tp(tp) {}
template <typename FunctionType> void handle_event(FunctionType f) {
++m_pending_tasks;
m_tp.submit([this, f]() {
f();
if (!--m_pending_tasks) {
m_pending_tasks.notify_all();
}
});
}
void wait_for_tasks() {
while (auto count = m_pending_tasks.load()) {
m_pending_tasks.wait(count);
}
}
};
int main() {
using namespace std::chrono_literals;
thread_pool pool;
event_handler ev{pool};
threadsafe_queue<int> out_queue;
for(int i = 0; i < 10; i++){
ev.handle_event([i, &out_queue]{
std::cout << "started task: " << i << '\n';
std::this_thread::sleep_for(0.5s);
std::cout << "ended task: " << i << '\n';
out_queue.push(i * 100);
});
}
ev.wait_for_tasks();
int outval = 0;
while(out_queue.try_pop(outval)){
std::cout << "Out val: " << outval << '\n';
}
}
#pragma once
// based on https://github.com/anthonywilliams/ccia_code_samples
// compile with -std=c++20 -pthread
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
template <typename T> class threadsafe_queue {
private:
mutable std::mutex mut;
std::queue<T> data_queue;
std::condition_variable_any data_cond;
public:
void push(T new_value) {
std::lock_guard<std::mutex> lk(mut);
data_queue.push(std::move(new_value));
data_cond.notify_one();
}
template <class... EmplaceArgs> void emplace(EmplaceArgs &&... args) {
std::lock_guard<std::mutex> lk(mut);
data_queue.emplace(std::forward<EmplaceArgs>(args)...);
data_cond.notify_one();
}
void wait_and_pop(T &value) {
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk, [this] { return !data_queue.empty(); });
value = std::move(data_queue.front());
data_queue.pop();
}
std::shared_ptr<T> wait_and_pop() {
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk, [this] { return !data_queue.empty(); });
std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
data_queue.pop();
return res;
}
bool wait_and_pop(T &value, std::stop_token stoken) {
std::unique_lock<std::mutex> lk(mut);
bool dataAvailable = data_cond.wait(lk, stoken, [this] { return !data_queue.empty(); });
if (dataAvailable) {
value = std::move(data_queue.front());
data_queue.pop();
return true;
} else {
return false;
}
} // based on https://github.com/anthonywilliams/ccia_code_samples
// compile with -std=c++20 -pthread
std::shared_ptr<T> wait_and_pop(std::stop_token stoken) {
std::unique_lock<std::mutex> lk(mut);
bool dataAvailable = data_cond.wait(lk, stoken, [this] { return !data_queue.empty(); });
if (dataAvailable) {
std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
data_queue.pop();
return res;
} else {
return {};
}
}
bool try_pop(T &value) {
std::lock_guard<std::mutex> lk(mut);
if (data_queue.empty())
return false;
value = std::move(data_queue.front());
data_queue.pop();
return true;
}
template <typename C>
// pop up to n into out container, return remaining count
size_t pop_n(C &outContianer, size_t n) {
std::lock_guard<std::mutex> lk(mut);
size_t numPopped = 0;
while (numPopped < n && !data_queue.empty()) {
outContianer.push_back(std::move(data_queue.front()));
data_queue.pop();
}
return data_queue.size();
}
std::shared_ptr<T> try_pop() {
std::lock_guard<std::mutex> lk(mut);
if (data_queue.empty())
return std::shared_ptr<T>();
std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
data_queue.pop();
return res;
}
void clear() {
std::lock_guard<std::mutex> lk(mut);
data_queue = {};
}
bool empty() const {
std::lock_guard<std::mutex> lk(mut);
return data_queue.empty();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment