-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy path03_pipeline_types.cpp
142 lines (124 loc) · 4.9 KB
/
03_pipeline_types.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <string_view>
#include "tdp/pipeline.hpp"
//---------------------------------------------------------------------------------------------------------------------
// The Darkest Pipeline provides two ways of declaring inputs and two ways of declaring outputs.
// From those, we can do a lot. Let's take a look.
//---------------------------------------------------------------------------------------------------------------------
// We've used functions and lambdas in the previous examples.
// We can also use functor classes as pipeline stages!
template <typename T>
struct get {
T operator()() {
// Simulating a thread that produces a result every 10ms
using namespace std::chrono_literals;
std::this_thread::sleep_for(10ms);
return {};
}
};
struct add {
// This function adds any two numbers
template <typename T1, typename T2>
auto operator()(T1 a, T2 b) {
return a + b;
}
};
struct square {
// This function takes the square of a number
template <typename T>
auto operator()(T x) {
return x * x;
}
};
template <typename T>
struct consume {
// This function returns void. It's going to be used as the end of a pipeline
void operator()(T) {}
};
// This function will show us its name, as seen by the compiler.
// This will help us understand the nature of pipelines.
template <typename... Ts>
void print_type(Ts&...);
int main() {
// Pipelines using manual input and output
auto p0 = tdp::input<int, int> >> add() >> tdp::output;
auto p1 = tdp::input<double> >> square() >> tdp::output;
auto p2 = tdp::input<int, int> >> add() >> square() >> tdp::output;
// Pipeline using a producer and polled output
// The "Producer threads" example shows how you can control production.
auto p3 = tdp::producer{get<int>()} >> square() >> tdp::output;
// You can't use a pipeline as input>>output, but can use a producer to generate output!
auto p4 = tdp::producer{get<int>()} >> tdp::output;
// Pipelines using user-generated input and a consumer
// The consumer processes the data, and no output given.
auto p5 = tdp::input<int> >> tdp::consumer{consume<int>()};
auto p6 = tdp::input<double, double> >> add() >> square() >> tdp::consumer{consume<double>()};
// Producer-consumer pipelines.
// We don't need to provide input OR output, it just runs until destruction.
// See more details on the "Producers with Consumers" example.
auto p7 = tdp::producer{get<int>()} >> square() >> tdp::consumer{consume<int>()};
auto p8 = tdp::producer{get<double>()} >> tdp::consumer{consume<double>()};
// We can set policies to our pipelines!
// See the Execution Policies example for more details.
auto p9 = tdp::input<int, int> >> add() >> square() >> tdp::output / tdp::policy::queue;
auto p10 = tdp::input<int> >> square() >> tdp::output / tdp::policy::triple_buffer;
// Here, we see the underlying types utilized. And why we should use auto in this library.
print_type(p1);
print_type(p2);
print_type(p3);
print_type(p4);
print_type(p5);
print_type(p6);
print_type(p7);
print_type(p8);
print_type(p9);
print_type(p10);
}
//---------------------------------------------------------------------------------------------------------------------
// Don't worry about this function. It's not part of the example, only its output is.
//
// This was tested on GCC 7/8 (Linux), Clang 8/9 (Linux) and MSVC 2017/2019 (Windows).
// Also works with Clang + VS 2019. Guaranteed not to work on Clang + VS 2017.
// Come on, don't look at me with that face. It's an example, you're probably not even running it.
//---------------------------------------------------------------------------------------------------------------------
template <typename... Ts>
void print_type(Ts&...) {
#ifdef _MSC_VER
constexpr const char* name = __FUNCSIG__;
constexpr char first_char = '(';
constexpr char last_char = ')';
constexpr auto offset = 1u;
constexpr auto offset_r = 2u;
#elif defined(__clang__)
constexpr const char* name = __PRETTY_FUNCTION__;
constexpr char first_char = '<';
constexpr char last_char = ']';
constexpr auto offset = 1u;
constexpr auto offset_r = 1u;
#elif defined(__GNUC__)
const char* name = __PRETTY_FUNCTION__;
constexpr char first_char = '{';
constexpr char last_char = '}';
constexpr auto offset = 1u;
constexpr auto offset_r = 0u;
#else
#error "Compiler not supported for this example."
#endif
auto find_char = [=](char c) {
const char* pos = name;
for (; *pos != c && *pos != '\0'; pos++) {
}
return pos;
};
const char* name_begin = find_char(first_char);
const char* name_end = find_char(last_char);
if (*name_begin == '\0') {
std::cout << ". " << name << "\n";
} else if (*name_end == '\0') {
std::cout << ". " << name_begin << "\n";
} else {
name_begin += offset;
name_end -= offset_r;
std::cout << "- " << std::string_view{name_begin, std::size_t(name_end - name_begin)} << "\n";
}
}