-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
348 lines (287 loc) · 14.8 KB
/
main.cpp
File metadata and controls
348 lines (287 loc) · 14.8 KB
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#include <iostream>
#include <functional>
#include <string>
#include <vector>
#include <memory>
#include <cassert>
// To educate me on std::function I created this example to demonstrates all the ways to use std::function and raw
// function pointers in modern C++20
//
// No classes are used, only free functions, lambdas, and function objects via structs
// ==================== PART 1: FUNCTION DECLARATIONS ====================
// Regular free function that will be used with both raw function pointers and std::function
bool freeFunction(const int x, const double y) {
std::cout << "Free function called with " << x << " and " << y << std::endl;
return x > static_cast<int>(y);
}
// Overloaded free function - demonstrates how std::function can help disambiguate
bool freeFunction(const std::string& str) {
std::cout << "Overloaded free function called with: " << str << std::endl;
return !str.empty();
}
// Variadic function template - can be used with specific instantiations
template<typename... Args>
bool variadicFunction(Args... args) {
std::cout << "Variadic function with " << sizeof...(args) << " arguments" << std::endl;
return sizeof...(args) > 0;
}
// Function that takes a raw function pointer
bool takesRawFunctionPointer(bool (*funcPtr)(int, double), int a, double b) {
std::cout << "Called via raw function pointer" << std::endl;
return funcPtr(a, b);
}
// Function that takes std::function
bool takesStdFunction(const std::function<bool(int, double)>& func, int a, double b) {
std::cout << "Called via std::function" << std::endl;
return func(a, b);
}
// Function returning a raw function pointer
bool (*getFunctionPointer())(int, double) {
return &freeFunction;
}
// Function returning std::function
std::function<bool(int, double)> getStdFunction() {
// Need to disambiguate the overloaded freeFunction
return static_cast<bool(*)(int, double)>(&freeFunction);
}
// ==================== PART 2: MAIN FUNCTION WITH EXAMPLES ====================
int main() {
std::cout << "===== MODERN C++20 FUNCTION EXAMPLES =====" << std::endl;
// ========== RAW FUNCTION POINTERS ==========
std::cout << "\n----- RAW FUNCTION POINTERS -----\n" << std::endl;
// 1. Basic function pointer declaration and assignment
bool (*rawFuncPtr)(int, double) = &freeFunction;
// The & is optional in most cases, but recommended for clarity
// 2. Calling a function through a raw function pointer
bool result1 = rawFuncPtr(5, 3.5);
std::cout << "Result 1: " << std::boolalpha << result1 << std::endl;
// 3. Function pointer initialization without explicit type - need to disambiguate overloaded function
auto rawFuncPtrAuto = static_cast<bool(*)(int, double)>(&freeFunction);
bool result2 = rawFuncPtrAuto(10, 7.5);
std::cout << "Result 2: " << result2 << std::endl;
// 4. Using typedef to simplify function pointer types
typedef bool (*FunctionPointerType)(int, double);
FunctionPointerType typedeffedFuncPtr = &freeFunction;
bool result3 = typedeffedFuncPtr(10, 7.5);
std::cout << "Result 3: " << result3 << std::endl;
// 5. Using alias (more modern than typedef)
using ModernFuncPtrType = bool(*)(int, double);
ModernFuncPtrType modernFuncPtr = &freeFunction;
bool result_mod = modernFuncPtr(10, 7.5);
std::cout << "Result Modern Type: " << result_mod << std::endl;
// 6. Storing function pointers in an array
bool (*funcPtrArray[3])(int, double) = {&freeFunction, &freeFunction, &freeFunction};
for (int i = 0; i < 3; ++i) {
bool result_array = funcPtrArray[i](1, i + 0.5);
std::cout << "Result Modern Type: (" << i << ") " << result_array << std::endl;
}
// 7. Passing function pointers to other functions
bool result_takes = takesRawFunctionPointer(rawFuncPtr, 15, 10.5);
std::cout << "Result Takes Type:" << result_takes << std::endl;
// 8. Getting function pointers from functions
bool (*retrievedFuncPtr)(int, double) = getFunctionPointer();
bool result_retrieved = retrievedFuncPtr(20, 15.5);
std::cout << "Result Retrieved Type:" << result_retrieved << std::endl;
// 9. Function pointers as template arguments
std::vector<bool(*)(int, double)> funcPtrVector;
funcPtrVector.push_back(static_cast<bool(*)(int, double)>(&freeFunction));
// 10. Null function pointer check
bool (*nullFuncPtr)(int, double) = nullptr;
if (nullFuncPtr == nullptr) {
std::cout << "Function pointer is null" << std::endl;
}
// ========== STD::FUNCTION ==========
std::cout << "\n----- STD::FUNCTION EXAMPLES -----\n" << std::endl;
// 1. Basic std::function creation - need to disambiguate overloaded function
std::function<bool(int, double)> stdFunc = static_cast<bool(*)(int, double)>(&freeFunction);
bool result4 = stdFunc(25, 20.5);
std::cout << "Result 4: " << result4 << std::endl;
// 2. std::function with lambda (stateless)
std::function<bool(int, double)> lambdaFunc = [](int x, double y) {
std::cout << "Stateless lambda called with " << x << " and " << y << std::endl;
return x < static_cast<int>(y);
};
bool result_lambda1 = lambdaFunc(30, 25.5);
std::cout << "Lambda 1 " << result_lambda1 << std::endl;
// 3. std::function with stateful lambda (with captures)
int multiplier = 2;
std::function<bool(int, double)> statefulLambda = [multiplier](int x, double y) {
std::cout << "Stateful lambda with multiplier=" << multiplier << std::endl;
return (x * multiplier) > y;
};
bool result_lambda2 = statefulLambda(5, 15.0);
std::cout << "Lambda 2 " << result_lambda2 << std::endl;
// 4. std::function with a function object (functor via struct)
struct Functor {
bool operator()(int x, double y) const {
std::cout << "Functor called with " << x << " and " << y << std::endl;
return x == static_cast<int>(y);
}
};
std::function<bool(int, double)> functorFunc = Functor{};
bool result_lambda3 = functorFunc(35, 35.0);
std::cout << "Lambda 3 " << result_lambda3 << std::endl;
// 5. std::function with std::bind - binding a free function
// Need to disambiguate overloaded function for bind
auto boundFunc = std::bind(static_cast<bool(*)(int, double)>(&freeFunction),
std::placeholders::_1, 100.5);
std::function<bool(int)> bindFunc = boundFunc;
bool result_bind = bindFunc(40);
std::cout << "Bind " << result_bind << std::endl;
// 6. std::function with std::bind with reordered parameters
auto reorderedBindFunc = std::bind(static_cast<bool(*)(int, double)>(&freeFunction),
std::placeholders::_2, // Second arg becomes first
std::placeholders::_1); // First arg becomes second
std::function<bool(double, int)> reorderedFunc = reorderedBindFunc;
bool result_reorder = reorderedFunc(45.5, 50);
std::cout << "Bind " << result_reorder << std::endl;
// 7. std::function with std::bind to bind some parameters
auto partialBindFunc = std::bind(static_cast<bool(*)(int, double)>(&freeFunction), 60, std::placeholders::_1);
std::function<bool(double)> partialFunc = partialBindFunc;
bool result_partial = partialFunc(55.5);
std::cout << "Bind partial" << result_partial << std::endl;
// 8. std::function to store overloaded function (explicit disambiguation)
std::function<bool(const std::string&)> overloadedFunc =
static_cast<bool(*)(const std::string&)>(&freeFunction);
bool result_overload = overloadedFunc("Hello, std::function!");
std::cout << "Overload " << result_overload << std::endl;
// 9. Empty std::function check
std::function<void()> emptyFunc;
if (!emptyFunc) {
std::cout << "std::function is empty" << std::endl;
}
// 10. Assigning to an empty std::function
emptyFunc = []() { std::cout << "No longer empty" << std::endl; };
if (emptyFunc) {
emptyFunc();
}
// 11. std::function with variadic template function
std::function<bool(int, double, std::string)> variadicFunc =
variadicFunction<int, double, std::string>;
bool result_variadic = variadicFunc(70, 65.5, "Variadic");
std::cout << "Variadic " << result_variadic << std::endl;
// 12. Passing std::function to functions
bool result_stdfunc = takesStdFunction(stdFunc, 75, 70.5);
std::cout << "std::func " << result_stdfunc << std::endl;
// 13. Getting std::function from functions
std::function<bool(int, double)> retrievedStdFunc = getStdFunction();
bool result_retrieved2 = retrievedStdFunc(80, 75.5);
std::cout << "retrieved 2 " << result_retrieved2 << std::endl;
// 14. Storing std::function in containers
std::vector<std::function<bool(int, double)>> funcVector;
funcVector.push_back(static_cast<bool(*)(int, double)>(&freeFunction));
funcVector.push_back(lambdaFunc);
funcVector.push_back(statefulLambda);
funcVector.push_back(functorFunc);
for (const auto& func : funcVector) {
bool result = func(85, 80.5);
std::cout << "funcVector " << result << std::endl;
}
// 15. Nested std::function (function returning function)
std::function<std::function<bool(double)>(int)> nestedFunc =
[](int x) -> std::function<bool(double)> {
return [x](double y) {
std::cout << "Nested function with " << x << " and " << y << std::endl;
return x > static_cast<int>(y);
};
};
auto innerFunc = nestedFunc(90);
bool result_nested = innerFunc(85.5);
std::cout << "funcVector " << result_nested << std::endl;
// ========== ADVANCED EXAMPLES ==========
std::cout << "\n----- ADVANCED EXAMPLES -----\n" << std::endl;
// 1. Function pointer to a function template instantiation
bool (*templateFuncPtr)(int, double, std::string) = &variadicFunction<int, double, std::string>;
bool result_template_fptr = templateFuncPtr(95, 90.5, "Template");
std::cout << "function template " << result_template_fptr << std::endl;
// 2. std::function storing a mutable lambda
int counter = 0;
std::function<bool()> mutableLambda = [counter]() mutable {
std::cout << "Mutable lambda counter: " << ++counter << std::endl;
return counter > 5;
};
for (int i = 0; i < 3; ++i) {
bool result_mut_lambda = mutableLambda();
std::cout << "function template " << result_mut_lambda << std::endl;
}
// 3. std::function with std::ref to capture by reference
int externalState = 100;
std::function<void()> refCaptureLambda = [&externalState]() {
std::cout << "Before modification: " << externalState << std::endl;
externalState += 10;
std::cout << "After modification: " << externalState << std::endl;
};
refCaptureLambda();
// 4. Using std::bind with std::ref
int bindRefValue = 200;
auto bindWithRef = std::bind([](int& x, const double y) -> bool {
std::cout << "Bind with ref before: " << x << std::endl;
x += 20;
std::cout << "Bind with ref after: " << x << std::endl;
return x > y;
}, std::ref(bindRefValue), std::placeholders::_1);
std::function<bool(double)> bindRefFunc = bindWithRef;
bool result_bindref = bindRefFunc(205.5);
std::cout << "result_bindref after call: " << result_bindref << std::endl;
// 5. Storing generic lambda (C++14 and later)
// Note: We cannot directly create a std::function with auto parameters,
// so we store specific instantiations instead
auto genericLambda = [](auto x, auto y) {
std::cout << "Generic lambda with " << x << " and " << y << std::endl;
return sizeof(x) > sizeof(y);
};
// Use the generic lambda with specific types
bool result_generic1 = genericLambda(100, 99.5);
std::cout << "result_generic1 after call: " << result_generic1 << std::endl;
bool result_generic2 = genericLambda("hello", 123);
std::cout << "result_generic2 after call: " << result_generic2 << std::endl;
// Store specific instantiations in std::function
std::function<bool(int, double)> specificGenericLambda =
[](auto x, auto y) {
std::cout << "Specific generic lambda with " << x << " and " << y << std::endl;
return sizeof(x) < sizeof(y);
};
bool result_specificGenericLambda = specificGenericLambda(210, 205.5);
std::cout << "result_specificGenericLambda after call: " << result_specificGenericLambda << std::endl;
// 6. Using C++20 std::bind_front (simpler than std::bind)
auto bindFrontFunc = std::bind_front(static_cast<bool(*)(int, double)>(&freeFunction), 220);
std::function<bool(double)> bindFrontStdFunc = bindFrontFunc;
bool result_bindFrontStdFunc = bindFrontStdFunc(215.5);
std::cout << "result_bindFrontStdFunc after call: " << result_bindFrontStdFunc << std::endl;
// 7. std::function with noexcept lambda (C++17 and later)
std::function<bool(int, double)> noexceptLambda = [](int x, double y) noexcept {
std::cout << "Noexcept lambda with " << x << " and " << y << std::endl;
return x >= static_cast<int>(y);
};
bool result_noexceptLambda = noexceptLambda(230, 225.5);
std::cout << "noexceptLambda after call: " << result_noexceptLambda << std::endl;
// 8. Smart pointer to hold dynamically allocated std::function
auto funcPtr = std::make_unique<std::function<bool(int, double)>>(
[](int x, double y) {
std::cout << "Function in smart pointer with " << x << " and " << y << std::endl;
return x != static_cast<int>(y);
}
);
bool result_funcptr = (*funcPtr)(240, 235.5);
std::cout << "result_funcptr after call: " << result_funcptr << std::endl;
// ========== PERFORMANCE CONSIDERATIONS ==========
std::cout << "\n----- PERFORMANCE CONSIDERATIONS -----\n" << std::endl;
// Raw function pointers are more lightweight than std::function
std::cout << "Size of raw function pointer: "
<< sizeof(bool(*)(int, double)) << " bytes" << std::endl;
std::cout << "Size of std::function: "
<< sizeof(std::function<bool(int, double)>) << " bytes" << std::endl;
// The difference is because std::function uses type erasure and may allocate memory
// ========== C++20 FEATURES ==========
std::cout << "\n----- C++20 FEATURES -----\n" << std::endl;
// 1. C++20 requires expression for disambiguating function overloads
auto overloadedFuncPtr = +[](const int x, const double y) {
return freeFunction(x, y);
};
std::function<bool(int, double)> requiresFunc = overloadedFuncPtr;
bool result_requiresFunc = requiresFunc(250, 245.5);
std::cout << "result_requiresFunc after call: " << result_requiresFunc << std::endl;
// Note: We also previously used std::bind_front which is a C++20 feature
std::cout << "\n===== END OF EXAMPLES =====" << std::endl;
return 0;
}