From a29d971692a7976b6a493ccfd661430887991c25 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 16:26:20 +0300 Subject: [PATCH 01/30] Write full message --- examples/multithreading.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/multithreading.cpp b/examples/multithreading.cpp index 3a061d5..49a6593 100644 --- a/examples/multithreading.cpp +++ b/examples/multithreading.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -15,7 +16,9 @@ int main() // Read const auto out = [](msd::channel& ch, std::size_t i) { for (auto number : ch) { - std::cout << number << " from thread: " << i << '\n'; + std::stringstream stream; + stream << number << " from thread: " << i << '\n'; + std::cout << stream.str(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } }; From ac62504283c0fc2da84c4c32f6bd9305eaeb723d Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 16:43:10 +0300 Subject: [PATCH 02/30] Move values --- include/msd/blocking_iterator.hpp | 7 ++-- tests/channel_test.cpp | 68 ++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index 4ce6d7d..bc9ec40 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -61,7 +61,7 @@ class blocking_iterator { * * @return The iterator itself. */ - blocking_iterator operator++() noexcept + blocking_iterator& operator++() noexcept { if (!chan_->read(value_)) { is_end_ = true; @@ -141,9 +141,10 @@ class blocking_writer_iterator { * * @return The iterator itself. */ - blocking_writer_iterator& operator=(const value_type& val) + template + blocking_writer_iterator& operator=(T&& val) { - chan_->write(val); + chan_->write(std::forward(val)); return *this; } diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index d976dd2..42acc71 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -323,24 +323,69 @@ TEST(ChannelTest, ReadWriteClose) EXPECT_EQ(nums, numbers); } -TEST(ChannelTest, MergeChannels) +class MovableOnly { + public: + explicit MovableOnly(int v) : value(v) {} + + MovableOnly() = default; + + MovableOnly(const MovableOnly&) + { + std::cout << "Copy constructor should not be called for MovableOnly"; + std::abort(); + } + + MovableOnly(MovableOnly&& other) noexcept : value{std::move(other.value)} { other.value = 0; } + + MovableOnly& operator=(const MovableOnly&) + { + std::cout << "Copy assignment should not be called for MovableOnly"; + std::abort(); + } + + MovableOnly& operator=(MovableOnly&& other) noexcept + { + if (this != &other) { + value = other.value; + other.value = 0; + } + + return *this; + } + + int getValue() const { return value; } + + private: + int value{0}; +}; + +TEST(ChannelTest, Transform) { const int numbers = 100; const std::int64_t expected_sum = 5050 * 2; std::atomic sum{0}; std::atomic nums{0}; - msd::channel input_chan{30}; + msd::channel input_chan{30}; msd::channel output_chan{10}; - // Send to channel + // Send to channel input channel const auto writer = [&input_chan]() { for (int i = 1; i <= numbers; ++i) { - input_chan.write(i); + input_chan.write(MovableOnly{i}); } input_chan.close(); }; + // Transform input channel values from MovableOnly to int + // by multiplying by 2 and write to output channel + const auto double_transformer = [&input_chan, &output_chan]() { + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), + [](auto&& value) { return value.getValue() * 2; }); + output_chan.close(); + }; + + // Read from output channel const auto reader = [&output_chan, &sum, &nums]() { for (const auto out : output_chan) { // blocking until channel is drained (closed and empty) sum += out; @@ -348,21 +393,16 @@ TEST(ChannelTest, MergeChannels) } }; - const auto double_transformer = [&input_chan, &output_chan]() { - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](int value) { return value * 2; }); - output_chan.close(); - }; - - const auto reader_1 = std::async(std::launch::async, reader); - const auto reader_2 = std::async(std::launch::async, reader); + // Create async tasks for reading, transforming, and writing + const auto reader_task_1 = std::async(std::launch::async, reader); + const auto reader_task_2 = std::async(std::launch::async, reader); const auto writer_task = std::async(std::launch::async, writer); const auto transformer_task = std::async(std::launch::async, double_transformer); - reader_1.wait(); - reader_2.wait(); writer_task.wait(); transformer_task.wait(); + reader_task_1.wait(); + reader_task_2.wait(); EXPECT_EQ(sum, expected_sum); EXPECT_EQ(nums, numbers); From fedd2922fe71c10f476f2d5a9a948d193ba72ee7 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:13:14 +0300 Subject: [PATCH 03/30] Set version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c8fbbab..65d5d7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12) project(cpp_channel) -set(PROJECT_VERSION 1.2.0) +set(PROJECT_VERSION 1.2.1) set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard") set(CMAKE_CXX_STANDARD_REQUIRED YES) From c9aa39e7142af5b6c71f183be70b92f6c6f16afb Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:13:20 +0300 Subject: [PATCH 04/30] MSVC --- include/msd/blocking_iterator.hpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index bc9ec40..780aa10 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -110,7 +110,7 @@ class blocking_writer_iterator { /** * @brief Constant reference to the type of the elements stored in the channel. */ - using reference = const value_type&; + using reference = value_type&; /** * @brief Supporting writing of elements. @@ -125,7 +125,7 @@ class blocking_writer_iterator { /** * @brief Pointer type to the value_type. */ - using pointer = const value_type*; + using pointer = value_type*; /** * @brief Constructs a blocking iterator from a channel reference. @@ -140,9 +140,14 @@ class blocking_writer_iterator { * @param val The value to be written into the channel. * * @return The iterator itself. + * + * @note This function is enabled for all types except `blocking_writer_iterator` itself. Reason: either I am awful + * at C++ or MSVC has a bug. It works fine with GCC and Clang, but MSVC fails to compile. */ template - blocking_writer_iterator& operator=(T&& val) + typename std::enable_if::type, blocking_writer_iterator>::value, + blocking_writer_iterator&>::type + operator=(T&& val) { chan_->write(std::forward(val)); return *this; From a6990b501ffa0578acdf7c305fb4058f4f5637f2 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:21:45 +0300 Subject: [PATCH 05/30] Use size of internal queue --- include/msd/channel.hpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/include/msd/channel.hpp b/include/msd/channel.hpp index 9ffd26a..ca3294f 100644 --- a/include/msd/channel.hpp +++ b/include/msd/channel.hpp @@ -103,7 +103,6 @@ class channel { } queue_.push(std::forward(value)); - ++size_; } cnd_.notify_one(); @@ -125,13 +124,12 @@ class channel { std::unique_lock lock{mtx_}; waitBeforeRead(lock); - if (is_closed_ && size_ == 0) { + if (is_closed_ && queue_.empty()) { return false; } out = std::move(queue_.front()); queue_.pop(); - --size_; } cnd_.notify_one(); @@ -147,7 +145,7 @@ class channel { NODISCARD size_type size() const noexcept { std::unique_lock lock{mtx_}; - return size_; + return queue_.size(); } /** @@ -159,7 +157,7 @@ class channel { NODISCARD bool empty() const noexcept { std::unique_lock lock{mtx_}; - return size_ == 0; + return queue_.empty(); } /** @@ -195,7 +193,7 @@ class channel { NODISCARD bool drained() noexcept { std::unique_lock lock{mtx_}; - return size_ == 0 && is_closed_; + return queue_.empty() && is_closed_; } /** @@ -223,7 +221,6 @@ class channel { private: std::queue queue_; - std::size_t size_{0}; const size_type cap_{0}; mutable std::mutex mtx_; std::condition_variable cnd_; @@ -231,13 +228,13 @@ class channel { void waitBeforeRead(std::unique_lock& lock) { - cnd_.wait(lock, [this]() { return size_ > 0 || is_closed_; }); + cnd_.wait(lock, [this]() { return !queue_.empty() || is_closed_; }); }; void waitBeforeWrite(std::unique_lock& lock) { - if (cap_ > 0 && size_ == cap_) { - cnd_.wait(lock, [this]() { return size_ < cap_; }); + if (cap_ > 0 && queue_.size() == cap_) { + cnd_.wait(lock, [this]() { return queue_.size() < cap_; }); } } }; From c2c21ced399e9ef1c19078c3deec14d043c7c1d6 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:22:20 +0300 Subject: [PATCH 06/30] Revert "MSVC" This reverts commit c9aa39e7142af5b6c71f183be70b92f6c6f16afb. --- include/msd/blocking_iterator.hpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index 780aa10..bc9ec40 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -110,7 +110,7 @@ class blocking_writer_iterator { /** * @brief Constant reference to the type of the elements stored in the channel. */ - using reference = value_type&; + using reference = const value_type&; /** * @brief Supporting writing of elements. @@ -125,7 +125,7 @@ class blocking_writer_iterator { /** * @brief Pointer type to the value_type. */ - using pointer = value_type*; + using pointer = const value_type*; /** * @brief Constructs a blocking iterator from a channel reference. @@ -140,14 +140,9 @@ class blocking_writer_iterator { * @param val The value to be written into the channel. * * @return The iterator itself. - * - * @note This function is enabled for all types except `blocking_writer_iterator` itself. Reason: either I am awful - * at C++ or MSVC has a bug. It works fine with GCC and Clang, but MSVC fails to compile. */ template - typename std::enable_if::type, blocking_writer_iterator>::value, - blocking_writer_iterator&>::type - operator=(T&& val) + blocking_writer_iterator& operator=(T&& val) { chan_->write(std::forward(val)); return *this; From 7c7f82d408e634e330fe300373f8fb096d57cc39 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:24:40 +0300 Subject: [PATCH 07/30] Trailing return type --- tests/channel_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 42acc71..f63a037 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -381,7 +381,7 @@ TEST(ChannelTest, Transform) // by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](auto&& value) { return value.getValue() * 2; }); + [](auto&& value) -> int { return value.getValue() * 2; }); output_chan.close(); }; From d100e93444d7b46f8bfc157ea7ea962d0106f8ae Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:30:27 +0300 Subject: [PATCH 08/30] Transform to same type --- tests/channel_test.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index f63a037..45b2882 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -367,7 +367,7 @@ TEST(ChannelTest, Transform) std::atomic nums{0}; msd::channel input_chan{30}; - msd::channel output_chan{10}; + msd::channel output_chan{10}; // Send to channel input channel const auto writer = [&input_chan]() { @@ -377,18 +377,17 @@ TEST(ChannelTest, Transform) input_chan.close(); }; - // Transform input channel values from MovableOnly to int - // by multiplying by 2 and write to output channel + // Transform input channel values by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](auto&& value) -> int { return value.getValue() * 2; }); + [](auto&& value) { return MovableOnly{value.getValue() * 2}; }); output_chan.close(); }; // Read from output channel const auto reader = [&output_chan, &sum, &nums]() { - for (const auto out : output_chan) { // blocking until channel is drained (closed and empty) - sum += out; + for (auto&& out : output_chan) { // blocking until channel is drained (closed and empty) + sum += out.getValue(); ++nums; } }; From 323fcb228399ffda28fbcab6f9ac3971279683bc Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:36:44 +0300 Subject: [PATCH 09/30] Transform to different type --- tests/channel_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 45b2882..2672d9a 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -367,7 +367,7 @@ TEST(ChannelTest, Transform) std::atomic nums{0}; msd::channel input_chan{30}; - msd::channel output_chan{10}; + msd::channel output_chan{10}; // Send to channel input channel const auto writer = [&input_chan]() { @@ -377,17 +377,17 @@ TEST(ChannelTest, Transform) input_chan.close(); }; - // Transform input channel values by multiplying by 2 and write to output channel + // Transform input channel values from MovableOnly to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](auto&& value) { return MovableOnly{value.getValue() * 2}; }); + [](auto&& value) -> int { return value.getValue() * 2; }); output_chan.close(); }; // Read from output channel const auto reader = [&output_chan, &sum, &nums]() { for (auto&& out : output_chan) { // blocking until channel is drained (closed and empty) - sum += out.getValue(); + sum += out; ++nums; } }; From ac55b79446783c5df3f14c18beb406681ce62a70 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 18:44:10 +0300 Subject: [PATCH 10/30] Document limitation --- README.md | 8 ++++++-- examples/merge_channels.cpp | 36 ++++++++++++++++++++++++++++++++++-- tests/channel_test.cpp | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 72f88ed..48611db 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Channel [![build](https://github.com/andreiavrammsd/cpp-channel/actions/workflows/cmake.yml/badge.svg)](https://github.com/andreiavrammsd/cpp-channel/actions) [![codecov](https://codecov.io/github/andreiavrammsd/cpp-channel/graph/badge.svg?token=CKQ0TVW62Z)](https://codecov.io/github/andreiavrammsd/cpp-channel) -[![documentation](https://github.com/andreiavrammsd/cpp-channel/workflows/doc/badge.svg)](https://andreiavrammsd.github.io/cpp-channel/) +[![documentation](https://github.com/andreiavrammsd/cpp-channel/actions/workflows/doc.yml/badge.svg)](https://andreiavrammsd.github.io/cpp-channel/) ### Thread-safe container for sharing data between threads (synchronized queue). Header-only. Compatible with C++11. @@ -11,7 +11,7 @@ * Blocking (forever waiting to fetch). * Range-based for loop supported. * Close to prevent pushing and stop waiting to fetch. -* Integrates well with STL algorithms in some cases. Eg: +* Integrates well with STD algorithms in some cases. Eg: * `std::move(ch.begin(), ch.end(), ...)` * `std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan))`. * Tested with GCC, Clang, and MSVC. @@ -136,6 +136,10 @@ int main() { See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples) and [documentation](https://andreiavrammsd.github.io/cpp-channel/). +## Known limitations + +* Integration with some STD algorithms does not compile with MSVC +
Developed with [CLion](https://www.jetbrains.com/?from=serializer) and [Visual Studio Code](https://code.visualstudio.com/). diff --git a/examples/merge_channels.cpp b/examples/merge_channels.cpp index a6cf195..5bae08b 100644 --- a/examples/merge_channels.cpp +++ b/examples/merge_channels.cpp @@ -37,8 +37,40 @@ int main() }; const auto transformer = [&input_chan, &output_chan]() { - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](int value) { return value * 2; }); + const auto double_value = [](int value) { return value * 2; }; + +#ifdef _MSC_VER + for (auto&& value : input_chan) { + output_chan.write(double_value(value)); + } + + // Does not work with std::transform: + // + // could be 'void std::queue>>::push(int &&)' + // with + // [ + // T=ChannelTest_Traits_Test::TestBody::type + // ] + // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,20): + // 'void std::queue>>::push(int &&)': cannot convert argument 1 from + // '_OutIt' to 'int &&' with + // [ + // T=ChannelTest_Traits_Test::TestBody::type + // ] + // and + // [ + // _OutIt=msd::blocking_writer_iterator> + // ] + // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,43): + // Reason: cannot convert from '_OutIt' to 'int' + // with + // [ + // _OutIt=msd::blocking_writer_iterator> + // ] +#else + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); +#endif // _MSC_VER + output_chan.close(); }; diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 2672d9a..9ddf7c2 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -379,8 +379,40 @@ TEST(ChannelTest, Transform) // Transform input channel values from MovableOnly to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](auto&& value) -> int { return value.getValue() * 2; }); + const auto double_value = [](auto&& value) -> int { return value.getValue() * 2; }; + +#ifdef _MSC_VER + for (auto&& value : input_chan) { + output_chan.write(double_value(value)); + } + + // Does not work with std::transform: + // + // could be 'void std::queue>>::push(int &&)' + // with + // [ + // T=ChannelTest_Traits_Test::TestBody::type + // ] + // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,20): + // 'void std::queue>>::push(int &&)': cannot convert argument 1 from + // '_OutIt' to 'int &&' with + // [ + // T=ChannelTest_Traits_Test::TestBody::type + // ] + // and + // [ + // _OutIt=msd::blocking_writer_iterator> + // ] + // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,43): + // Reason: cannot convert from '_OutIt' to 'int' + // with + // [ + // _OutIt=msd::blocking_writer_iterator> + // ] +#else + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); +#endif // _MSC_VER + output_chan.close(); }; From 3c302ccdeba91652e1e59e0e86928c073b9308be Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:10:33 +0300 Subject: [PATCH 11/30] Add explicit instantiation --- tests/channel_test.cpp | 45 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 9ddf7c2..caa2133 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -359,6 +359,15 @@ class MovableOnly { int value{0}; }; +template msd::blocking_writer_iterator>& msd::blocking_writer_iterator>::operator= + (int&&); + +template msd::blocking_writer_iterator>& msd::blocking_writer_iterator>::operator= + (const int&); + +template bool msd::channel::write(int&&); +template bool msd::channel::write(const int&); + TEST(ChannelTest, Transform) { const int numbers = 100; @@ -379,40 +388,8 @@ TEST(ChannelTest, Transform) // Transform input channel values from MovableOnly to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { - const auto double_value = [](auto&& value) -> int { return value.getValue() * 2; }; - -#ifdef _MSC_VER - for (auto&& value : input_chan) { - output_chan.write(double_value(value)); - } - - // Does not work with std::transform: - // - // could be 'void std::queue>>::push(int &&)' - // with - // [ - // T=ChannelTest_Traits_Test::TestBody::type - // ] - // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,20): - // 'void std::queue>>::push(int &&)': cannot convert argument 1 from - // '_OutIt' to 'int &&' with - // [ - // T=ChannelTest_Traits_Test::TestBody::type - // ] - // and - // [ - // _OutIt=msd::blocking_writer_iterator> - // ] - // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,43): - // Reason: cannot convert from '_OutIt' to 'int' - // with - // [ - // _OutIt=msd::blocking_writer_iterator> - // ] -#else - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); -#endif // _MSC_VER - + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), + [](auto&& value) -> int { return value.getValue() * 2; }); output_chan.close(); }; From 20379569e954951452e79c144248115d79391912 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:31:24 +0300 Subject: [PATCH 12/30] Rename val to value --- include/msd/blocking_iterator.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index bc9ec40..30b2cc5 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -142,9 +142,9 @@ class blocking_writer_iterator { * @return The iterator itself. */ template - blocking_writer_iterator& operator=(T&& val) + blocking_writer_iterator& operator=(T&& value) { - chan_->write(std::forward(val)); + chan_->write(std::forward(value)); return *this; } From 6df31b2dea28e2b6a486a17d3315c5ed7f5df7da Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:43:18 +0300 Subject: [PATCH 13/30] Revert "Add explicit instantiation" This reverts commit 3c302ccdeba91652e1e59e0e86928c073b9308be. --- tests/channel_test.cpp | 45 +++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index caa2133..9ddf7c2 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -359,15 +359,6 @@ class MovableOnly { int value{0}; }; -template msd::blocking_writer_iterator>& msd::blocking_writer_iterator>::operator= - (int&&); - -template msd::blocking_writer_iterator>& msd::blocking_writer_iterator>::operator= - (const int&); - -template bool msd::channel::write(int&&); -template bool msd::channel::write(const int&); - TEST(ChannelTest, Transform) { const int numbers = 100; @@ -388,8 +379,40 @@ TEST(ChannelTest, Transform) // Transform input channel values from MovableOnly to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](auto&& value) -> int { return value.getValue() * 2; }); + const auto double_value = [](auto&& value) -> int { return value.getValue() * 2; }; + +#ifdef _MSC_VER + for (auto&& value : input_chan) { + output_chan.write(double_value(value)); + } + + // Does not work with std::transform: + // + // could be 'void std::queue>>::push(int &&)' + // with + // [ + // T=ChannelTest_Traits_Test::TestBody::type + // ] + // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,20): + // 'void std::queue>>::push(int &&)': cannot convert argument 1 from + // '_OutIt' to 'int &&' with + // [ + // T=ChannelTest_Traits_Test::TestBody::type + // ] + // and + // [ + // _OutIt=msd::blocking_writer_iterator> + // ] + // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,43): + // Reason: cannot convert from '_OutIt' to 'int' + // with + // [ + // _OutIt=msd::blocking_writer_iterator> + // ] +#else + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); +#endif // _MSC_VER + output_chan.close(); }; From 994a0deabc0a013ea1f7a0bbaae644a7505f6e95 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:47:07 +0300 Subject: [PATCH 14/30] Fix doc --- include/msd/blocking_iterator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index 30b2cc5..f2eb56b 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -137,7 +137,7 @@ class blocking_writer_iterator { /** * @brief Writes an element into the channel, blocking until space is available. * - * @param val The value to be written into the channel. + * @param value The value to be written into the channel. * * @return The iterator itself. */ From 51d0df995d91430b71403f65d40130e1158a2b10 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 20:02:11 +0300 Subject: [PATCH 15/30] Test algos --- tests/channel_test.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 9ddf7c2..17e8ddc 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -438,3 +439,55 @@ TEST(ChannelTest, Transform) EXPECT_EQ(sum, expected_sum); EXPECT_EQ(nums, numbers); } + +TEST(ChannelTest, TransformAndAccumulate) +{ + msd::channel input_chan{10}; + msd::channel output_chan{10}; + + // Producer: fill input channel with 1..5 + const auto producer = [&input_chan]() { + for (int i = 1; i <= 5; ++i) { + input_chan.write(i); + } + input_chan.close(); + }; + + // Transformer: double the values using std::transform with blocking iterators + const auto transformer = [&input_chan, &output_chan]() { + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), + [](int v) { return v * 2; }); + output_chan.close(); + }; + + const auto producer_task = std::async(std::launch::async, producer); + const auto transformer_task = std::async(std::launch::async, transformer); + + producer_task.wait(); + transformer_task.wait(); + + // Consumer: accumulate output channel values + const int sum = std::accumulate(output_chan.begin(), output_chan.end(), 0); + EXPECT_EQ(sum, 30); +} + +TEST(ChannelTest, CopyToVector) +{ + msd::channel chan{10}; + std::vector results; + + // Producer: write 1..4 into channel and close + const auto producer = [&]() { + for (int i = 1; i <= 4; ++i) { + chan.write(i); + } + chan.close(); + }; + + producer(); + + // Use std::copy to copy from channel to vector using channel's blocking iterator + std::copy(chan.begin(), chan.end(), std::back_inserter(results)); + + EXPECT_EQ(results, std::vector({1, 2, 3, 4})); +} From dad2512ca2dbd2b52fc639e4279746e9d10f72ad Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:09:00 +0300 Subject: [PATCH 16/30] Test with copy_if --- tests/channel_test.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 17e8ddc..81fc62f 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -445,30 +445,31 @@ TEST(ChannelTest, TransformAndAccumulate) msd::channel input_chan{10}; msd::channel output_chan{10}; - // Producer: fill input channel with 1..5 + // Producer: fill input channel with 1..101 const auto producer = [&input_chan]() { - for (int i = 1; i <= 5; ++i) { + for (int i = 1; i <= 101; ++i) { input_chan.write(i); } input_chan.close(); }; - // Transformer: double the values using std::transform with blocking iterators - const auto transformer = [&input_chan, &output_chan]() { - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](int v) { return v * 2; }); + // Filter: take even numbers + const auto filter = [&input_chan, &output_chan]() { + std::copy_if(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), + [](int v) { return v % 2 == 0; }); output_chan.close(); }; const auto producer_task = std::async(std::launch::async, producer); - const auto transformer_task = std::async(std::launch::async, transformer); - - producer_task.wait(); - transformer_task.wait(); + const auto filter_task = std::async(std::launch::async, filter); // Consumer: accumulate output channel values const int sum = std::accumulate(output_chan.begin(), output_chan.end(), 0); - EXPECT_EQ(sum, 30); + + producer_task.wait(); + filter_task.wait(); + + EXPECT_EQ(sum, 2550); } TEST(ChannelTest, CopyToVector) @@ -486,7 +487,7 @@ TEST(ChannelTest, CopyToVector) producer(); - // Use std::copy to copy from channel to vector using channel's blocking iterator + // Copy from channel to vector std::copy(chan.begin(), chan.end(), std::back_inserter(results)); EXPECT_EQ(results, std::vector({1, 2, 3, 4})); From a699fb3515f83d6b5c4576f11354ed0fc2934601 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:17:11 +0300 Subject: [PATCH 17/30] Use std::fill_n --- README.md | 3 ++- tests/channel_test.cpp | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 48611db..9e89687 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,10 @@ * Blocking (forever waiting to fetch). * Range-based for loop supported. * Close to prevent pushing and stop waiting to fetch. -* Integrates well with STD algorithms in some cases. Eg: +* Integrates with some of the STD algorithms. Eg: * `std::move(ch.begin(), ch.end(), ...)` * `std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan))`. + * `std::copy(chan.begin(), chan.end(), ...);` * Tested with GCC, Clang, and MSVC. * Includes stack-based, exception-free alternative (static channel). diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 81fc62f..5534dd5 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -479,6 +479,8 @@ TEST(ChannelTest, CopyToVector) // Producer: write 1..4 into channel and close const auto producer = [&]() { + std::fill_n(msd::back_inserter(chan), 4, 0); + for (int i = 1; i <= 4; ++i) { chan.write(i); } @@ -490,5 +492,5 @@ TEST(ChannelTest, CopyToVector) // Copy from channel to vector std::copy(chan.begin(), chan.end(), std::back_inserter(results)); - EXPECT_EQ(results, std::vector({1, 2, 3, 4})); + EXPECT_EQ(results, std::vector({0, 0, 0, 0, 1, 2, 3, 4})); } From d421e9178ea242028bcd65908daf37c2a1f56309 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:35:30 +0300 Subject: [PATCH 18/30] Doc copy_if --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e89687..ce8e7f5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ * Integrates with some of the STD algorithms. Eg: * `std::move(ch.begin(), ch.end(), ...)` * `std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan))`. - * `std::copy(chan.begin(), chan.end(), ...);` + * `std::copy_if(chan.begin(), chan.end(), ...);` * Tested with GCC, Clang, and MSVC. * Includes stack-based, exception-free alternative (static channel). From 53b95b25ca85a4689b6ede00856dd3a8d10352ed Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:41:36 +0300 Subject: [PATCH 19/30] Assign by l-value --- include/msd/blocking_iterator.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index f2eb56b..8947866 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -141,10 +141,9 @@ class blocking_writer_iterator { * * @return The iterator itself. */ - template - blocking_writer_iterator& operator=(T&& value) + blocking_writer_iterator& operator=(const value_type& value) { - chan_->write(std::forward(value)); + chan_->write(value); return *this; } From d4bd15fc310021faf9e1b262a122043a69d85234 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:48:13 +0300 Subject: [PATCH 20/30] Remove conditional compilation --- examples/merge_channels.cpp | 35 ++------------------------ tests/channel_test.cpp | 49 ++++++++----------------------------- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/examples/merge_channels.cpp b/examples/merge_channels.cpp index 5bae08b..7f06ea5 100644 --- a/examples/merge_channels.cpp +++ b/examples/merge_channels.cpp @@ -37,39 +37,8 @@ int main() }; const auto transformer = [&input_chan, &output_chan]() { - const auto double_value = [](int value) { return value * 2; }; - -#ifdef _MSC_VER - for (auto&& value : input_chan) { - output_chan.write(double_value(value)); - } - - // Does not work with std::transform: - // - // could be 'void std::queue>>::push(int &&)' - // with - // [ - // T=ChannelTest_Traits_Test::TestBody::type - // ] - // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,20): - // 'void std::queue>>::push(int &&)': cannot convert argument 1 from - // '_OutIt' to 'int &&' with - // [ - // T=ChannelTest_Traits_Test::TestBody::type - // ] - // and - // [ - // _OutIt=msd::blocking_writer_iterator> - // ] - // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,43): - // Reason: cannot convert from '_OutIt' to 'int' - // with - // [ - // _OutIt=msd::blocking_writer_iterator> - // ] -#else - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); -#endif // _MSC_VER + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), + [](int value) { return value * 2; }); output_chan.close(); }; diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 5534dd5..8b5ed62 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -326,7 +326,7 @@ TEST(ChannelTest, ReadWriteClose) class MovableOnly { public: - explicit MovableOnly(int v) : value(v) {} + explicit MovableOnly(int value) : value_{value} {} MovableOnly() = default; @@ -336,7 +336,7 @@ class MovableOnly { std::abort(); } - MovableOnly(MovableOnly&& other) noexcept : value{std::move(other.value)} { other.value = 0; } + MovableOnly(MovableOnly&& other) noexcept : value_{std::move(other.value_)} { other.value_ = 0; } MovableOnly& operator=(const MovableOnly&) { @@ -347,17 +347,19 @@ class MovableOnly { MovableOnly& operator=(MovableOnly&& other) noexcept { if (this != &other) { - value = other.value; - other.value = 0; + value_ = other.value_; + other.value_ = 0; } return *this; } - int getValue() const { return value; } + int getValue() const { return value_; } + + virtual ~MovableOnly() = default; private: - int value{0}; + int value_{0}; }; TEST(ChannelTest, Transform) @@ -380,39 +382,8 @@ TEST(ChannelTest, Transform) // Transform input channel values from MovableOnly to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { - const auto double_value = [](auto&& value) -> int { return value.getValue() * 2; }; - -#ifdef _MSC_VER - for (auto&& value : input_chan) { - output_chan.write(double_value(value)); - } - - // Does not work with std::transform: - // - // could be 'void std::queue>>::push(int &&)' - // with - // [ - // T=ChannelTest_Traits_Test::TestBody::type - // ] - // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,20): - // 'void std::queue>>::push(int &&)': cannot convert argument 1 from - // '_OutIt' to 'int &&' with - // [ - // T=ChannelTest_Traits_Test::TestBody::type - // ] - // and - // [ - // _OutIt=msd::blocking_writer_iterator> - // ] - // D:\a\cpp-channel\cpp-channel\include\msd\channel.hpp(105,43): - // Reason: cannot convert from '_OutIt' to 'int' - // with - // [ - // _OutIt=msd::blocking_writer_iterator> - // ] -#else - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); -#endif // _MSC_VER + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), + [](auto&& value) { return value.getValue() * 2; }); output_chan.close(); }; From 5a3bbea5e73b65c869f22c9c11405ffc31d3d535 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:54:27 +0300 Subject: [PATCH 21/30] Cosmetic --- examples/merge_channels.cpp | 1 - tests/channel_test.cpp | 34 +++++++++++++++++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/examples/merge_channels.cpp b/examples/merge_channels.cpp index 7f06ea5..a6cf195 100644 --- a/examples/merge_channels.cpp +++ b/examples/merge_channels.cpp @@ -39,7 +39,6 @@ int main() const auto transformer = [&input_chan, &output_chan]() { std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), [](int value) { return value * 2; }); - output_chan.close(); }; diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 8b5ed62..654e64b 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -324,27 +324,27 @@ TEST(ChannelTest, ReadWriteClose) EXPECT_EQ(nums, numbers); } -class MovableOnly { +class movable_only { public: - explicit MovableOnly(int value) : value_{value} {} + explicit movable_only(int value) : value_{value} {} - MovableOnly() = default; + movable_only() = default; - MovableOnly(const MovableOnly&) + movable_only(const movable_only&) { - std::cout << "Copy constructor should not be called for MovableOnly"; + std::cout << "Copy constructor should not be called"; std::abort(); } - MovableOnly(MovableOnly&& other) noexcept : value_{std::move(other.value_)} { other.value_ = 0; } + movable_only(movable_only&& other) noexcept : value_{std::move(other.value_)} { other.value_ = 0; } - MovableOnly& operator=(const MovableOnly&) + movable_only& operator=(const movable_only&) { - std::cout << "Copy assignment should not be called for MovableOnly"; + std::cout << "Copy assignment should not be called"; std::abort(); } - MovableOnly& operator=(MovableOnly&& other) noexcept + movable_only& operator=(movable_only&& other) noexcept { if (this != &other) { value_ = other.value_; @@ -356,7 +356,7 @@ class MovableOnly { int getValue() const { return value_; } - virtual ~MovableOnly() = default; + virtual ~movable_only() = default; private: int value_{0}; @@ -365,22 +365,22 @@ class MovableOnly { TEST(ChannelTest, Transform) { const int numbers = 100; - const std::int64_t expected_sum = 5050 * 2; - std::atomic sum{0}; - std::atomic nums{0}; + const int expected_sum = 5050 * 2; + std::atomic sum{0}; + std::atomic nums{0}; - msd::channel input_chan{30}; + msd::channel input_chan{30}; msd::channel output_chan{10}; // Send to channel input channel const auto writer = [&input_chan]() { for (int i = 1; i <= numbers; ++i) { - input_chan.write(MovableOnly{i}); + input_chan.write(movable_only{i}); } input_chan.close(); }; - // Transform input channel values from MovableOnly to int by multiplying by 2 and write to output channel + // Transform input channel values from movable_only to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), [](auto&& value) { return value.getValue() * 2; }); @@ -427,7 +427,7 @@ TEST(ChannelTest, TransformAndAccumulate) // Filter: take even numbers const auto filter = [&input_chan, &output_chan]() { std::copy_if(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](int v) { return v % 2 == 0; }); + [](int value) { return value % 2 == 0; }); output_chan.close(); }; From 21870becc4a475ef361925464924030db680ecff Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Wed, 11 Jun 2025 22:05:39 +0300 Subject: [PATCH 22/30] Revert "Assign by l-value" This reverts commit 53b95b25ca85a4689b6ede00856dd3a8d10352ed. --- include/msd/blocking_iterator.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index 8947866..f2eb56b 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -141,9 +141,10 @@ class blocking_writer_iterator { * * @return The iterator itself. */ - blocking_writer_iterator& operator=(const value_type& value) + template + blocking_writer_iterator& operator=(T&& value) { - chan_->write(value); + chan_->write(std::forward(value)); return *this; } From b218ae1b29c0cab3e2245bdf4664e053a0bc4272 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 09:57:03 +0300 Subject: [PATCH 23/30] Use for --- README.md | 2 +- tests/channel_test.cpp | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ce8e7f5..a87ee69 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/example ## Known limitations -* Integration with some STD algorithms does not compile with MSVC +* Integration with some [STD algorithms](https://github.com/andreiavrammsd/cpp-channel/blob/master/examples/merge_channels.cpp) does not compile with MSVC
diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 654e64b..eb82447 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -382,8 +382,19 @@ TEST(ChannelTest, Transform) // Transform input channel values from movable_only to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { - std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), - [](auto&& value) { return value.getValue() * 2; }); + const auto double_value = [](auto&& value) { return value.getValue() * 2; }; +#ifdef _MSC_VER + for (auto&& value : input_chan) { + output_chan.write(double_value(value)); + } + + // Does not work with std::transform: warning C4702: unreachable code + // -- Building for: Visual Studio 17 2022 + // -- The C compiler identification is MSVC 19.43.34808.0 + // -- The CXX compiler identification is MSVC 19.43.34808.0 +#else + std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); +#endif // _MSC_VER output_chan.close(); }; From d08a7c07db43198391dac18cf90ec78781ca84eb Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:07:47 +0300 Subject: [PATCH 24/30] Ref --- include/msd/blocking_iterator.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index f2eb56b..4d79be7 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -141,10 +141,9 @@ class blocking_writer_iterator { * * @return The iterator itself. */ - template - blocking_writer_iterator& operator=(T&& value) + blocking_writer_iterator& operator=(reference value) { - chan_->write(std::forward(value)); + chan_->write(value); return *this; } From 815d4e780c4c8fa199af424d8c0a7f3f003704c3 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:16:14 +0300 Subject: [PATCH 25/30] Limits --- README.md | 2 +- tests/channel_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a87ee69..4ffa5ba 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/example ## Known limitations -* Integration with some [STD algorithms](https://github.com/andreiavrammsd/cpp-channel/blob/master/examples/merge_channels.cpp) does not compile with MSVC +* In some cases, the integration with some STD algorithms does not compile with MSVC. See the [Transform test](https://github.com/andreiavrammsd/cpp-channel/blob/master/tests/channel_test.cpp).
diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index eb82447..649a8e9 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -422,7 +422,7 @@ TEST(ChannelTest, Transform) EXPECT_EQ(nums, numbers); } -TEST(ChannelTest, TransformAndAccumulate) +TEST(ChannelTest, FilterAndAccumulate) { msd::channel input_chan{10}; msd::channel output_chan{10}; From ddc963e1e74a9667b5b1f86ac7bb7a8035aef33c Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:31:24 +0300 Subject: [PATCH 26/30] Comments --- tests/channel_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 649a8e9..ed79940 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -372,7 +372,7 @@ TEST(ChannelTest, Transform) msd::channel input_chan{30}; msd::channel output_chan{10}; - // Send to channel input channel + // Send to input channel const auto writer = [&input_chan]() { for (int i = 1; i <= numbers; ++i) { input_chan.write(movable_only{i}); @@ -427,7 +427,7 @@ TEST(ChannelTest, FilterAndAccumulate) msd::channel input_chan{10}; msd::channel output_chan{10}; - // Producer: fill input channel with 1..101 + // Producer: send numbers on input channel const auto producer = [&input_chan]() { for (int i = 1; i <= 101; ++i) { input_chan.write(i); @@ -435,7 +435,7 @@ TEST(ChannelTest, FilterAndAccumulate) input_chan.close(); }; - // Filter: take even numbers + // Filter: take even numbers from input channel and write them to output channel const auto filter = [&input_chan, &output_chan]() { std::copy_if(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), [](int value) { return value % 2 == 0; }); From e5be3f96be41a8366b9b0b86b7ee6973bbb5c5d8 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 11:29:50 +0300 Subject: [PATCH 27/30] Document issue --- include/msd/blocking_iterator.hpp | 5 +++++ tests/channel_test.cpp | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index 4d79be7..46ed653 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -150,6 +150,11 @@ class blocking_writer_iterator { /** * @brief Not applicable (handled by operator=). * + * @note It's uncommon to return a reference to an iterator, but I don't want to return a value from the channel. + * This iterator is supposed to be used only to write values. + * I don't know if it's a terrible idea or not, but it looks related to the issue with MSVC + * in the Transform test in tests/channel_test.cpp. + * * @return The iterator itself. */ blocking_writer_iterator& operator*() { return *this; } diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index ed79940..885b1fe 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -388,10 +388,18 @@ TEST(ChannelTest, Transform) output_chan.write(double_value(value)); } - // Does not work with std::transform: warning C4702: unreachable code + // Does not work with std::transform // -- Building for: Visual Studio 17 2022 // -- The C compiler identification is MSVC 19.43.34808.0 // -- The CXX compiler identification is MSVC 19.43.34808.0 + // + // Release: does not compile - warning C4702: unreachable code + // Debug: compiles, but copies the movable_only object instead of moving it + // + // Posibilities: + // - I am doing something very wrong (see operator* in blocking_writer_iterator) + // - MSVC has a bug + // - Other compilers are more permissive #else std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); #endif // _MSC_VER From b4823ded158d501c630009bd5ddaaf07d7adfef7 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:16:29 +0300 Subject: [PATCH 28/30] Add related links --- tests/channel_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index 885b1fe..3b353f3 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -399,6 +399,8 @@ TEST(ChannelTest, Transform) // Posibilities: // - I am doing something very wrong (see operator* in blocking_writer_iterator) // - MSVC has a bug + // - https://github.com/ericniebler/range-v3/issues/1814 + // - https://github.com/ericniebler/range-v3/issues/1762 // - Other compilers are more permissive #else std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan), double_value); From c1dda333de03811a2dff6b20eb77710331f7f0d5 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:40:30 +0300 Subject: [PATCH 29/30] Test writing on closed channel --- include/msd/blocking_iterator.hpp | 2 ++ tests/blocking_iterator_test.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index 46ed653..21fe521 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -137,6 +137,8 @@ class blocking_writer_iterator { /** * @brief Writes an element into the channel, blocking until space is available. * + * @note There is no effect if the channel is closed. + * * @param value The value to be written into the channel. * * @return The iterator itself. diff --git a/tests/blocking_iterator_test.cpp b/tests/blocking_iterator_test.cpp index 474fd16..3c10ec8 100644 --- a/tests/blocking_iterator_test.cpp +++ b/tests/blocking_iterator_test.cpp @@ -70,6 +70,7 @@ TEST(BlockingWriterIteratorTest, WriteToChannelUsingBackInserter) *out = 20; *out = 30; channel.close(); + *out = 40; // ignored because channel is closed }); std::vector results; From 79125bd92fade42311ac5a3df1844f5d480c12c1 Mon Sep 17 00:00:00 2001 From: Andrei Avram <6795248+andreiavrammsd@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:56:37 +0300 Subject: [PATCH 30/30] Improve doc --- include/msd/blocking_iterator.hpp | 17 ++++++----------- include/msd/channel.hpp | 9 ++------- include/msd/static_channel.hpp | 13 ++++--------- tests/blocking_iterator_test.cpp | 2 +- 4 files changed, 13 insertions(+), 28 deletions(-) diff --git a/include/msd/blocking_iterator.hpp b/include/msd/blocking_iterator.hpp index 21fe521..90a3b78 100644 --- a/include/msd/blocking_iterator.hpp +++ b/include/msd/blocking_iterator.hpp @@ -9,9 +9,9 @@ namespace msd { /** - * @brief An iterator that block the current thread, waiting to fetch elements from the channel. + * @brief An iterator that blocks the current thread, waiting to fetch elements from the channel. * - * Used to implement channel range-based for loop. + * @details Used to implement channel range-based for loop. * * @tparam Channel Type of channel being iterated. */ @@ -80,7 +80,6 @@ class blocking_iterator { * @brief Makes iteration continue until the channel is closed and empty. * * @param other Another blocking_iterator to compare with. - * * @return true if the channel is not closed or not empty (continue iterating). * @return false if the channel is closed and empty (stop iterating). */ @@ -95,7 +94,7 @@ class blocking_iterator { /** * @brief An output iterator pushes elements into a channel. Blocking until the channel is not full. * - * Used to integrate with standard algorithms that require an output iterator. + * @details Used to integrate with standard algorithms that require an output iterator. * * @tparam Channel Type of channel being iterated. */ @@ -137,11 +136,9 @@ class blocking_writer_iterator { /** * @brief Writes an element into the channel, blocking until space is available. * - * @note There is no effect if the channel is closed. - * * @param value The value to be written into the channel. - * * @return The iterator itself. + * @note There is no effect if the channel is closed. */ blocking_writer_iterator& operator=(reference value) { @@ -152,12 +149,12 @@ class blocking_writer_iterator { /** * @brief Not applicable (handled by operator=). * + * @return The iterator itself. + * * @note It's uncommon to return a reference to an iterator, but I don't want to return a value from the channel. * This iterator is supposed to be used only to write values. * I don't know if it's a terrible idea or not, but it looks related to the issue with MSVC * in the Transform test in tests/channel_test.cpp. - * - * @return The iterator itself. */ blocking_writer_iterator& operator*() { return *this; } @@ -183,9 +180,7 @@ class blocking_writer_iterator { * @brief Creates a blocking iterator for the given channel. * * @tparam Channel Type of channel being iterated. - * * @param chan Reference to the channel this iterator will iterate over. - * * @return A blocking iterator for the specified channel. */ template diff --git a/include/msd/channel.hpp b/include/msd/channel.hpp index ca3294f..eb6a61e 100644 --- a/include/msd/channel.hpp +++ b/include/msd/channel.hpp @@ -31,7 +31,8 @@ class closed_channel : public std::runtime_error { /** * @brief Thread-safe container for sharing data between threads. * - * Implements a blocking input iterator. + * - Not movable, not copyable. + * - Includes a blocking input iterator. * * @tparam T The type of the elements. */ @@ -85,9 +86,7 @@ class channel { * @brief Pushes an element into the channel. * * @tparam Type The type of the elements. - * * @param value The element to be pushed into the channel. - * * @return true If an element was successfully pushed into the channel. * @return false If the channel is closed. */ @@ -114,7 +113,6 @@ class channel { * @brief Pops an element from the channel. * * @param out Reference to the variable where the popped element will be stored. - * * @return true If an element was successfully read from the channel. * @return false If the channel is closed and empty. */ @@ -210,9 +208,6 @@ class channel { */ iterator end() noexcept { return blocking_iterator>{*this, true}; } - /** - * Channel cannot be copied or moved. - */ channel(const channel&) = delete; channel& operator=(const channel&) = delete; channel(channel&&) = delete; diff --git a/include/msd/static_channel.hpp b/include/msd/static_channel.hpp index 469000f..bb6e7ba 100644 --- a/include/msd/static_channel.hpp +++ b/include/msd/static_channel.hpp @@ -16,9 +16,10 @@ namespace msd { /** * @brief Thread-safe container for sharing data between threads. * - * Allocates elements on the stack. - * Does not throw exceptions. - * Implements a blocking input iterator. + * - Allocates elements on the stack. + * - Does not throw exceptions. + * - Not movable, not copyable. + * - Includes a blocking input iterator. * * @tparam T The type of the elements. * @tparam Capacity The maximum number of elements the channel can hold before blocking. @@ -52,9 +53,7 @@ class static_channel { * @brief Pushes an element into the channel. * * @tparam Type The type of the elements. - * * @param value The element to be pushed into the channel. - * * @return true If an element was successfully pushed into the channel. * @return false If the channel is closed. */ @@ -82,7 +81,6 @@ class static_channel { * @brief Pops an element from the channel. * * @param out Reference to the variable where the popped element will be stored. - * * @return true If an element was successfully read from the channel. * @return false If the channel is closed and empty. */ @@ -179,9 +177,6 @@ class static_channel { */ iterator end() noexcept { return blocking_iterator>{*this, true}; } - /** - * Channel cannot be copied or moved. - */ static_channel(const static_channel&) = delete; static_channel& operator=(const static_channel&) = delete; static_channel(static_channel&&) = delete; diff --git a/tests/blocking_iterator_test.cpp b/tests/blocking_iterator_test.cpp index 3c10ec8..b88ff6e 100644 --- a/tests/blocking_iterator_test.cpp +++ b/tests/blocking_iterator_test.cpp @@ -70,7 +70,7 @@ TEST(BlockingWriterIteratorTest, WriteToChannelUsingBackInserter) *out = 20; *out = 30; channel.close(); - *out = 40; // ignored because channel is closed + *out = 40; // Ignored because the channel is closed }); std::vector results;