Skip to content

Commit d4667ca

Browse files
Examples (#79)
* Add semaphore example * Add concurrent map filter example * Refine readme examples * Fix markdown issues * Set CMAKE_CXX_EXTENSIONS
1 parent 6799006 commit d4667ca

File tree

13 files changed

+272
-193
lines changed

13 files changed

+272
-193
lines changed

.github/workflows/cmake.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888

8989
- name: Run examples
9090
working-directory: ${{github.workspace}}/build
91-
run: cmake --build . --config ${{ matrix.config.build_type }} --target examples -j
91+
run: cmake --build . --config ${{ matrix.config.build_type }} --target run_examples -j
9292

9393
coverage:
9494
name: Coverage

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"cSpell.words": [
3131
"ARGN",
3232
"clangd",
33+
"cout",
3334
"cppcoreguidelines",
3435
"cpupower",
3536
"ctest",

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ project(cpp_channel VERSION 1.3.0)
33

44
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard")
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6-
set(CXX_EXTENSIONS OFF)
6+
set(CMAKE_CXX_EXTENSIONS OFF)
77

88
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
99
include(warnings)

README.md

Lines changed: 64 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,44 @@
33
[![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)
44
[![documentation](https://github.com/andreiavrammsd/cpp-channel/actions/workflows/doc.yml/badge.svg)](https://andreiavrammsd.github.io/cpp-channel/)
55

6-
### Thread-safe container for sharing data between threads (synchronized queue). Header-only. Compatible with C++11 and newer.
6+
> Thread-safe container for sharing data between threads (synchronized queue). Header-only. Compatible with C++11 and newer.
77
88
## About
99

1010
`msd::channel`
11+
1112
* A synchronized queue that can be easily and safely shared between multiple threads.
1213
* Tested with GCC, Clang, and MSVC.
1314
* Uses [std::mutex](https://en.cppreference.com/w/cpp/thread/mutex.html) for synchronization.
1415
* Uses a customizable `storage` to store elements.
1516

1617
It's a class that can be constructed in several ways:
18+
1719
* Buffered:
18-
* The channel accepts a specified number of elements, after which it blocks the writer threads and waits for a reader thread to read an element.
19-
* It blocks the reader threads when channel is empty until a writer thread writes elements.
20-
* `msd::channel<int> chan{2};`
20+
* The channel accepts a specified number of elements, after which it blocks the writer threads and waits for a reader thread to read an element.
21+
* It blocks the reader threads when channel is empty until a writer thread writes elements.
22+
* `msd::channel<int> chan{2};`
2123
* Unbuffered:
22-
* Never blocks writes.
23-
* It blocks the reader threads when channel is empty until a writer thread writes elements.
24-
* `msd::channel<int> chan{};`
24+
* Never blocks writes.
25+
* It blocks the reader threads when channel is empty until a writer thread writes elements.
26+
* `msd::channel<int> chan{};`
2527
* Heap- or stack-allocated: pass a custom storage or choose a [built-in storage](https://github.com/andreiavrammsd/cpp-channel/blob/master/include/msd/storage.hpp):
26-
* `msd::queue_storage` (default): uses [std::queue](https://en.cppreference.com/w/cpp/container/queue.html)
27-
* `msd::vector_storage`: uses [std::vector](https://en.cppreference.com/w/cpp/container/vector.html) (if cache locality is important)
28-
* `msd::channel<int, msd::vector_storage<int>> chan{2};`
29-
* `msd::array_storage` (always buffered): uses [std::array](https://en.cppreference.com/w/cpp/container/array.html) (if you want stack allocation)
30-
* `msd::channel<int, msd::array_storage<int, 10>> chan{};`
31-
* `msd::channel<int, msd::array_storage<int, 10>> chan{10}; // does not compile because capacity is already passed as template argument`
32-
* aka `msd::static_channel<int, 10>`
28+
* `msd::queue_storage` (default): uses [std::queue](https://en.cppreference.com/w/cpp/container/queue.html)
29+
* `msd::vector_storage`: uses [std::vector](https://en.cppreference.com/w/cpp/container/vector.html) (if cache locality is important)
30+
* `msd::channel<int, msd::vector_storage<int>> chan{2};`
31+
* `msd::array_storage` (always buffered): uses [std::array](https://en.cppreference.com/w/cpp/container/array.html) (if you want stack allocation)
32+
* `msd::channel<int, msd::array_storage<int, 10>> chan{};`
33+
* `msd::channel<int, msd::array_storage<int, 10>> chan{10}; // does not compile because capacity is already passed as template argument`
34+
* aka `msd::static_channel<int, 10>`
3335

3436
A `storage` is:
37+
3538
* A class with a specific interface for storing elements.
3639
* Must implement [FIFO](https://en.wikipedia.org/wiki/FIFO) logic.
3740
* See [built-in storages](https://github.com/andreiavrammsd/cpp-channel/blob/master/include/msd/storage.hpp).
3841

3942
Exceptions:
43+
4044
* msd::operator<< throws `msd::closed_channel` if channel is closed.
4145
* `msd::channel::write` returns `bool` status instead of throwing.
4246
* Heap-allocated storages could throw.
@@ -78,104 +82,85 @@ VERSION=X.Y.Z \
7882

7983
## Usage
8084

81-
```c++
82-
#include <cassert>
85+
```cpp
86+
// Unbuffered channel
8387

8488
#include <msd/channel.hpp>
8589

86-
int main() {
87-
msd::channel<int> chan; // Unbuffered
88-
89-
// Send to channel
90-
chan << 1 << 2;
91-
92-
// Read from channel
93-
int first{};
94-
int second{};
90+
int main()
91+
{
92+
msd::channel<int> chan;
9593

96-
chan >> first >> second;
94+
chan << 1 << 2; // Send
9795

98-
assert(first == 1);
99-
assert(second == 2);
96+
int first_value{};
97+
int second_value{};
98+
chan >> first_value >> second_value; // Receive
99+
chan.read(first_value); // Returns channel close status (true/false), blocks thread when channel is empty
100100
}
101101
```
102102

103-
```c++
104-
#include <cassert>
105-
106-
#include <msd/channel.hpp>
107-
108-
int main() {
109-
msd::channel<int, msd::vector_storage<int>> chan{2}; // Buffered with vector storage
103+
```cpp
104+
// Buffered channel with custom storage
110105

111-
// Send to channel
112-
chan << 1; // Throws if the channel is closed (after chan.close())
113-
assert(chan.write(2)); // Returns false if the channel is closed (after chan.close())
114-
chan << 3; // Blocks because the capacity is 2 (and no one reads from channel)
115-
}
116-
```
117-
118-
```c++
119106
#include <msd/channel.hpp>
120107

121-
int main() {
122-
msd::channel<int> chan{2}; // Buffered
123-
124-
int in = 1;
125-
int out = 0;
126-
127-
// Send to channel
128-
chan << in;
129-
chan << in;
108+
int main()
109+
{
110+
msd::channel<int, msd::vector_storage<int>> chan{2};
130111

131-
// Read from channel
132-
chan.read(out);
133-
chan >> out;
134-
chan >> out; // Blocks because the channel is empty (and no one writes on it)
112+
chan << 1; // Throws if channel is closed
113+
chan.write(2); // Non-throwing write, returns channel close status (true/false)
114+
chan << 3; // Blocks thread (no space, no reader)
135115
}
136116
```
137117

138-
```c++
139-
#include <iostream>
118+
```cpp
119+
// Range-based iteration
140120

141121
#include <msd/channel.hpp>
142122

143-
int main() {
144-
msd::channel<int, msd::vector_storage<int>> chan;
123+
#include <iostream>
145124

146-
int in1 = 1;
147-
int in2 = 2;
125+
int main()
126+
{
127+
msd::channel<int> chan{2};
148128

149-
chan << in1 << in2;
129+
chan << 1 << 2;
130+
for (int value : chan) {
131+
if (chan.closed()) {
132+
// You can break before it's empty
133+
break;
134+
}
150135

151-
for (const auto out : chan) { // Blocks: waits forever for channel items
152-
std::cout << out << '\n';
136+
std::cout << value << '\n'; // Blocks thread until there is data to read or channel is closed and empty
153137
}
154138
}
155139
```
156140

157-
```c++
141+
```cpp
142+
// Channel with statically-allocated storage (always buffered)
143+
158144
#include <msd/static_channel.hpp>
159145

160-
int main() {
161-
msd::static_channel<int, 2> chan{}; // Always buffered
162-
// Same as msd::channel<int, msd::array_storage<int, 2>>
146+
#include <algorithm>
147+
148+
int main()
149+
{
150+
msd::static_channel<int, 2> src{};
151+
msd::static_channel<int, 2> dst{};
163152

164-
int in = 1;
165-
int out = 0;
153+
src.write(1);
154+
src.write(2);
155+
src.close();
166156

167-
// Send to channel
168-
chan.write(in);
169-
chan.write(in);
157+
std::copy_if(src.begin(), src.end(), msd::back_inserter(dst), [](int value) { return value % 2 == 0; });
170158

171-
// Read from channel
172-
chan.read(out);
173-
chan.read(out);
174-
chan.read(out); // Blocks because the channel is empty (and no one writes on it)
159+
dst.size(); // 1
175160
}
176161
```
177162

178-
See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples) and [documentation](https://andreiavrammsd.github.io/cpp-channel/).
163+
See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples) and [tests](https://github.com/andreiavrammsd/cpp-channel/tree/master/tests). Read the [documentation](https://andreiavrammsd.github.io/cpp-channel/) for full API reference.
179164

180165
## Known limitations
181166

examples/.clang-tidy

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,4 @@ InheritParentConfig: true
22

33
Checks: >
44
-cppcoreguidelines-avoid-magic-numbers,
5-
-readability-magic-numbers,
6-
-fuchsia-default-arguments-calls
7-
8-
CheckOptions:
9-
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
10-
- { key: readability-identifier-length.MinimumVariableNameLength, value: '1' }
5+
-readability-magic-numbers

examples/CMakeLists.txt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ function(add_example NAME)
1313
endfunction()
1414

1515
function(run_example NAME)
16-
add_custom_command(
17-
TARGET ${NAME}
18-
POST_BUILD
16+
add_custom_target(
17+
run_${NAME}
1918
COMMAND ${NAME}
19+
DEPENDS ${NAME}
2020
COMMENT "Running example: ${NAME}")
21+
22+
add_dependencies(run_examples run_${NAME})
2123
endfunction()
2224

2325
add_custom_target(examples)
26+
add_custom_target(run_examples)
2427

2528
# Examples
2629
add_example(example_basic basic.cpp)
@@ -40,5 +43,8 @@ run_example(example_streaming)
4043
add_example(example_multithreading_static_channel multithreading_static_channel.cpp)
4144
run_example(example_multithreading_static_channel)
4245

43-
add_example(example_merge_channels merge_channels.cpp)
44-
run_example(example_merge_channels)
46+
add_example(example_concurrent_map_filter concurrent_map_filter.cpp)
47+
run_example(example_concurrent_map_filter)
48+
49+
add_example(example_semaphore semaphore.cpp)
50+
run_example(example_semaphore)

examples/close.cpp

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
#include <future>
44
#include <iostream>
55
#include <mutex>
6-
#include <string>
6+
#include <sstream>
77
#include <thread>
88

99
int main()
1010
{
1111
msd::channel<int> channel{};
1212

1313
// Write data on the channel until it's closed
14-
const auto input = [](msd::channel<int>& chan, int time_ms) {
14+
const auto produce = [](msd::channel<int>& chan, int time_ms) {
1515
static int inc = 0;
1616

1717
while (!chan.closed()) {
@@ -20,37 +20,39 @@ int main()
2020
std::this_thread::sleep_for(std::chrono::milliseconds{time_ms});
2121
}
2222
};
23-
const auto input_future = std::async(input, std::ref(channel), 10);
23+
const auto producer = std::async(produce, std::ref(channel), 10);
2424

2525
// Close the channel after some time
26-
const auto timeout = [](msd::channel<int>& chan, int time_ms) {
26+
const auto close = [](msd::channel<int>& chan, int time_ms) {
2727
std::this_thread::sleep_for(std::chrono::milliseconds{time_ms});
2828

2929
chan.close();
3030
};
31-
auto timeout_future = std::async(timeout, std::ref(channel), 100);
31+
const auto closer = std::async(close, std::ref(channel), 100);
3232

3333
// Display all the data from the channel
3434
// When the channel is closed and empty, the iteration will end
35-
std::mutex cout_mutex;
35+
std::mutex mutex;
3636

37-
const auto write = [&cout_mutex](msd::channel<int>& chan, int time_ms) {
38-
for (auto out : chan) {
39-
std::string msg{"out: " + std::to_string(out) + "\n"};
37+
const auto consume = [&mutex](msd::channel<int>& chan, int time_ms) {
38+
for (auto value : chan) {
39+
std::stringstream stream;
40+
stream << "value " << value << " from consumer " << std::this_thread::get_id() << '\n';
4041

4142
{
42-
std::lock_guard<std::mutex> lock(cout_mutex);
43-
std::cout << msg;
43+
std::lock_guard<std::mutex> lock(mutex);
44+
45+
std::cout << stream.str();
4446
}
4547

4648
std::this_thread::sleep_for(std::chrono::milliseconds{time_ms});
4749
}
4850
};
49-
const auto write_future1 = std::async(write, std::ref(channel), 1);
50-
const auto write_future2 = std::async(write, std::ref(channel), 100);
51+
const auto consumer_1 = std::async(consume, std::ref(channel), 50);
52+
const auto consumer_2 = std::async(consume, std::ref(channel), 10);
5153

52-
input_future.wait();
53-
timeout_future.wait();
54-
write_future1.wait();
55-
write_future2.wait();
54+
producer.wait();
55+
closer.wait();
56+
consumer_1.wait();
57+
consumer_2.wait();
5658
}

0 commit comments

Comments
 (0)