C++ Library Extensions 2022.12.09
To help learn modern C++ programming
011-passing_parameter.cpp
Go to the documentation of this file.
1#include <tpf_output.hpp>
2#include <future>
3#include <thread>
4#include <memory>
5
6/*
7 If you are my audience, do NOT pollute global scope.
8 Instead use static member fields or static member functions.
9
10 Always enclose multithreading code in try-catch construct.
11
12 Please understand the intentions of the C++ Standard Committee.
13 If you ever have to pass the parameter using call-by-reference semantic,
14 use std::cref() for const reference,
15 use std::ref() for non-const reference.
16 In such case, the lifetime of the parameter should
17 OUTLIVE the scope of the thread function.
18
19 We cannot PASS std::unique_ptr or std::shared_ptr
20 to a thread function in NORMAL function-call syntax.
21
22 Because the smart pointers such as
23 std::unique_ptr and std::shared_ptr do not have copy-constructor,
24 and std::async's function-call parameters should be
25 convertible to Rvalue Reference,
26 So, we can try it using std::move. std::move() typecasts to
27 rvalue reference. After std::move(), regard that parameter
28 as INVALID.
29
30 If we want to use the smart pointers such as std::unique_ptr,
31 std::shared_ptr after passing to a thread function,
32 then we can pass the smart pointer using call-by-reference.
33
34 NOTE THAT:
35 We are passing parameters from one thread to another thread.
36 We will learn more about fundamentals of C++ Threading Technology in future sessions.
37
38*/
39
41{
42 using return_t = int;
43
44 static return_t call_by_value_sum(int a, int b)
45 {
46 return a + b;
47 }
48
49 static return_t call_by_const_reference_value_sum(const int& a, const int& b)
50 {
51 return a + b;
52 }
53
54 static return_t call_by_reference_sum(int& a, int& b)
55 {
56 return a + b;
57 }
58
59 static return_t call_by_pointer(int* ap, int* bp)
60 {
61 return (*ap) * (*bp);
62 }
63
64 static return_t call_by_unique_ptr(std::unique_ptr<int> ua,
65 std::unique_ptr<int> ub)
66 {
67 return (*ua) / (*ub);
68 }
69
70 static return_t call_by_shared_ptr(std::shared_ptr<int> sa,
71 std::shared_ptr<int> sb)
72 {
73 return (*sa) / (*sb);
74 }
75
77 {
78 std::unique_ptr<int> ua{ra};
79 std::unique_ptr<int> ub{rb};
80
81 return *ua + *ub;
82
83 // after this block, memory for ra and rb are deleted.
84 }
85
86 static return_t passing_unique_ptr_by_reference(std::unique_ptr<int>& ua,
87 std::unique_ptr<int>& ub)
88 {
89 return (*ua) * (*ub);
90 }
91};
92
94{
95 try
96 {
98
99 auto call_by_value_sum = std::async(&sld::call_by_value_sum, 1, 2);
100
101
102 int n = 4, m = 5;
103
104 // this is okay, but it is call-by-value, not call-by-const-reference
105 // auto call_by_const_reference_value_sum = std::async(&sld::call_by_const_reference_value_sum, n, m);
106
107 // we are now passing n and m by call_by_const_reference
108 auto call_by_const_reference_value_sum =
109 std::async(&sld::call_by_const_reference_value_sum, std::cref(n), std::cref(m));
110
111 // this does not compile
112 // auto call_by_reference_sum = std::async(&sld::call_by_reference_sum, n, m);
113
114 // Passing parameters call-by-reference is A DANGEROUS PRACETICE
115 // So, C++ committee wanted C++ programmers pay special attention
116 // when passing parameters by reference to thread functions.
117 //
118 // But still we may need to pass parameters to thread functions
119 // using call-by-reference semantic. To address this issue,
120 // C++ committee created std::cref() for const reference,
121 // std::ref() for non-const reference
122 // to pass parameter call-by-reference semantic
123
124 // now we are passing n and m using call-by-reference
125 auto call_by_reference_sum =
126 std::async(&sld::call_by_reference_sum, std::ref(n), std::ref(m));
127
129
130 stream << "call_by_value_sum with 1, 2: "
131 << call_by_value_sum.get() << tpf::endl;
132
133 stream << "call_by_const_reference_value_sum with 4, 5: "
134 << call_by_const_reference_value_sum.get() << tpf::endl;
135
136 stream << "call_by_reference_sum with 4, 5: "
137 << call_by_reference_sum.get() << tpf::endl;
138
139 auto call_by_pointer = std::async( &sld::call_by_pointer, &n, &m);
140
141 std::unique_ptr<int> ua{new int{12}};
142 std::unique_ptr<int> ub{new int{6}};
143
144 auto call_by_unique_ptr = std::async(&sld::call_by_unique_ptr,
145 std::move(ua), std::move(ub));
146
147 std::shared_ptr<int> sa{new int{12}};
148 std::shared_ptr<int> sb{new int{6}};
149
150 auto call_by_shared_ptr =
151 std::async(&sld::call_by_shared_ptr, std::move(sa), std::move(sb));
152
153 stream << "call_by_pointer with 4, 5: "
154 << call_by_pointer.get() << tpf::endl;
155
156 stream << "call_by_unique_ptr with 12, 6: "
157 << call_by_unique_ptr.get() << tpf::endl;
158
159 /*
160 If you pass unique_ptr to another function,
161 that pass parameter is INVALID after the function call.
162 So, at this point of the code
163
164 Dereferencing unque_ptr after passing to another function,
165 that unique_ptrs are INVALID.
166
167 *ua, *ub are INVALID operation.
168
169 In case of unique_ptr, after std::move(),
170 the parameter is INVALID.
171 */
172
173 stream << "call_by_shared_ptr with 12, 6: "
174 << call_by_shared_ptr.get() << tpf::endl;
175
176 /*
177 If you want to access the object the pointer is pointing to,
178 you have to pass the pointer using shared_ptr.
179
180 So, after passing shared_ptr, you can still access that parameter.
181
182 After passing shared_ptrs to another function,
183 you can still access that shared_ptrs.
184
185 *sa, *sb are VALID.
186
187 In case of shared_ptr, Yes, shared_ptr also has move constructor,
188 so, after std::move(shared_ptr), that parameter is also INVALID.
189
190 */
191
192
193 // this is okay, no memory leaks
194 auto passing_raw_pointer_the_better_way =
195 std::async( &sld::passing_raw_pointer_the_better_way, new int{5}, new int{4});
196
197 stream << "passing_raw_pointer_the_better_way with 5, 4: "
198 << passing_raw_pointer_the_better_way.get() << tpf::endl;
199
200
201 std::unique_ptr<int> uua{ new int{5} };
202 std::unique_ptr<int> uub{ new int{4} };
203
204 auto passing_unique_ptr_by_reference =
205 std::async( &sld::passing_unique_ptr_by_reference, std::ref(uua), std::ref(uub));
206
207 stream << "passing_unique_ptr_by_reference with 12, 6: "
208 << passing_unique_ptr_by_reference.get() << tpf::endl;
209
210 /*
211 *ua, *ub are still VALID.
212 Because we passed ua and ub as lvalue reference,
213 we have not disowned ua and ub using std::move().
214 */
215
216 *uua = 10;
217 *uub = 2;
218
219 stream <<"*uua, *uub are still VALID operation: " << (*uua + *uub) << tpf::endl;
220 }
221 catch(const std::exception& e)
222 {
224 stream << e << tpf::endl;
225 }
226
227}
228
229int main()
230{
232}
void examples_for_passing_parameters()
int main()
reference_wrapper< Type > ref(Type &val) noexcept
tpf::sstream stream
constexpr auto endl
Definition: tpf_output.hpp:973
static return_t passing_unique_ptr_by_reference(std::unique_ptr< int > &ua, std::unique_ptr< int > &ub)
static return_t call_by_const_reference_value_sum(const int &a, const int &b)
static return_t call_by_reference_sum(int &a, int &b)
static return_t call_by_unique_ptr(std::unique_ptr< int > ua, std::unique_ptr< int > ub)
static return_t call_by_shared_ptr(std::shared_ptr< int > sa, std::shared_ptr< int > sb)
static return_t call_by_pointer(int *ap, int *bp)
static return_t call_by_value_sum(int a, int b)
static return_t passing_raw_pointer_the_better_way(int *ra, int *rb)
Stream output operators << are implemented.