├── 10 ├── cxx20_views.cpp ├── cxx20_views_bad_transform.cpp └── transparent_hash.cpp ├── 11 └── string.hpp ├── 12 ├── chrono_cxx20.cpp ├── chrono_measure.cpp ├── random_ints.cpp ├── regex_email.cpp ├── regex_iterator_email.cpp ├── shuffle_vector.cpp ├── std_literals.cpp ├── tuple.cpp └── user_literals.cpp ├── 14 ├── expected_error_code.cpp ├── expected_simple.cpp ├── filesystem_error.cpp └── optional_expected_rvo.cpp ├── 16 ├── async_future.cpp ├── bad_threaded_increment.cpp ├── cond_var.cpp ├── memory_reordering.cpp ├── packaged_task.cpp ├── packaged_task_sync.cpp ├── promise.cpp ├── thread.cpp └── thread_local_lifetime.cpp ├── 17 ├── asio.h ├── asio_post.cpp ├── asio_post_mt.cpp ├── asio_timer.cpp ├── echo_server_async.cpp ├── echo_server_coroutine.cpp ├── echo_server_mt.cpp ├── echo_server_st.cpp ├── fake_io_context.cpp ├── fake_io_context.h ├── fibonacci_generator.cpp ├── fibonacci_manual_iteration.cpp └── stream_client.cpp ├── 18 ├── clang-tidy-demo.cpp └── past_the_end_iterator.cpp ├── .clang-format ├── .clang-tidy ├── 01 └── string.hpp ├── 02 ├── stack_unwind.cpp └── temporary_object.cpp ├── 03 └── rvo.cpp ├── 04 ├── .clang-tidy ├── iteration_overload.cpp └── iteration_unified_98.cpp ├── 06 ├── counted_ops.cpp ├── function_object.cpp └── function_ref_ptr.cpp ├── 07 ├── associative_containers.cpp ├── hash.cpp ├── list_member_func.cpp ├── obj_set.cpp ├── priority_queue.cpp ├── queue.cpp ├── stack.cpp ├── unordered_associative_containers.cpp ├── vector.cpp └── vector_move.cpp ├── 08 ├── null_sentinel.cpp └── reverse_iterate.cpp ├── 09 ├── copy_sort_cxx11.cpp ├── copy_sort_cxx20.cpp ├── max_min.cpp ├── par_reduce.cpp ├── set_erase_if.cpp └── sort_projection.cpp ├── README.md └── common ├── finally.h ├── ostream_range.h └── scoped_thread.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # BasedOnStyle: WebKit 3 | AccessModifierOffset: -4 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlinesLeft: false 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: Empty 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterDefinitionReturnType: None 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: false 18 | AlwaysBreakTemplateDeclarations: Yes 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BraceWrapping: 22 | AfterClass: false 23 | AfterControlStatement: false 24 | AfterEnum: false 25 | AfterFunction: true 26 | AfterNamespace: false 27 | AfterObjCDeclaration: false 28 | AfterStruct: false 29 | AfterUnion: false 30 | BeforeCatch: true 31 | BeforeElse: false 32 | IndentBraces: false 33 | SplitEmptyRecord: false 34 | BreakBeforeBinaryOperators: false 35 | BreakBeforeBraces: Custom 36 | BreakBeforeTernaryOperators: true 37 | BreakConstructorInitializers: BeforeColon 38 | ColumnLimit: 67 39 | CommentPragmas: '^ IWYU pragma:' 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 41 | ConstructorInitializerIndentWidth: 4 42 | ContinuationIndentWidth: 4 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: false 45 | ExperimentalAutoDetectBinPacking: false 46 | FixNamespaceComments: true 47 | IncludeCategories: 48 | - Regex: '^<.*/.*>$' 49 | Priority: 3 50 | - Regex: '^<.*\.h>$' 51 | Priority: 2 52 | - Regex: '^<' 53 | Priority: 1 54 | - Regex: '.*' 55 | Priority: 4 56 | IndentCaseLabels: false 57 | IndentWidth: 4 58 | IndentWrappedFunctionNames: false 59 | KeepEmptyLinesAtTheStartOfBlocks: false 60 | MacroBlockBegin: '' 61 | MacroBlockEnd: '' 62 | MaxEmptyLinesToKeep: 1 63 | NamespaceIndentation: None 64 | ObjCSpaceAfterProperty: true 65 | ObjCSpaceBeforeProtocolList: true 66 | PenaltyBreakBeforeFirstCallParameter: 19 67 | PenaltyBreakComment: 300 68 | PenaltyBreakFirstLessLess: 120 69 | PenaltyBreakString: 1000 70 | PenaltyExcessCharacter: 1000000 71 | PenaltyReturnTypeOnItsOwnLine: 60 72 | PointerAlignment: Left 73 | ReflowComments: true 74 | SortIncludes: true 75 | SpaceAfterCStyleCast: false 76 | SpaceBeforeAssignmentOperators: true 77 | SpaceBeforeParens: ControlStatements 78 | SpaceInEmptyParentheses: false 79 | SpacesBeforeTrailingComments: 2 80 | SpacesInAngles: false 81 | SpacesInContainerLiterals: true 82 | SpacesInCStyleCastParentheses: false 83 | SpacesInParentheses: false 84 | SpacesInSquareBrackets: false 85 | Standard: c++20 86 | TabWidth: 4 87 | UseTab: Never 88 | ... 89 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: 3 | bugprone-*, 4 | -bugprone-easily-swappable-parameters, 5 | -bugprone-sizeof-container, 6 | clang-diagnostic-*, 7 | clang-analyzer-*, 8 | cppcoreguidelines-*, 9 | -cppcoreguidelines-avoid-c-arrays, 10 | -cppcoreguidelines-avoid-do-while, 11 | -cppcoreguidelines-avoid-magic-numbers, 12 | -cppcoreguidelines-avoid-non-const-global-variables, 13 | -cppcoreguidelines-macro-usage, 14 | -cppcoreguidelines-missing-std-forward, 15 | -cppcoreguidelines-no-malloc, 16 | -cppcoreguidelines-owning-memory, 17 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 18 | -cppcoreguidelines-pro-bounds-constant-array-index, 19 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 20 | -cppcoreguidelines-pro-type-const-cast, 21 | -cppcoreguidelines-pro-type-reinterpret-cast, 22 | -cppcoreguidelines-pro-type-static-cast-downcast, 23 | -cppcoreguidelines-pro-type-vararg, 24 | llvm-namespace-comment, 25 | modernize-*, 26 | -modernize-avoid-c-arrays, 27 | -modernize-deprecated-headers, 28 | -modernize-return-braced-init-list, 29 | -modernize-use-nodiscard, 30 | -modernize-use-trailing-return-type, 31 | -modernize-use-transparent-functors, 32 | -modernize-use-using, 33 | readability-*, 34 | -readability-convert-member-functions-to-static, 35 | -readability-else-after-return, 36 | -readability-identifier-length, 37 | -readability-implicit-bool-conversion, 38 | -readability-inconsistent-declaration-parameter-name, 39 | -readability-magic-numbers, 40 | -readability-make-member-function-const, 41 | -readability-named-parameter, 42 | -readability-qualified-auto, 43 | -readability-suspicious-call-argument 44 | WarningsAsErrors: '' 45 | FormatStyle: file 46 | ExtraArgs: ['-std=c++20'] 47 | CheckOptions: 48 | - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays 49 | value: 'true' 50 | - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions 51 | value: 'true' 52 | - key: llvm-namespace-comment.ShortNamespaceLines 53 | value: '1' 54 | - key: llvm-namespace-comment.SpacesBeforeComments 55 | value: '1' 56 | - key: readability-identifier-naming.MacroDefinitionCase 57 | value: UPPER_CASE 58 | - key: readability-identifier-naming.MemberCase 59 | value: lower_case 60 | - key: readability-identifier-naming.PrivateMemberSuffix 61 | value: _ 62 | - key: readability-identifier-naming.PublicMemberSuffix 63 | value: '' 64 | - key: readability-identifier-naming.NamespaceCase 65 | value: lower_case 66 | - key: readability-identifier-naming.ParameterCase 67 | value: lower_case 68 | - key: readability-identifier-naming.VariableCase 69 | value: lower_case 70 | - key: readability-implicit-bool-conversion.AllowPointerConditions 71 | value: 'true' 72 | ... 73 | -------------------------------------------------------------------------------- /01/string.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STRING_HPP 2 | #define STRING_HPP 3 | 4 | #include // memcmp/memcpy/strlen/size_t 5 | 6 | class String { 7 | public: 8 | String() : ptr_(nullptr), len_(0) {} 9 | String(const char* s) : ptr_(nullptr), len_(strlen(s)) 10 | { 11 | if (len_ != 0) { 12 | ptr_ = new char[len_ + 1]; 13 | memcpy(ptr_, s, len_ + 1); 14 | } 15 | } 16 | String(const String& rhs) : ptr_(nullptr), len_(rhs.len_) 17 | { 18 | if (len_ != 0) { 19 | ptr_ = new char[rhs.len_ + 1]; 20 | memcpy(ptr_, rhs.ptr_, len_ + 1); 21 | } 22 | } 23 | String& operator=(const String& rhs) 24 | { 25 | if (this != &rhs) { 26 | char* ptr = nullptr; 27 | if (rhs.len_ != 0) { 28 | ptr = new char[rhs.len_ + 1]; 29 | memcpy(ptr, rhs.ptr_, len_ + 1); 30 | } 31 | delete[] ptr_; 32 | ptr_ = ptr; 33 | len_ = rhs.len_; 34 | } 35 | return *this; 36 | } 37 | ~String() 38 | { 39 | delete[] ptr_; 40 | } 41 | 42 | const char* c_str() const 43 | { 44 | return ptr_; 45 | } 46 | size_t size() const 47 | { 48 | return len_; 49 | } 50 | 51 | void assign(const char* s); 52 | 53 | static bool equals(const String& lhs, const String& rhs) 54 | { 55 | if (lhs.len_ != rhs.len_) { 56 | return false; 57 | } 58 | return memcmp(lhs.ptr_, rhs.ptr_, lhs.len_) == 0; 59 | } 60 | 61 | private: 62 | char* ptr_; 63 | size_t len_; 64 | }; 65 | 66 | inline void String::assign(const char* s) 67 | { 68 | size_t len = strlen(s); 69 | char* ptr = nullptr; 70 | if (len != 0) { 71 | ptr = new char[len + 1]; 72 | memcpy(ptr, s, len + 1); 73 | } 74 | delete[] ptr_; 75 | ptr_ = ptr; 76 | len_ = len; 77 | } 78 | 79 | inline bool operator==(const String& lhs, const String& rhs) 80 | { 81 | return String::equals(lhs, rhs); 82 | } 83 | 84 | #endif // STRING_HPP 85 | -------------------------------------------------------------------------------- /02/stack_unwind.cpp: -------------------------------------------------------------------------------- 1 | #include // puts 2 | 3 | class Obj { 4 | public: 5 | Obj() { puts("Obj()"); } 6 | ~Obj() { puts("~Obj()"); } 7 | }; 8 | 9 | void foo(int n) 10 | { 11 | Obj obj; 12 | if (n == 42) { 13 | throw "life, the universe and everything"; 14 | } 15 | } 16 | 17 | int main() 18 | { 19 | try { 20 | foo(41); 21 | foo(42); 22 | } 23 | catch (const char* s) { 24 | puts(s); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /02/temporary_object.cpp: -------------------------------------------------------------------------------- 1 | #include // puts 2 | 3 | class Shape { 4 | public: 5 | virtual ~Shape() {} 6 | }; 7 | 8 | class Circle : public Shape { 9 | public: 10 | Circle() { puts("Circle()"); } 11 | ~Circle() { puts("~Circle()"); } 12 | }; 13 | 14 | class Triangle : public Shape { 15 | public: 16 | Triangle() { puts("Triangle()"); } 17 | ~Triangle() { puts("~Triangle()"); } 18 | }; 19 | 20 | class Result { 21 | public: 22 | Result() { puts("Result()"); } 23 | ~Result() { puts("~Result()"); } 24 | }; 25 | 26 | Result process_shape(const Shape& shape1, const Shape& shape2) 27 | { 28 | puts("process_shape()"); 29 | return Result(); 30 | } 31 | 32 | int main() 33 | { 34 | puts("main()"); 35 | process_shape(Circle(), Triangle()); 36 | puts("something else"); 37 | } 38 | -------------------------------------------------------------------------------- /03/rvo.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::move 3 | 4 | using namespace std; 5 | 6 | bool cond = false; 7 | 8 | class A { 9 | public: 10 | A(int num = 0) : num_{num} 11 | { 12 | cout << "Create A(" << num_ << ")\n"; 13 | } 14 | ~A() 15 | { 16 | cout << "Destroy A(" << num_ << ")\n"; 17 | } 18 | A(const A& rhs) : num_{rhs.num_} 19 | { 20 | cout << "Copy A(" << rhs.num_ << ")\n"; 21 | } 22 | A& operator=(const A& rhs) 23 | { 24 | cout << "Copy= A(" << rhs.num_ << ") <- " << num_ << "\n"; 25 | num_ = rhs.num_; 26 | return *this; 27 | } 28 | #ifndef DISABLE_MOVE 29 | A(A&& rhs) noexcept : num_(rhs.num_) 30 | { 31 | cout << "Move A(" << rhs.num_ << ")\n"; 32 | rhs.num_ = -1; 33 | } 34 | A& operator=(A&& rhs) noexcept 35 | { 36 | cout << "Move= A(" << rhs.num_ << ") <- " << num_ << "\n"; 37 | num_ = rhs.num_; 38 | rhs.num_ = -1; 39 | return *this; 40 | } 41 | #endif 42 | 43 | private: 44 | int num_; 45 | }; 46 | 47 | A getA_unnamed_rvo() 48 | { 49 | return A(); 50 | } 51 | 52 | A getA_named_rvo() 53 | { 54 | A a; 55 | return a; 56 | } 57 | 58 | A getA_named_rvo_suppressed() 59 | { 60 | A a; 61 | return std::move(a); 62 | } 63 | 64 | A getA_no_rvo1() 65 | { 66 | A a(1); 67 | if (cond) { 68 | return A(); 69 | } else { 70 | return a; 71 | } 72 | } 73 | 74 | A getA_no_rvo2() 75 | { 76 | A a1(1); 77 | A a2(2); 78 | if (cond) { 79 | return a1; 80 | } else { 81 | return a2; 82 | } 83 | } 84 | 85 | int main() 86 | { 87 | { 88 | cout << "*** Calling getA_unnamed_rvo\n"; 89 | A a = getA_unnamed_rvo(); 90 | } 91 | { 92 | cout << "*** Calling getA_named_rvo\n"; 93 | A a = getA_named_rvo(); 94 | } 95 | { 96 | cout << "*** Calling getA_named_rvo_suppressed\n"; 97 | A a = getA_named_rvo_suppressed(); 98 | } 99 | { 100 | cout << "*** Calling getA_no_rvo1\n"; 101 | A a = getA_no_rvo1(); 102 | } 103 | { 104 | cout << "*** Calling getA_no_rvo2\n"; 105 | A a = getA_no_rvo2(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /04/.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: 3 | bugprone-*, 4 | -bugprone-easily-swappable-parameters, 5 | -bugprone-sizeof-container, 6 | clang-diagnostic-*, 7 | clang-analyzer-*, 8 | cppcoreguidelines-*, 9 | -cppcoreguidelines-avoid-c-arrays, 10 | -cppcoreguidelines-avoid-do-while, 11 | -cppcoreguidelines-avoid-magic-numbers, 12 | -cppcoreguidelines-avoid-non-const-global-variables, 13 | -cppcoreguidelines-macro-usage, 14 | -cppcoreguidelines-missing-std-forward, 15 | -cppcoreguidelines-no-malloc, 16 | -cppcoreguidelines-owning-memory, 17 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 18 | -cppcoreguidelines-pro-bounds-constant-array-index, 19 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 20 | -cppcoreguidelines-pro-type-const-cast, 21 | -cppcoreguidelines-pro-type-reinterpret-cast, 22 | -cppcoreguidelines-pro-type-static-cast-downcast, 23 | -cppcoreguidelines-pro-type-vararg, 24 | llvm-namespace-comment, 25 | readability-*, 26 | -readability-convert-member-functions-to-static, 27 | -readability-else-after-return, 28 | -readability-identifier-length, 29 | -readability-implicit-bool-conversion, 30 | -readability-inconsistent-declaration-parameter-name, 31 | -readability-magic-numbers, 32 | -readability-make-member-function-const, 33 | -readability-named-parameter, 34 | -readability-qualified-auto, 35 | -readability-suspicious-call-argument 36 | WarningsAsErrors: '' 37 | FormatStyle: file 38 | ExtraArgs: ['-std=c++17'] 39 | CheckOptions: 40 | - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays 41 | value: 'true' 42 | - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions 43 | value: 'true' 44 | - key: llvm-namespace-comment.ShortNamespaceLines 45 | value: '1' 46 | - key: llvm-namespace-comment.SpacesBeforeComments 47 | value: '1' 48 | - key: readability-identifier-naming.MacroDefinitionCase 49 | value: UPPER_CASE 50 | - key: readability-identifier-naming.MemberCase 51 | value: lower_case 52 | - key: readability-identifier-naming.PrivateMemberSuffix 53 | value: _ 54 | - key: readability-identifier-naming.PublicMemberSuffix 55 | value: '' 56 | - key: readability-identifier-naming.NamespaceCase 57 | value: lower_case 58 | - key: readability-identifier-naming.ParameterCase 59 | value: lower_case 60 | - key: readability-identifier-naming.VariableCase 61 | value: lower_case 62 | - key: readability-implicit-bool-conversion.AllowPointerConditions 63 | value: 'true' 64 | ... 65 | -------------------------------------------------------------------------------- /04/iteration_overload.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | template 7 | void foo(const C& c) 8 | { 9 | for (typename C::const_iterator it = c.begin(), end = c.end(); 10 | it != end; ++it) { 11 | // 循环体 12 | } 13 | } 14 | 15 | template 16 | void foo(const T (&a)[N]) 17 | { 18 | typedef const T* ptr_t; 19 | for (ptr_t it = a, end = a + N; it != end; ++it) { 20 | // 循环体 21 | } 22 | } 23 | 24 | int main() 25 | { 26 | std::vector v; 27 | v.push_back(1); 28 | v.push_back(2); 29 | v.push_back(3); 30 | foo(v); 31 | int a[] = {1, 2, 3}; 32 | foo(a); 33 | } 34 | -------------------------------------------------------------------------------- /04/iteration_unified_98.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | template 7 | struct traits { 8 | typedef typename T::const_iterator const_iterator; 9 | static const_iterator begin(const T& c) 10 | { 11 | return c.begin(); 12 | } 13 | static const_iterator end(const T& c) 14 | { 15 | return c.end(); 16 | } 17 | }; 18 | 19 | template 20 | struct traits { 21 | typedef const T* const_iterator; 22 | static const_iterator begin(const T (&a)[N]) 23 | { 24 | return a; 25 | } 26 | static const_iterator end(const T (&a)[N]) 27 | { 28 | return a + N; 29 | } 30 | }; 31 | 32 | template 33 | void foo(const T& c) 34 | { 35 | for (typename traits::const_iterator 36 | it = traits::begin(c), 37 | end = traits::end(c); 38 | it != end; ++it) { 39 | // 循环体 40 | } 41 | } 42 | 43 | int main() 44 | { 45 | std::vector v; 46 | v.push_back(1); 47 | v.push_back(2); 48 | v.push_back(3); 49 | foo(v); 50 | int a[] = {1, 2, 3}; 51 | foo(a); 52 | } 53 | -------------------------------------------------------------------------------- /06/counted_ops.cpp: -------------------------------------------------------------------------------- 1 | #include // std::function 2 | #include // std::cout 3 | #include // std::map 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | int count_plus{}; 10 | int count_minus{}; 11 | int count_multiplies{}; 12 | int count_divides{}; 13 | 14 | map> ops{ 15 | {"+", 16 | [&count_plus](int x, int y) { 17 | ++count_plus; 18 | return x + y; 19 | }}, 20 | {"-", 21 | [&count_minus](int x, int y) { 22 | ++count_minus; 23 | return x - y; 24 | }}, 25 | {"*", 26 | [&count_multiplies](int x, int y) { 27 | ++count_multiplies; 28 | return x * y; 29 | }}, 30 | {"/", 31 | [&count_divides](int x, int y) { 32 | ++count_divides; 33 | return x / y; 34 | }}, 35 | }; 36 | 37 | cout << ops.at("+")(ops.at("*")(5, 8), 2) << '\n'; 38 | cout << count_multiplies << '\n'; 39 | cout << count_plus << '\n'; 40 | } 41 | -------------------------------------------------------------------------------- /06/function_object.cpp: -------------------------------------------------------------------------------- 1 | #include // std::bind/bind2nd/function/plus 2 | #include // std::cout 3 | #include // std::map 4 | #include // std::string 5 | 6 | using namespace std; 7 | 8 | struct Adder { 9 | Adder(int n) : n_(n) {} 10 | int operator()(int x) const 11 | { 12 | return x + n_; 13 | } 14 | 15 | private: 16 | int n_; 17 | }; 18 | 19 | int main() 20 | { 21 | { 22 | Adder add2(2); 23 | cout << "Manual function object: " << add2(5) << '\n'; 24 | } 25 | 26 | #if __cplusplus < 201703L 27 | #if defined(__GNUC__) 28 | #pragma GCC diagnostic push 29 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 30 | #endif 31 | { 32 | auto add2 = bind2nd(plus(), 2); 33 | cout << "bind2nd: " << add2(5) << '\n'; 34 | } 35 | #if defined(__GNUC__) 36 | #pragma GCC diagnostic pop 37 | #endif 38 | #endif 39 | 40 | { 41 | using namespace std::placeholders; 42 | // NOLINTNEXTLINE(modernize-avoid-bind) 43 | auto add2 = bind(plus(), _1, 2); 44 | cout << "bind: " << add2(5) << '\n'; 45 | } 46 | 47 | { 48 | auto add2 = [](int x) { return x + 2; }; 49 | cout << "Lambda: " << add2(5) << '\n'; 50 | } 51 | 52 | { 53 | auto adder = [](int n) { return [n](int x) { return x + n; }; }; 54 | cout << "Currying: " << adder(2)(5) << '\n'; 55 | } 56 | 57 | { 58 | map> op_dict{ 59 | {"+", [](int x, int y) { return x + y; }}, 60 | {"-", [](int x, int y) { return x - y; }}, 61 | {"*", [](int x, int y) { return x * y; }}, 62 | {"/", [](int x, int y) { return x / y; }}, 63 | }; 64 | cout << "std::function: " << op_dict.at("+")(1, 6) << '\n'; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /06/function_ref_ptr.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | 3 | using namespace std; 4 | 5 | int add2(int x) 6 | { 7 | return x + 2; 8 | }; 9 | 10 | template 11 | auto test1(T fn) 12 | { 13 | return fn(2); 14 | } 15 | 16 | template 17 | auto test2(T& fn) 18 | { 19 | return fn(2); 20 | } 21 | 22 | template 23 | auto test3(T* fn) 24 | { 25 | return (*fn)(2); 26 | } 27 | 28 | int main() 29 | { 30 | cout << test1(add2) << '\n'; 31 | cout << test2(add2) << '\n'; 32 | cout << test3(add2) << '\n'; 33 | } 34 | -------------------------------------------------------------------------------- /07/associative_containers.cpp: -------------------------------------------------------------------------------- 1 | #include // std::greater 2 | #include // std::cout 3 | #include // std::map/multimap 4 | #include // std::set/multiset 5 | #include // std::string 6 | #include // std::tuple 7 | #include "ostream_range.h" // operator<< for ranges 8 | 9 | using namespace std; 10 | 11 | int main() 12 | { 13 | cout << boolalpha; 14 | 15 | set s{1, 1, 1, 2, 3, 4}; 16 | cout << s << '\n'; // 重复元素被去除 17 | multiset> ms{1, 1, 1, 2, 3, 4}; 18 | cout << ms << '\n'; // 重复元素会保留 19 | map mp{ 20 | {"one", 1}, {"two", 2}, {"three", 3}, {"four", 4} 21 | }; 22 | cout << mp << '\n'; 23 | mp.insert({"four", 4}); // 键已存在,插入无效 24 | cout << mp << '\n'; 25 | cout << (mp.find("four") != mp.end()) << '\n'; 26 | cout << (mp.find("five") != mp.end()) << '\n'; 27 | mp["five"] = 5; // 创建或覆盖 "five" 键 28 | mp.erase("one"); // 删除 "one" 键 29 | cout << (mp.find("five") != mp.end()) << '\n'; 30 | cout << mp << '\n'; 31 | multimap mmp{ 32 | {"one", 1}, {"two", 2}, {"three", 3}, {"four", 4} 33 | }; 34 | mmp.insert({"four", -4}); // 一定插入成功(键可以重复) 35 | cout << mmp << '\n'; 36 | 37 | auto it = mp.find("four"); 38 | if (it != mp.end()) { 39 | cout << "Found: " << it->second << '\n'; 40 | } 41 | it = mp.lower_bound("four"); 42 | if (it != mp.end() && 43 | !(less{}("four", it->first))) { 44 | // 后半部分条件此处也可以用更简单的 it->first == "four" 45 | cout << "Found: " << it->second << '\n'; 46 | } 47 | 48 | multimap::iterator lower, upper; 49 | std::tie(lower, upper) = mmp.equal_range("four"); 50 | if ((lower != upper)) { 51 | cout << "Found:"; 52 | while (lower != upper) { 53 | cout << ' ' << lower->second; 54 | ++lower; 55 | } 56 | cout << '\n'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /07/hash.cpp: -------------------------------------------------------------------------------- 1 | #include // std::hash 2 | #include // std::cout 3 | #include // std::string 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | cout << hex; 10 | 11 | auto hp = hash(); 12 | cout << "hash(nullptr) = " << hp(nullptr) << '\n'; 13 | 14 | auto hs = hash(); 15 | cout << "hash(\"world\") = " << hs(string("world")) << '\n'; 16 | cout << "hash(\"world \") = " << hs(string("world ")) << '\n'; 17 | } 18 | -------------------------------------------------------------------------------- /07/list_member_func.cpp: -------------------------------------------------------------------------------- 1 | #include // std::sort 2 | #include // std::cout/endl 3 | #include // std::list 4 | #include // std::vector 5 | #include "ostream_range.h" // operator<< for ranges 6 | 7 | using namespace std; 8 | 9 | int main() 10 | { 11 | list lst{1, 7, 2, 8, 3}; 12 | vector vec{1, 7, 2, 8, 3}; 13 | 14 | sort(vec.begin(), vec.end()); // 正常 15 | // sort(lst.begin(), lst.end()); // 不能编译 16 | lst.sort(); // 正常 17 | 18 | cout << lst << endl; // 输出 { 1, 2, 3, 7, 8 } 19 | cout << vec << endl; // 输出 { 1, 2, 3, 7, 8 } 20 | } 21 | -------------------------------------------------------------------------------- /07/obj_set.cpp: -------------------------------------------------------------------------------- 1 | #include // std::array 2 | #include // std::cout 3 | #include // std::set 4 | 5 | using namespace std; 6 | 7 | template 8 | struct id_compare { 9 | template 10 | bool operator()(const T& lhs, const U& rhs) const 11 | { 12 | return lhs.id < rhs.id; 13 | } 14 | template 15 | bool operator()(const T& lhs, IdType rhs_id) const 16 | { 17 | return lhs.id < rhs_id; 18 | } 19 | template 20 | bool operator()(IdType lhs_id, const T& rhs) const 21 | { 22 | return lhs_id < rhs.id; 23 | } 24 | 25 | typedef void is_transparent; 26 | }; 27 | 28 | struct Obj { 29 | int id; 30 | std::array other_info; 31 | }; 32 | 33 | int main() 34 | { 35 | set> s{{1, {}}, {2, {}}}; 36 | cout << 1 << ": " 37 | << (s.find(1) != s.end() ? "FOUND" : "NOT found") << '\n'; 38 | cout << 0 << ": " 39 | << (s.find(0) != s.end() ? "FOUND" : "NOT found") << '\n'; 40 | } 41 | -------------------------------------------------------------------------------- /07/priority_queue.cpp: -------------------------------------------------------------------------------- 1 | #include // std::greater 2 | #include // std::cout/endl 3 | #include // std::pair 4 | #include // std::priority_queue 5 | #include // std::vector 6 | #include "ostream_range.h" // operator<< for ranges 7 | 8 | using namespace std; 9 | 10 | int main() 11 | { 12 | priority_queue, vector>, 13 | greater<>> 14 | q; 15 | q.emplace(1, 1); 16 | q.emplace(2, 2); 17 | q.emplace(0, 3); 18 | q.emplace(9, 4); 19 | while (!q.empty()) { 20 | cout << q.top() << endl; 21 | q.pop(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /07/queue.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::queue 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | queue q; 9 | q.push(1); 10 | q.push(2); 11 | q.push(3); 12 | while (!q.empty()) { 13 | cout << q.front() << '\n'; 14 | q.pop(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07/stack.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::stack 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | stack s; 9 | s.push(1); 10 | s.push(2); 11 | s.push(3); 12 | while (!s.empty()) { 13 | cout << s.top() << '\n'; 14 | s.pop(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07/unordered_associative_containers.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::string 3 | #include // std::tuple 4 | #include // std::unordered_map/unordered_multimap 5 | #include // std::unordered_set/unordered_multiset 6 | #include "ostream_range.h" // operator<< for ranges 7 | 8 | using namespace std; 9 | 10 | int main() 11 | { 12 | cout << boolalpha; 13 | 14 | unordered_set s{1, 1, 1, 2, 3, 4}; 15 | cout << s << '\n'; // 重复元素被去除 16 | unordered_multiset ms{1, 1, 1, 2, 3, 4}; 17 | cout << ms << '\n'; // 重复元素会保留 18 | unordered_map mp{ 19 | {"one", 1}, {"two", 2}, {"three", 3}, {"four", 4} 20 | }; 21 | cout << mp << '\n'; 22 | mp.insert({"four", 4}); // 键已存在,插入无效 23 | cout << mp << '\n'; 24 | cout << (mp.find("four") != mp.end()) << '\n'; 25 | cout << (mp.find("five") != mp.end()) << '\n'; 26 | mp["five"] = 5; // 创建或覆盖 "five" 键 27 | mp.erase("one"); // 删除 "one" 键 28 | cout << (mp.find("five") != mp.end()) << '\n'; 29 | cout << mp << '\n'; 30 | unordered_multimap mmp{ 31 | {"one", 1}, {"two", 2}, {"three", 3}, {"four", 4} 32 | }; 33 | mmp.insert({"four", -4}); // 一定插入成功(键可以重复) 34 | cout << mmp << '\n'; 35 | 36 | auto it = mp.find("four"); 37 | if (it != mp.end()) { 38 | cout << "Found: " << it->second << '\n'; 39 | } 40 | 41 | unordered_multimap::iterator lower, upper; 42 | std::tie(lower, upper) = mmp.equal_range("four"); 43 | if ((lower != upper)) { 44 | cout << "Found:"; 45 | while (lower != upper) { 46 | cout << ' ' << lower->second; 47 | ++lower; 48 | } 49 | cout << '\n'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /07/vector.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::vector 3 | #include // size_t 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | vector v{1, 2, 3, 4}; 10 | v.push_back(5); 11 | v.insert(v.begin(), 0); 12 | for (size_t i = 0; i < v.size(); ++i) { 13 | cout << v[i] << ' '; 14 | } 15 | cout << '\n'; 16 | 17 | int sum = 0; 18 | for (auto it = v.begin(); it != v.end(); ++it) { 19 | sum += *it; 20 | } 21 | cout << sum << '\n'; 22 | } 23 | -------------------------------------------------------------------------------- /07/vector_move.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::vector 3 | 4 | using namespace std; 5 | 6 | class Obj1 { 7 | public: 8 | Obj1() { cout << "Obj1()\n"; } 9 | Obj1(const Obj1&) { cout << "Obj1(const Obj1&)\n"; } 10 | Obj1(Obj1&&) { cout << "Obj1(Obj1&&)\n"; } 11 | Obj1& operator=(const Obj1&) 12 | { 13 | cout << "Obj1& operator=(const Obj1&)\n"; 14 | return *this; 15 | } 16 | Obj1& operator=(Obj1&&) 17 | { 18 | cout << "Obj1& operator=(Obj1&&)\n"; 19 | return *this; 20 | } 21 | }; 22 | 23 | class Obj2 { 24 | public: 25 | Obj2() { cout << "Obj2()\n"; } 26 | Obj2(const Obj2&) { cout << "Obj2(const Obj2&)\n"; } 27 | Obj2(Obj2&&) noexcept { cout << "Obj2(Obj2&&)\n"; } 28 | Obj2& operator=(const Obj2&) 29 | { 30 | cout << "Obj2& operator=(const Obj2&)\n"; 31 | return *this; 32 | } 33 | Obj2& operator=(Obj2&&) noexcept 34 | { 35 | cout << "Obj2& operator=(Obj2&&)\n"; 36 | return *this; 37 | } 38 | }; 39 | 40 | int main() 41 | { 42 | vector v1; 43 | v1.reserve(2); 44 | v1.emplace_back(); 45 | v1.emplace_back(); 46 | v1.emplace_back(); 47 | v1.erase(v1.begin()); 48 | 49 | vector v2; 50 | v2.reserve(2); 51 | v2.emplace_back(); 52 | v2.emplace_back(); 53 | v2.emplace_back(); 54 | v2.erase(v2.begin()); 55 | } 56 | -------------------------------------------------------------------------------- /08/null_sentinel.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | 3 | #if __cplusplus >= 202002L 4 | #include // std::ranges::for_each 5 | #endif 6 | 7 | using std::cout; 8 | 9 | struct null_sentinel {}; 10 | 11 | template 12 | bool operator==(I i, null_sentinel) 13 | { 14 | return *i == 0; 15 | } 16 | 17 | template 18 | bool operator==(null_sentinel, I i) 19 | { 20 | return *i == 0; 21 | } 22 | 23 | template 24 | bool operator!=(I i, null_sentinel) 25 | { 26 | return *i != 0; 27 | } 28 | 29 | template 30 | bool operator!=(null_sentinel, I i) 31 | { 32 | return *i != 0; 33 | } 34 | 35 | class c_string_reader { 36 | public: 37 | c_string_reader(const char* s) : ptr_(s) {} 38 | const char* begin() const { return ptr_; } 39 | null_sentinel end() const { return {}; } 40 | 41 | private: 42 | const char* ptr_; 43 | }; 44 | 45 | int main() 46 | { 47 | const char* msg = "Hello world!\n"; 48 | 49 | for (auto ptr = msg; ptr != null_sentinel{}; ++ptr) { 50 | cout << *ptr; 51 | } 52 | 53 | for (char ch : c_string_reader(msg)) { 54 | cout << ch; 55 | } 56 | 57 | #if __cplusplus >= 202002L 58 | std::ranges::for_each(msg, null_sentinel{}, 59 | [](char ch) { cout << ch; }); 60 | #endif 61 | } 62 | -------------------------------------------------------------------------------- /08/reverse_iterate.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::vector 3 | 4 | using namespace std; 5 | 6 | template 7 | void reverseOutput(const C& c) 8 | { 9 | for (auto it = c.end(); it != c.begin();) { 10 | --it; 11 | cout << *it << ' '; 12 | } 13 | cout << '\n'; 14 | 15 | for (auto it = c.rbegin(); it != c.rend(); ++it) { 16 | cout << *it << ' '; 17 | } 18 | cout << '\n'; 19 | } 20 | 21 | int main() 22 | { 23 | vector v{1, 2, 3, 4, 5}; 24 | reverseOutput(v); 25 | } 26 | -------------------------------------------------------------------------------- /09/copy_sort_cxx11.cpp: -------------------------------------------------------------------------------- 1 | #include // std::copy/sort 2 | #include // std::cout 3 | #include // std::ostream_iterator 4 | 5 | int main() 6 | { 7 | using namespace std; 8 | int a[] = {1, 7, 3, 6, 5, 2, 4, 8}; 9 | copy(begin(a), end(a), ostream_iterator(cout, " ")); 10 | cout << '\n'; 11 | sort(begin(a), end(a)); 12 | copy(begin(a), end(a), ostream_iterator(cout, " ")); 13 | cout << '\n'; 14 | } 15 | -------------------------------------------------------------------------------- /09/copy_sort_cxx20.cpp: -------------------------------------------------------------------------------- 1 | #include // std::ranges::copy/sort 2 | #include // std::cout 3 | #include // std::ostream_iterator 4 | 5 | int main() 6 | { 7 | using namespace std; 8 | using std::ranges::copy; 9 | using std::ranges::sort; 10 | int a[] = {1, 7, 3, 6, 5, 2, 4, 8}; 11 | copy(a, ostream_iterator(cout, " ")); 12 | cout << '\n'; 13 | sort(a); 14 | copy(a, ostream_iterator(cout, " ")); 15 | cout << '\n'; 16 | } 17 | -------------------------------------------------------------------------------- /09/max_min.cpp: -------------------------------------------------------------------------------- 1 | #include // std::max/min/minmax 2 | #include // assert 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | int n = 1; 9 | int m = -1; 10 | assert(&min(n, m) == &m); 11 | assert(&max(n, m) == &n); 12 | assert(&(minmax(n, m).first) == &m); 13 | assert(&(minmax(n, m).second) == &n); 14 | 15 | assert(min({n, m, 5}) == m); 16 | // Can't compile: assert(&max({n, m}) == &n); 17 | } 18 | -------------------------------------------------------------------------------- /09/par_reduce.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono::steady_clock 2 | #include // std::execution::par 3 | #include // std::cout 4 | #include // std::accumulate/reduce 5 | #include // std::vector 6 | 7 | using namespace std; 8 | 9 | int main() 10 | { 11 | vector v(10000000, 0.0625); 12 | 13 | { 14 | auto t1 = chrono::steady_clock::now(); 15 | double result = accumulate(v.begin(), v.end(), 0.0); 16 | auto t2 = chrono::steady_clock::now(); 17 | cout << "accumulate: result " << result << " took " 18 | << (t2 - t1) / 1.0ms << " ms\n"; 19 | } 20 | 21 | { 22 | auto t1 = chrono::steady_clock::now(); 23 | double result = reduce(execution::seq, v.begin(), v.end()); 24 | auto t2 = chrono::steady_clock::now(); 25 | cout << "reduce (seq): result " << result << " took " 26 | << (t2 - t1) / 1.0ms << " ms\n"; 27 | } 28 | 29 | { 30 | auto t1 = chrono::steady_clock::now(); 31 | double result = reduce(execution::par, v.begin(), v.end()); 32 | auto t2 = chrono::steady_clock::now(); 33 | cout << "reduce (par): result " << result << " took " 34 | << (t2 - t1) / 1.0ms << " ms\n"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /09/set_erase_if.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::set 3 | #include "ostream_range.h" // operator<< for ranges 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | set s{1, 2, 3, 4, 5, 6, 7, 8}; 10 | cout << "Original set: " << s << '\n'; 11 | 12 | #if __cpp_lib_erase_if >= 202002L 13 | erase_if(s, [](int n) { return n % 2 != 0; }); 14 | #else 15 | for (auto it = s.begin(); it != s.end();) { 16 | if (*it % 2 != 0) { 17 | it = s.erase(it); 18 | } else { 19 | ++it; 20 | } 21 | } 22 | #endif 23 | cout << "Filtered set: " << s << '\n'; 24 | } 25 | -------------------------------------------------------------------------------- /09/sort_projection.cpp: -------------------------------------------------------------------------------- 1 | #include // std::ranges::sort 2 | #include // std::less 3 | #include // std::cout 4 | #include // std::string_view 5 | #include // std::pair 6 | #include "ostream_range.h" // operator<< for ranges 7 | 8 | int main() 9 | { 10 | using namespace std; 11 | using MyPair = pair; 12 | MyPair a[]{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}}; 13 | #if __cpp_lib_ranges >= 201911L 14 | using std::ranges::sort; 15 | sort(a, less{}, &MyPair::second); 16 | #else 17 | sort(begin(a), end(a), 18 | [](const MyPair& lhs, const MyPair& rhs) { 19 | return lhs.second < rhs.second; 20 | }); 21 | #endif 22 | cout << a << '\n'; 23 | } 24 | -------------------------------------------------------------------------------- /10/cxx20_views.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::map 3 | #include // std::views::* 4 | #include // std::string 5 | #include "ostream_range.h" // operator<< for ranges 6 | 7 | int main() 8 | { 9 | std::map mp{ 10 | {1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}}; 11 | std::cout << (mp | std::views::reverse 12 | | std::views::filter([](const auto& pr) { 13 | return pr.first % 2 == 0; 14 | }) 15 | | std::views::values) 16 | << '\n'; 17 | } 18 | -------------------------------------------------------------------------------- /10/cxx20_views_bad_transform.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::map 3 | #include // std::views::* 4 | #include // std::string 5 | #include "ostream_range.h" // operator<< for ranges 6 | 7 | int main() 8 | { 9 | std::map mp{ 10 | {1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}}; 11 | int tf_count{}; 12 | std::cout << (mp | std::views::transform([&tf_count](const auto& pr) { 13 | ++tf_count; 14 | return pr.first; 15 | }) 16 | | std::views::filter([](int num) { 17 | return num % 2 == 0; 18 | })) 19 | << '\n'; 20 | std::cout << tf_count << " transformations are made\n"; 21 | } 22 | -------------------------------------------------------------------------------- /10/transparent_hash.cpp: -------------------------------------------------------------------------------- 1 | #include // std::size_t 2 | #include // std::equal_to/hash 3 | #include // std::cout 4 | #include // std::string 5 | #include // std::string_view 6 | #include // std::unordered_set 7 | 8 | struct MyStrHash { 9 | using is_transparent = void; 10 | std::size_t operator()(std::string_view str) const noexcept 11 | { 12 | return std::hash{}(str); 13 | } 14 | }; 15 | 16 | int main() 17 | { 18 | using namespace std; 19 | cout << boolalpha; 20 | unordered_set> s{ 21 | "one", "two", "three"}; 22 | cout << s.contains("one")<< '\n'; 23 | cout << s.contains("two")<< '\n'; 24 | cout << s.contains("tres")<< '\n'; 25 | } 26 | -------------------------------------------------------------------------------- /11/string.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STRING_HPP 2 | #define STRING_HPP 3 | 4 | #include // std::unique_ptr/make_unique 5 | #include // std::swap 6 | #include // memcmp/memcpy/strlen/size_t 7 | 8 | using namespace std; 9 | 10 | class String { 11 | public: 12 | String() : len_(0) {} 13 | String(const char* s) : len_(strlen(s)) 14 | { 15 | if (len_ != 0) { 16 | ptr_ = make_unique(len_ + 1); 17 | memcpy(ptr_.get(), s, len_ + 1); 18 | } 19 | } 20 | String(const String& rhs) : len_(rhs.len_) 21 | { 22 | if (len_ != 0) { 23 | ptr_ = make_unique(len_ + 1); 24 | memcpy(ptr_.get(), rhs.ptr_.get(), len_ + 1); 25 | } 26 | } 27 | String(String&& rhs) noexcept 28 | : ptr_(std::move(rhs.ptr_)), len_{rhs.len_} 29 | { 30 | rhs.len_ = 0; 31 | } 32 | String& operator=(const String& rhs) 33 | { 34 | String(rhs).swap(*this); 35 | return *this; 36 | } 37 | String& operator=(String&& rhs) noexcept 38 | { 39 | String(std::move(rhs)).swap(*this); 40 | return *this; 41 | } 42 | ~String() = default; 43 | 44 | void swap(String& rhs) noexcept 45 | { 46 | using std::swap; 47 | swap(ptr_, rhs.ptr_); 48 | swap(len_, rhs.len_); 49 | } 50 | 51 | const char* c_str() const 52 | { 53 | return ptr_.get(); 54 | } 55 | size_t size() const 56 | { 57 | return len_; 58 | } 59 | 60 | void assign(const char* s) 61 | { 62 | this->operator=(String(s)); 63 | } 64 | 65 | static bool equals(const String& lhs, const String& rhs) 66 | { 67 | if (lhs.len_ != rhs.len_) { 68 | return false; 69 | } 70 | return memcmp(lhs.ptr_.get(), rhs.ptr_.get(), lhs.len_) == 0; 71 | } 72 | 73 | private: 74 | std::unique_ptr ptr_; 75 | size_t len_; 76 | }; 77 | 78 | inline bool operator==(const String& lhs, const String& rhs) 79 | { 80 | return String::equals(lhs, rhs); 81 | } 82 | 83 | #endif // STRING_HPP 84 | -------------------------------------------------------------------------------- /12/chrono_cxx20.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono::* 2 | #include // std::cout 3 | #include // std::format 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | auto now = chrono::system_clock::now(); 10 | cout << "UTC time: " << now << '\n'; 11 | auto trunc_now = chrono::time_point_cast(now); 12 | cout << "UTC time: " << trunc_now << '\n'; 13 | auto local_tz = chrono::get_tzdb().current_zone(); 14 | auto zoned_now = chrono::zoned_time(local_tz, trunc_now); 15 | cout << format("Local time: {:%F %T}", zoned_now) << '\n'; 16 | cout << format("Local time: {:%F %H:%M:%S %Z}", zoned_now) 17 | << '\n'; 18 | 19 | cout << "Last days of months in 2024:\n"; 20 | constexpr auto one_month = chrono::months{1}; 21 | using std::chrono::last; 22 | for (auto ymd = 2024y/1/last; ymd < 2025y/1/1; ymd += one_month) { 23 | cout << ' ' << chrono::sys_days{ymd} << '\n'; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /12/chrono_measure.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono::* 2 | #include // std::cout 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | { 9 | auto t1 = chrono::steady_clock::now(); 10 | cout << "Hello world\n"; 11 | auto t2 = chrono::steady_clock::now(); 12 | cout << (t2 - t1) / 1ns << " ns has elapsed\n"; 13 | } 14 | { 15 | auto t1 = chrono::steady_clock::now(); 16 | cout << "Hello world\n"; 17 | auto t2 = chrono::steady_clock::now(); 18 | cout << (t2 - t1) / 1ns << " ns has elapsed\n"; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /12/random_ints.cpp: -------------------------------------------------------------------------------- 1 | #include // std::generate 2 | #include // std::cout 3 | #include // std::mt19937/random_device/... 4 | #include // std::vector 5 | #include "ostream_range.h" // operator<< for ranges 6 | 7 | using namespace std; 8 | 9 | int main() 10 | { 11 | auto seed = random_device{}(); 12 | cout << "Seed is " << seed << '\n'; 13 | mt19937 engine{seed}; 14 | uniform_int_distribution dist{1, 1000}; 15 | vector v(100); 16 | generate(v.begin(), v.end(), [&] { return dist(engine); }); 17 | cout << v << '\n'; 18 | } 19 | -------------------------------------------------------------------------------- /12/regex_email.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::regex/regex_match/smatch 3 | #include // std::string/getline 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | string line; 10 | regex pat(R"(\w+([.%+-]?\w+)*@\w+([.-]?\w+)*\.\w{2,})"); 11 | 12 | while (cin) { 13 | getline(cin, line); 14 | smatch matches; 15 | auto it = line.cbegin(); 16 | auto ite = line.cend(); 17 | while (regex_search(it, ite, matches, pat)) { 18 | cout << matches[0] << '\n'; 19 | it = matches.suffix().first; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /12/regex_iterator_email.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::regex/regex_match/smatch 3 | #include // std::string/getline 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | string line; 10 | regex pat(R"(\w+([.%+-]?\w+)*@\w+([.-]?\w+)*\.\w{2,})"); 11 | 12 | while (cin) { 13 | getline(cin, line); 14 | sregex_iterator pos(line.cbegin(), line.cend(), pat); 15 | sregex_iterator end; 16 | for (; pos != end; ++pos) { 17 | cout << (*pos)[0] << '\n'; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /12/shuffle_vector.cpp: -------------------------------------------------------------------------------- 1 | #include // std::generate/iota/shuffle 2 | #include // std::array 3 | #include // std::uint32_t 4 | #include // std::cout 5 | #include // std::mt19937/random_device/seed_seq 6 | #include // std::vector 7 | #include "ostream_range.h" // operator<< for ranges 8 | 9 | using namespace std; 10 | 11 | int main() 12 | { 13 | random_device rd; 14 | array seeds{}; 15 | generate(seeds.begin(), seeds.end(), [&] { return rd(); }); 16 | cout << "Seeds are " << seeds << '\n'; 17 | seed_seq sq(seeds.begin(), seeds.end()); 18 | mt19937 engine{sq}; 19 | vector v(54); 20 | iota(v.begin(), v.end(), 1); 21 | shuffle(v.begin(), v.end(), engine); 22 | cout << v << '\n'; 23 | } 24 | -------------------------------------------------------------------------------- /12/std_literals.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::complex/complex_literals 3 | #include // std::cout 4 | #include // std::string_literals 5 | #include // std::string_view_literals 6 | #include // std::this_thread::sleep_for 7 | 8 | using namespace std; 9 | 10 | int main() 11 | { 12 | cout << "i * i = " << 1i * 1i << '\n'; 13 | cout << "Waiting for 500ms\n"; 14 | this_thread::sleep_for(500ms); 15 | cout << "Hello world"s.substr(0, 5) << '\n'; 16 | cout << "Hello world"sv.substr(6) << '\n'; 17 | } 18 | -------------------------------------------------------------------------------- /12/tuple.cpp: -------------------------------------------------------------------------------- 1 | #include // std::sort 2 | #include // std::cout 3 | #include // std::string 4 | #include // std::tuple 5 | #include // std::vector 6 | 7 | using namespace std; 8 | 9 | using num_tuple = tuple; 10 | 11 | ostream& operator<<(ostream& os, const num_tuple& value) 12 | { 13 | os << get<0>(value) << ',' << get<1>(value) << ',' 14 | << get<2>(value); 15 | return os; 16 | } 17 | 18 | int main() 19 | { 20 | cout << "Tuple size is " << tuple_size_v << '\n'; 21 | vector vn{ 22 | {1, "one", "un"}, 23 | {2, "two", "deux"}, 24 | {3, "three", "trois"}, 25 | {4, "four", "quatre"}, 26 | }; 27 | get<2>(vn[0]) = "une"; 28 | sort(vn.begin(), vn.end(), 29 | [](auto& x, auto& y) { return get<2>(x) < get<2>(y); }); 30 | for (const auto& value : vn) { 31 | cout << value << '\n'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /12/user_literals.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | 3 | using namespace std; 4 | 5 | class length { 6 | public: 7 | enum unit { 8 | metre, 9 | kilometre, 10 | millimetre, 11 | centimetre, 12 | inch, 13 | foot, 14 | yard, 15 | mile, 16 | }; 17 | explicit length(double v, unit u = metre) 18 | : value_(v * factors[u]) 19 | { 20 | } 21 | 22 | double value() const { return value_; }; 23 | 24 | private: 25 | double value_; 26 | 27 | static constexpr double factors[] = { 28 | 1.0, 1000.0, 1e-3, 1e-2, 0.0254, 0.3048, 0.9144, 1609.344}; 29 | }; 30 | 31 | length operator+(length lhs, length rhs) 32 | { 33 | return length(lhs.value() + rhs.value()); 34 | } 35 | 36 | length operator""_m(long double v) 37 | { 38 | return length(static_cast(v), length::metre); 39 | } 40 | 41 | length operator""_cm(long double v) 42 | { 43 | return length(static_cast(v), length::centimetre); 44 | } 45 | 46 | int main() 47 | { 48 | auto result = 1.0_m + 10.0_cm; 49 | cout << "Result is " << result.value() << "m\n"; 50 | } 51 | -------------------------------------------------------------------------------- /14/expected_error_code.cpp: -------------------------------------------------------------------------------- 1 | #include // INT_MAX/INT_MIN 2 | #include // std::boolalpha 3 | #include // std::cout/endl 4 | #include // std::ostream 5 | #include // std::string 6 | #include // std::errc/error_code/error_condition/... 7 | #include // std::true_type 8 | 9 | #if __cplusplus > 202002L && __has_include() 10 | #include // std::expected/unexpected 11 | #endif 12 | 13 | #if __cpp_lib_expected >= 202211L 14 | using std::expected; 15 | using std::unexpected; 16 | #elif __has_include() 17 | #include // tl::expected/unexpected 18 | using tl::expected; 19 | using tl::unexpected; 20 | #else 21 | #error "No implementation of expected is found" 22 | #endif 23 | 24 | enum class DivErrc { 25 | success, 26 | divide_by_zero, 27 | integer_divide_overflows, 28 | not_integer_division, 29 | }; 30 | 31 | template <> 32 | struct std::is_error_code_enum : true_type { 33 | }; 34 | 35 | class DivErrcCategory : public std::error_category { 36 | public: 37 | const char* name() const noexcept override 38 | { 39 | return "divide error"; 40 | } 41 | std::string message(int c) const override 42 | { 43 | switch (static_cast(c)) { 44 | case DivErrc::success: 45 | return "Successful"; 46 | case DivErrc::divide_by_zero: 47 | return "Divide by zero"; 48 | case DivErrc::integer_divide_overflows: 49 | return "Integer divide overflows"; 50 | case DivErrc::not_integer_division: 51 | return "Not integer division"; 52 | } 53 | return "Unknown"; 54 | } 55 | // Optional: Allow generic error conditions to be compared 56 | std::error_condition 57 | default_error_condition(int c) const noexcept override 58 | { 59 | switch (static_cast(c)) { 60 | case DivErrc::success: 61 | break; 62 | case DivErrc::divide_by_zero: 63 | return {std::errc::invalid_argument}; 64 | case DivErrc::integer_divide_overflows: 65 | return {std::errc::value_too_large}; 66 | case DivErrc::not_integer_division: 67 | return {std::errc::result_out_of_range}; 68 | } 69 | return {c, *this}; 70 | } 71 | }; 72 | 73 | std::error_code make_error_code(DivErrc e) 74 | { 75 | static DivErrcCategory category; 76 | return {static_cast(e), category}; 77 | } 78 | 79 | expected divideSafe(int i, int j) 80 | { 81 | if (j == 0) { 82 | return unexpected(DivErrc::divide_by_zero); 83 | } 84 | if (i == INT_MIN && j == -1) { 85 | return unexpected(DivErrc::integer_divide_overflows); 86 | } 87 | if (i % j != 0) { 88 | return unexpected(DivErrc::not_integer_division); 89 | } 90 | return i / j; 91 | } 92 | 93 | expected addDivideSafe(int i, int j, int k) 94 | { 95 | return divideSafe(j, k).and_then( 96 | [i](int q) -> expected { 97 | if ((i > 0 && q > INT_MAX - i) || 98 | (i < 0 && q < INT_MIN - i)) { 99 | return unexpected( 100 | make_error_code(std::errc::value_too_large)); 101 | } 102 | return i + q; 103 | }); 104 | } 105 | 106 | void printError(std::ostream& os, const std::error_code& ec, 107 | const char* end = "") 108 | { 109 | os << ec.category().name() << ": " << ec.message() << end; 110 | } 111 | 112 | void printError(std::ostream& os, const std::error_condition& ec, 113 | const char* end = "") 114 | { 115 | os << ec.category().name() << ": " << ec.message() << end; 116 | } 117 | 118 | std::ostream& operator<<(std::ostream& os, 119 | const expected& exp) 120 | { 121 | if (exp) { 122 | os << *exp; 123 | } else { 124 | printError(os, exp.error()); 125 | } 126 | return os; 127 | } 128 | 129 | void check(const expected& result) 130 | { 131 | std::cout << result; 132 | if (!result && result.error() == DivErrc::divide_by_zero) { 133 | std::cout << ": Are you serious?"; 134 | } else if (result && *result == 42) { 135 | std::cout << ": Ha, I got you!"; 136 | } 137 | std::cout << std::endl; 138 | } 139 | 140 | int main() 141 | { 142 | std::cout << "*** Error code test\n"; 143 | printError(std::cout, std::errc::address_not_available, "\n"); 144 | printError(std::cout, DivErrc(10), "\n"); 145 | printError(std::cout, DivErrc::divide_by_zero, "\n"); 146 | std::cout << std::boolalpha 147 | << (make_error_code(DivErrc::divide_by_zero) == 148 | std::errc::invalid_argument) 149 | << std::endl; 150 | std::cout << "*** Expected test\n"; 151 | check(addDivideSafe(INT_MAX, 2, 2)); 152 | check(addDivideSafe(1, INT_MIN, -1)); 153 | check(addDivideSafe(2, 1, 0)); 154 | check(addDivideSafe(37, 20, 7)); 155 | check(addDivideSafe(39, 21, 7)); 156 | } 157 | -------------------------------------------------------------------------------- /14/expected_simple.cpp: -------------------------------------------------------------------------------- 1 | #include // INT_MIN 2 | #include // std::boolalpha 3 | #include // std::cout/endl 4 | #include // std::ostream 5 | 6 | #if __cplusplus > 202002L && __has_include() 7 | #include // std::expected/unexpected 8 | #endif 9 | 10 | #if __cpp_lib_expected >= 202211L 11 | using std::expected; 12 | using std::unexpected; 13 | #elif __has_include() 14 | #include // tl::expected/unexpected 15 | using tl::expected; 16 | using tl::unexpected; 17 | #else 18 | #error "No implementation of expected is found" 19 | #endif 20 | 21 | enum class DivErrc { 22 | success, 23 | divide_by_zero, 24 | integer_divide_overflows, 25 | not_integer_division, 26 | }; 27 | 28 | expected divideSafe(int i, int j) 29 | { 30 | if (j == 0) { 31 | return unexpected(DivErrc::divide_by_zero); 32 | } 33 | if (i == INT_MIN && j == -1) { 34 | return unexpected(DivErrc::integer_divide_overflows); 35 | } 36 | if (i % j != 0) { 37 | return unexpected(DivErrc::not_integer_division); 38 | } 39 | return i / j; 40 | } 41 | 42 | expected addDivideSafe(int i, int j, int k) 43 | { 44 | return divideSafe(j, k).and_then( 45 | [i](int q) -> expected { 46 | return i + q; 47 | }); 48 | } 49 | 50 | expected addDivideSafeVerbose(int i, int j, int k) 51 | { 52 | auto result = divideSafe(j, k); 53 | if (!result) { 54 | return unexpected(result.error()); 55 | } 56 | auto q = *result; 57 | return i + q; 58 | } 59 | 60 | expected isQuotient42(int i, int j) 61 | { 62 | return divideSafe(i, j).and_then( 63 | [](int q) -> expected { 64 | return q == 42; 65 | }); 66 | } 67 | 68 | std::ostream& operator<<(std::ostream& os, 69 | const expected& exp) 70 | { 71 | if (exp) { 72 | os << *exp; 73 | } else { 74 | os << "DivErrc: " << int(exp.error()); 75 | } 76 | return os; 77 | } 78 | 79 | void check(const expected& result) 80 | { 81 | std::cout << result; 82 | if (!result && result.error() == DivErrc::divide_by_zero) { 83 | std::cout << ": Are you serious?"; 84 | } else if (result && *result == 42) { 85 | std::cout << ": Ha, I got you!"; 86 | } 87 | std::cout << std::endl; 88 | } 89 | 90 | void check(const expected& result) 91 | { 92 | if (!result) { 93 | std::cout << "Bad result!" << std::endl; 94 | return; 95 | } 96 | if (*result) { 97 | std::cout << "Check is successful!" << std::endl; 98 | } else { 99 | std::cout << "Check has failed!" << std::endl; 100 | } 101 | } 102 | 103 | int main() 104 | { 105 | std::cout << "*** Expected test\n"; 106 | check(addDivideSafeVerbose(2, 1, 0)); 107 | check(addDivideSafe(37, 20, 7)); 108 | check(addDivideSafe(39, 21, 7)); 109 | check(isQuotient42(83, 3)); 110 | check(isQuotient42(84, 3)); 111 | check(isQuotient42(84, 2)); 112 | } 113 | -------------------------------------------------------------------------------- /14/filesystem_error.cpp: -------------------------------------------------------------------------------- 1 | #include // std::filesystem::* 2 | #include // std::cout 3 | #include // std::errc/error_code 4 | 5 | void printError(std::ostream& os, const std::error_code& ec, 6 | const char* end = "") 7 | { 8 | os << ec.category().name() << ": " << ec.message() << end; 9 | } 10 | 11 | using std::cout; 12 | using std::errc; 13 | using std::error_code; 14 | 15 | int main() 16 | { 17 | namespace fs = std::filesystem; 18 | fs::path path{"abcdef/example"}; 19 | 20 | // 方法一 21 | try { 22 | bool result = fs::remove(path); 23 | cout << "remove " << (result ? "succeeded" : "failed") 24 | << "!\n"; 25 | } 26 | catch (const fs::filesystem_error& e) { 27 | cout << "remove failed with exception!\n"; 28 | cout << e.what() << '\n'; 29 | if (e.code() == errc::permission_denied) { 30 | cout << "Please check permission!\n"; 31 | } 32 | } 33 | 34 | // 方法二 35 | error_code ec; 36 | if (fs::remove(path, ec)) { 37 | cout << "remove succeeded!\n"; 38 | } else { 39 | cout << "remove failed"; 40 | if (ec) { 41 | cout << " with error!\n"; 42 | printError(cout, ec, "\n"); 43 | if (ec == errc::permission_denied) { 44 | cout << "Please check permission!\n"; 45 | } 46 | } else { 47 | cout << "!\n"; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /14/optional_expected_rvo.cpp: -------------------------------------------------------------------------------- 1 | #include // std::copy_n/fill_n 2 | #include // std::size 3 | #include // assert 4 | #include // std::cout/endl 5 | #include // std::optional 6 | #include // std::runtime_error 7 | #include // std::errc/error_code/make_error_code 8 | 9 | #if __cplusplus > 202002L && __has_include() 10 | #include // std::expected/unexpected 11 | #endif 12 | 13 | #if __cpp_lib_expected >= 202211L 14 | using std::expected; 15 | using std::unexpected; 16 | #elif __has_include() 17 | #include // tl::expected/unexpected 18 | using tl::expected; 19 | using tl::unexpected; 20 | #else 21 | #error "No implementation of expected is found" 22 | #endif 23 | 24 | using std::copy_n; 25 | using std::cout; 26 | using std::errc; 27 | using std::error_code; 28 | using std::fill_n; 29 | using std::make_error_code; 30 | using std::optional; 31 | using std::size; 32 | 33 | class BigObj { 34 | public: 35 | BigObj() noexcept 36 | { 37 | cout << __PRETTY_FUNCTION__ << '\n'; 38 | } 39 | BigObj(int n) noexcept 40 | { 41 | cout << __PRETTY_FUNCTION__ << '\n'; 42 | fill_n(data_, size(data_), n); 43 | } 44 | BigObj(const BigObj& rhs) noexcept 45 | { 46 | cout << __PRETTY_FUNCTION__ << '\n'; 47 | copy_n(rhs.data_, size(rhs.data_), data_); 48 | } 49 | BigObj& operator=(const BigObj& rhs) noexcept 50 | { 51 | cout << __PRETTY_FUNCTION__ << '\n'; 52 | if (this != &rhs) { 53 | copy_n(rhs.data_, size(rhs.data_), data_); 54 | } 55 | return *this; 56 | } 57 | ~BigObj() 58 | { 59 | cout << __PRETTY_FUNCTION__ << '\n'; 60 | } 61 | 62 | void op() 63 | { 64 | cout << __PRETTY_FUNCTION__ << '\n'; 65 | } 66 | 67 | private: 68 | int data_[300]; 69 | }; 70 | 71 | struct BigStruct { 72 | int data[300]; 73 | }; 74 | 75 | BigObj getObj1(bool cond) 76 | { 77 | if (!cond) { 78 | throw std::runtime_error("Condition not satisfied"); 79 | } 80 | BigObj obj{42}; 81 | obj.op(); 82 | return obj; 83 | } 84 | 85 | optional getObj2(bool cond) 86 | { 87 | if (!cond) { 88 | return {}; 89 | } 90 | BigObj obj{42}; 91 | obj.op(); 92 | return obj; 93 | } 94 | 95 | optional getObj3(bool cond) 96 | { 97 | optional result; 98 | if (!cond) { 99 | return result; 100 | } 101 | result.emplace(42); 102 | result->op(); 103 | return result; 104 | } 105 | 106 | expected getObj4(bool cond) 107 | { 108 | if (!cond) { 109 | return unexpected(make_error_code(errc::invalid_argument)); 110 | } 111 | BigObj obj{42}; 112 | obj.op(); 113 | return obj; 114 | } 115 | 116 | expected getObj5(bool cond) 117 | { 118 | expected result{unexpected(error_code{})}; 119 | if (!cond) { 120 | result = 121 | unexpected(make_error_code(errc::invalid_argument)); 122 | return result; 123 | } 124 | result.emplace(42); 125 | result->op(); 126 | return result; 127 | } 128 | 129 | expected getObj6(bool cond) 130 | { 131 | expected result; 132 | if (!cond) { 133 | result = 134 | unexpected(make_error_code(errc::invalid_argument)); 135 | return result; 136 | } 137 | assert(result.has_value()); 138 | std::fill_n(result->data, size(result->data), 42); 139 | return result; 140 | } 141 | 142 | int main() // NOLINT(bugprone-exception-escape) 143 | { 144 | { 145 | cout << "*** Return directly w/ RVO\n"; 146 | auto obj = getObj1(true); 147 | } 148 | { 149 | cout << "*** Return optional w/o RVO\n"; 150 | auto obj = getObj2(true); 151 | } 152 | { 153 | cout << "*** Return optional w/ RVO\n"; 154 | auto obj = getObj3(true); 155 | } 156 | { 157 | cout << "*** Return expected w/o RVO\n"; 158 | auto obj = getObj4(true); 159 | } 160 | { 161 | cout << "*** Return expected w/ RVO\n"; 162 | auto obj = getObj5(true); 163 | } 164 | { 165 | auto obj = getObj6(true); 166 | assert(obj->data[99] == 42); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /16/async_future.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::async/launch 3 | #include // std::cout 4 | #include // std::this_thread::sleep_for 5 | 6 | using namespace std; 7 | 8 | int work() 9 | { 10 | this_thread::sleep_for(2s); 11 | return 42; 12 | } 13 | 14 | int main() 15 | { 16 | auto fut = async(launch::async, work); 17 | cout << "I am waiting now\n"; 18 | cout << "Answer: " << fut.get() << '\n'; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /16/bad_threaded_increment.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include "scoped_thread.h" // scoped_thread 3 | 4 | #define LOOPS 100000 5 | 6 | volatile int v; 7 | 8 | void increment(volatile int& n) 9 | { 10 | for (int i = 0; i < LOOPS; ++i) { 11 | n = n + 1; 12 | } 13 | } 14 | 15 | int main() 16 | { 17 | { 18 | scoped_thread t1{increment, std::ref(v)}; 19 | scoped_thread t2{increment, std::ref(v)}; 20 | } 21 | std::cout << "Result is " << v << std::endl; 22 | } 23 | -------------------------------------------------------------------------------- /16/cond_var.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::condition_variable 3 | #include // std::ref 4 | #include // std::cout 5 | #include // std::mutex/lock_guard/unique_lock 6 | #include // std::this_thread::sleep_for 7 | #include "scoped_thread.h" // scoped_thread 8 | 9 | using namespace std; 10 | 11 | void work(condition_variable& cv, mutex& cv_mut, 12 | bool& result_ready, int& result) 13 | { 14 | this_thread::sleep_for(2s); 15 | result = 42; 16 | { 17 | lock_guard guard{cv_mut}; 18 | result_ready = true; 19 | } 20 | cv.notify_one(); 21 | } 22 | 23 | int main() 24 | { 25 | condition_variable cv; 26 | mutex cv_mut; 27 | bool result_ready = false; 28 | int result{-1}; 29 | 30 | scoped_thread th{work, ref(cv), ref(cv_mut), 31 | ref(result_ready), ref(result)}; 32 | this_thread::sleep_for(2s); 33 | cout << "I am waiting now\n"; 34 | unique_lock lock{cv_mut}; 35 | cv.wait(lock, [&] { return result_ready; }); 36 | cout << "Answer: " << result << '\n'; 37 | } 38 | -------------------------------------------------------------------------------- /16/memory_reordering.cpp: -------------------------------------------------------------------------------- 1 | // Jeff Preshing's example that shows the reordering of Intel CPUs made to 2 | // work on both Linux and macOS. 3 | // 4 | // Modified by Wu Yongwei 5 | // 6 | // Original source: 7 | // 8 | // https://preshing.com/20120515/memory-reordering-caught-in-the-act/ 9 | // 10 | // Apple semaphore change is based on the answer by dho at: 11 | // 12 | // https://stackoverflow.com/questions/27736618/why-are-sem-init-sem-getvalue-sem-destroy-deprecated-on-mac-os-x-and-w 13 | // 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // Set either of these to 1 to prevent CPU reordering 21 | #define USE_CPU_FENCE 0 22 | #define USE_SINGLE_HW_THREAD 0 // Supported on Linux, but not Cygwin or PS3 23 | 24 | #if USE_SINGLE_HW_THREAD 25 | #include 26 | #endif 27 | 28 | #ifdef __APPLE__ 29 | #include 30 | #else 31 | #include 32 | #endif 33 | 34 | struct rk_sema { 35 | #ifdef __APPLE__ 36 | dispatch_semaphore_t sem; 37 | #else 38 | sem_t sem; 39 | #endif 40 | }; 41 | 42 | 43 | static inline void 44 | rk_sema_init(struct rk_sema *s, uint32_t value) 45 | { 46 | #ifdef __APPLE__ 47 | s->sem = dispatch_semaphore_create(value); 48 | #else 49 | sem_init(&s->sem, 0, value); 50 | #endif 51 | } 52 | 53 | static inline void 54 | rk_sema_wait(struct rk_sema *s) 55 | { 56 | 57 | #ifdef __APPLE__ 58 | dispatch_semaphore_wait(s->sem, DISPATCH_TIME_FOREVER); 59 | #else 60 | int r; 61 | 62 | do { 63 | r = sem_wait(&s->sem); 64 | } while (r == -1 && errno == EINTR); 65 | #endif 66 | } 67 | 68 | static inline void 69 | rk_sema_post(struct rk_sema *s) 70 | { 71 | 72 | #ifdef __APPLE__ 73 | dispatch_semaphore_signal(s->sem); 74 | #else 75 | sem_post(&s->sem); 76 | #endif 77 | } 78 | 79 | 80 | //------------------------------------- 81 | // MersenneTwister 82 | // A thread-safe random number generator with good randomness 83 | // in a small number of instructions. We'll use it to introduce 84 | // random timing delays. 85 | //------------------------------------- 86 | #define MT_IA 397 87 | #define MT_LEN 624 88 | 89 | class MersenneTwister 90 | { 91 | unsigned int m_buffer[MT_LEN]; 92 | int m_index; 93 | 94 | public: 95 | MersenneTwister(unsigned int seed); 96 | // Declare noinline so that the function call acts as a compiler barrier: 97 | unsigned int integer() __attribute__((noinline)); 98 | }; 99 | 100 | MersenneTwister::MersenneTwister(unsigned int seed) 101 | { 102 | // Initialize by filling with the seed, then iterating 103 | // the algorithm a bunch of times to shuffle things up. 104 | for (int i = 0; i < MT_LEN; i++) { 105 | m_buffer[i] = seed; 106 | } 107 | m_index = 0; 108 | for (int i = 0; i < MT_LEN * 100; i++) { 109 | integer(); 110 | } 111 | } 112 | 113 | unsigned int MersenneTwister::integer() 114 | { 115 | // Indices 116 | int i = m_index; 117 | int i2 = m_index + 1; 118 | if (i2 >= MT_LEN) { 119 | i2 = 0; // wrap-around 120 | } 121 | int j = m_index + MT_IA; 122 | if (j >= MT_LEN) { 123 | j -= MT_LEN; // wrap-around 124 | } 125 | 126 | // Twist 127 | unsigned int s = (m_buffer[i] & 0x80000000) | (m_buffer[i2] & 0x7fffffff); 128 | unsigned int r = m_buffer[j] ^ (s >> 1) ^ ((s & 1) * 0x9908B0DF); 129 | m_buffer[m_index] = r; 130 | m_index = i2; 131 | 132 | // Swizzle 133 | r ^= (r >> 11); 134 | r ^= (r << 7) & 0x9d2c5680UL; 135 | r ^= (r << 15) & 0xefc60000UL; 136 | r ^= (r >> 18); 137 | return r; 138 | } 139 | 140 | 141 | //------------------------------------- 142 | // Main program, as decribed in the post 143 | //------------------------------------- 144 | rk_sema beginSema1; 145 | rk_sema beginSema2; 146 | rk_sema endSema; 147 | 148 | int X; 149 | int Y; 150 | int r1, r2; 151 | 152 | void *thread1Func(void *param) 153 | { 154 | MersenneTwister random(1); 155 | for (;;) 156 | { 157 | rk_sema_wait(&beginSema1); // Wait for signal 158 | while (random.integer() % 8 != 0) {} // Random delay 159 | 160 | // ----- THE TRANSACTION! ----- 161 | X = 1; 162 | #if USE_CPU_FENCE 163 | asm volatile("mfence" ::: "memory"); // Prevent CPU reordering 164 | #else 165 | asm volatile("" ::: "memory"); // Prevent compiler reordering 166 | #endif 167 | r1 = Y; 168 | 169 | rk_sema_post(&endSema); // Notify transaction complete 170 | } 171 | return NULL; // Never returns 172 | }; 173 | 174 | void *thread2Func(void *param) 175 | { 176 | MersenneTwister random(2); 177 | for (;;) 178 | { 179 | rk_sema_wait(&beginSema2); // Wait for signal 180 | while (random.integer() % 8 != 0) {} // Random delay 181 | 182 | // ----- THE TRANSACTION! ----- 183 | Y = 1; 184 | #if USE_CPU_FENCE 185 | asm volatile("mfence" ::: "memory"); // Prevent CPU reordering 186 | #else 187 | asm volatile("" ::: "memory"); // Prevent compiler reordering 188 | #endif 189 | r2 = X; 190 | 191 | rk_sema_post(&endSema); // Notify transaction complete 192 | } 193 | return NULL; // Never returns 194 | }; 195 | 196 | int main() 197 | { 198 | // Initialize the semaphores 199 | rk_sema_init(&beginSema1, 0); 200 | rk_sema_init(&beginSema2, 0); 201 | rk_sema_init(&endSema, 0); 202 | 203 | // Spawn the threads 204 | pthread_t thread1; 205 | pthread_t thread2; 206 | pthread_create(&thread1, NULL, thread1Func, NULL); 207 | pthread_create(&thread2, NULL, thread2Func, NULL); 208 | 209 | #if USE_SINGLE_HW_THREAD 210 | // Force thread affinities to the same cpu core. 211 | cpu_set_t cpus; 212 | CPU_ZERO(&cpus); 213 | CPU_SET(0, &cpus); 214 | pthread_setaffinity_np(thread1, sizeof(cpu_set_t), &cpus); 215 | pthread_setaffinity_np(thread2, sizeof(cpu_set_t), &cpus); 216 | #endif 217 | 218 | // Repeat the experiment ad infinitum 219 | int detected = 0; 220 | for (int iterations = 1; ; iterations++) 221 | { 222 | // Reset X and Y 223 | X = 0; 224 | Y = 0; 225 | // Signal both threads 226 | rk_sema_post(&beginSema1); 227 | rk_sema_post(&beginSema2); 228 | // Wait for both threads 229 | rk_sema_wait(&endSema); 230 | rk_sema_wait(&endSema); 231 | // Check if there was a simultaneous reorder 232 | if (r1 == 0 && r2 == 0) 233 | { 234 | detected++; 235 | printf("%d reorders detected after %d iterations\n", detected, 236 | iterations); 237 | } 238 | } 239 | return 0; // Never returns 240 | } 241 | -------------------------------------------------------------------------------- /16/packaged_task.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::packaged_task 3 | #include // std::cout 4 | #include // std::this_thread::sleep_for 5 | #include // std::move 6 | #include "scoped_thread.h" // scoped_thread 7 | 8 | using namespace std; 9 | 10 | int work() 11 | { 12 | this_thread::sleep_for(2s); 13 | return 42; 14 | } 15 | 16 | int main() 17 | { 18 | packaged_task task{work}; 19 | auto fut = task.get_future(); 20 | scoped_thread th{std::move(task)}; 21 | this_thread::sleep_for(1s); 22 | cout << "I am waiting now\n"; 23 | cout << "Answer: " << fut.get() << '\n'; 24 | } 25 | -------------------------------------------------------------------------------- /16/packaged_task_sync.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::packaged_task 3 | #include // std::cout 4 | #include // std::this_thread::sleep_for 5 | 6 | using namespace std; 7 | 8 | int work() 9 | { 10 | this_thread::sleep_for(2s); 11 | return 42; 12 | } 13 | 14 | int main() 15 | { 16 | packaged_task task{work}; 17 | 18 | this_thread::sleep_for(1s); 19 | cout << "Calculating synchronously\n"; 20 | task(); 21 | cout << "Answer: " << task.get_future().get() << '\n'; 22 | task.reset(); 23 | task(); 24 | cout << "Answer: " << task.get_future().get() << '\n'; 25 | } 26 | -------------------------------------------------------------------------------- /16/promise.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::promise 3 | #include // std::cout 4 | #include // std::this_thread::sleep_for 5 | #include // std::move 6 | #include "scoped_thread.h" // scoped_thread 7 | 8 | using namespace std; 9 | 10 | void work(promise prom) 11 | { 12 | this_thread::sleep_for(2s); 13 | prom.set_value(42); 14 | } 15 | 16 | int main() 17 | { 18 | promise prom; 19 | auto fut = prom.get_future(); 20 | scoped_thread th{work, std::move(prom)}; 21 | cout << "I am waiting now\n"; 22 | cout << "Answer: " << fut.get() << '\n'; 23 | } 24 | -------------------------------------------------------------------------------- /16/thread.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::cout 3 | #include // std::mutex/lock_guard 4 | #include // std::thread/this_thread 5 | 6 | using namespace std; 7 | 8 | mutex output_lock; 9 | 10 | void func(const char* name) 11 | { 12 | this_thread::sleep_for(100ms); 13 | lock_guard guard{output_lock}; 14 | cout << "I am thread " << name << '\n'; 15 | } 16 | 17 | int main() 18 | { 19 | thread t1{func, "A"}; 20 | thread t2{func, "B"}; 21 | t1.join(); 22 | t2.join(); 23 | } 24 | -------------------------------------------------------------------------------- /16/thread_local_lifetime.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::thread 3 | 4 | using namespace std; 5 | 6 | class Obj1 { // NOLINT(cppcoreguidelines-special-member-functions) 7 | public: 8 | Obj1() { cout << "Obj1 is created\n"; } 9 | ~Obj1() { cout << "Obj1 is destroyed\n"; } 10 | void op() {} 11 | }; 12 | 13 | class Obj2 { // NOLINT(cppcoreguidelines-special-member-functions) 14 | public: 15 | ~Obj2() { cout << "Obj2 is destroyed\n"; } 16 | void op() {} 17 | }; 18 | 19 | thread_local Obj1 obj1; 20 | thread_local Obj2 obj2; 21 | 22 | int main() 23 | { 24 | cout << "In main\n"; 25 | std::thread{[] { 26 | cout << "In thread 1\n"; 27 | obj1.op(); 28 | cout << "Exiting thread 1\n"; 29 | }}.join(); 30 | std::thread{[] { 31 | cout << "In thread 2\n"; 32 | obj2.op(); 33 | obj1.op(); 34 | cout << "Exiting thread 2\n"; 35 | }}.join(); 36 | cout << "Exiting main\n"; 37 | } 38 | -------------------------------------------------------------------------------- /17/asio.h: -------------------------------------------------------------------------------- 1 | #ifndef ASIO_H 2 | #define ASIO_H 3 | 4 | #ifndef USE_BOOST_ASIO 5 | #if __has_include() 6 | #define USE_BOOST_ASIO 0 7 | #elif __has_include() 8 | #define USE_BOOST_ASIO 1 9 | #else 10 | #error "Asio is not detected" 11 | #endif 12 | #endif 13 | 14 | #if USE_BOOST_ASIO 15 | #include // boost::asio::io_context 16 | #include // boost::sytstem::error_code 17 | namespace asio = boost::asio; 18 | using boost::system::error_code; 19 | #else 20 | #include // asio::io_context 21 | #include // std::error_code 22 | using std::error_code; 23 | #endif 24 | 25 | #endif // ASIO_H 26 | -------------------------------------------------------------------------------- /17/asio_post.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::cout 3 | #include // std::this_thread::sleep_for 4 | #include "asio.h" // asio::* 5 | 6 | namespace this_thread = std::this_thread; 7 | using namespace std::chrono_literals; 8 | using std::cout; 9 | 10 | asio::io_context context; 11 | 12 | void doA(); 13 | void doB(); 14 | void doC(); 15 | 16 | void doA() 17 | { 18 | cout << "doA starts\n"; 19 | post(context, doB); 20 | post(context, doC); 21 | cout << "doA ends\n"; 22 | } 23 | 24 | void doB() 25 | { 26 | cout << "doB starts\n"; 27 | this_thread::sleep_for(10ms); 28 | cout << "doB ends\n"; 29 | } 30 | 31 | void doC() 32 | { 33 | cout << "doC starts\n"; 34 | this_thread::sleep_for(10ms); 35 | cout << "doC ends\n"; 36 | } 37 | 38 | int main() 39 | { 40 | post(context, doA); 41 | context.run(); 42 | } 43 | -------------------------------------------------------------------------------- /17/asio_post_mt.cpp: -------------------------------------------------------------------------------- 1 | #include // std::chrono_literals 2 | #include // std::cout 3 | #include // std::this_thread::sleep_for 4 | #include // std::vector 5 | #include "asio.h" // asio::* 6 | #include "scoped_thread.h" // scoped_thread 7 | 8 | namespace this_thread = std::this_thread; 9 | using namespace std::chrono_literals; 10 | using std::cout; 11 | using std::vector; 12 | 13 | asio::io_context context; 14 | 15 | void doA(); 16 | void doB(); 17 | void doC(); 18 | 19 | void doA() 20 | { 21 | cout << "doA starts\n"; 22 | post(context, doB); 23 | post(context, doC); 24 | cout << "doA ends\n"; 25 | } 26 | 27 | void doB() 28 | { 29 | cout << "doB starts\n"; 30 | this_thread::sleep_for(10ms); 31 | cout << "doB ends\n"; 32 | } 33 | 34 | void doC() 35 | { 36 | cout << "doC starts\n"; 37 | this_thread::sleep_for(10ms); 38 | cout << "doC ends\n"; 39 | } 40 | 41 | int main() 42 | { 43 | post(context, doA); 44 | vector threads; 45 | threads.emplace_back([] { context.run(); }); 46 | threads.emplace_back([] { context.run(); }); 47 | } 48 | -------------------------------------------------------------------------------- /17/asio_timer.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::make_shared 3 | #include // std::error_code 4 | #include "asio.h" // asio::* 5 | 6 | using namespace std::chrono_literals; 7 | using std::cout; 8 | using std::make_shared; 9 | 10 | asio::io_context context; 11 | 12 | void sayHello() 13 | { 14 | cout << "Hello, world!\n"; 15 | } 16 | 17 | void doA() 18 | { 19 | cout << "doA starts\n"; 20 | auto t = make_shared(context, 2s); 21 | t->async_wait([t](const error_code& ec) { 22 | if (ec == asio::error::operation_aborted) { 23 | return; 24 | } 25 | sayHello(); 26 | }); 27 | 28 | //t->cancel(); 29 | cout << "doA ends\n"; 30 | } 31 | 32 | int main() 33 | { 34 | post(context, doA); 35 | context.run(); 36 | } 37 | -------------------------------------------------------------------------------- /17/echo_server_async.cpp: -------------------------------------------------------------------------------- 1 | #include // std::exception 2 | #include // std::cerr 3 | #include // std::make_shared/... 4 | #include // std::move 5 | #include // size_t 6 | #include // uint16_t 7 | #include "asio.h" // asio::* 8 | 9 | using asio::buffer; 10 | using asio::ip::tcp; 11 | using std::cerr; 12 | using std::enable_shared_from_this; 13 | using std::exception; 14 | using std::make_shared; 15 | 16 | class Session : public enable_shared_from_this { 17 | public: 18 | Session(tcp::socket socket) : socket_(std::move(socket)) {} 19 | 20 | void start() { doRead(); } 21 | 22 | private: 23 | void doRead() 24 | { 25 | auto self = shared_from_this(); 26 | socket_.async_read_some( 27 | buffer(data_), 28 | [this, self](error_code ec, size_t length) { 29 | if (!ec) { 30 | doWrite(length); 31 | } else if (ec == asio::error::eof) { 32 | cerr << "Session done\n"; 33 | } else { 34 | cerr << "Error: " << ec.message() << "\n"; 35 | } 36 | }); 37 | } 38 | 39 | void doWrite(size_t length) 40 | { 41 | auto self = shared_from_this(); 42 | asio::async_write( 43 | socket_, buffer(data_, length), 44 | [this, self](error_code ec, size_t /*length*/) { 45 | if (!ec) { 46 | doRead(); 47 | } else { 48 | cerr << "Error: " << ec.message() << "\n"; 49 | } 50 | }); 51 | } 52 | 53 | tcp::socket socket_; 54 | char data_[1024]; 55 | }; 56 | 57 | class Server { 58 | public: 59 | Server(asio::io_context& context, uint16_t port) 60 | : acceptor_(context, tcp::endpoint(tcp::v4(), port)) 61 | { 62 | doAccept(); 63 | } 64 | 65 | private: 66 | void doAccept() 67 | { 68 | acceptor_.async_accept([this](error_code ec, 69 | tcp::socket socket) { 70 | if (!ec) { 71 | make_shared(std::move(socket))->start(); 72 | } 73 | doAccept(); 74 | }); 75 | } 76 | 77 | tcp::acceptor acceptor_; 78 | }; 79 | 80 | int main() 81 | { 82 | asio::io_context context(1); 83 | try { 84 | Server s(context, 6667); 85 | context.run(); 86 | } 87 | catch (const exception& e) { 88 | cerr << "Exception: " << e.what() << "\n"; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /17/echo_server_coroutine.cpp: -------------------------------------------------------------------------------- 1 | #include // std::exception 2 | #include // std::cerr 3 | #include // std::move 4 | #include // size_t 5 | #include "asio.h" // asio::* 6 | 7 | using asio::awaitable; 8 | using asio::buffer; 9 | using asio::co_spawn; 10 | using asio::detached; 11 | using asio::use_awaitable; 12 | using asio::ip::tcp; 13 | using std::cerr; 14 | using std::exception; 15 | 16 | #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING) 17 | #define use_awaitable \ 18 | use_awaitable_t(__FILE__, __LINE__, __PRETTY_FUNCTION__) 19 | #endif 20 | 21 | awaitable processConnection(tcp::socket socket) 22 | { 23 | char data[1024]; 24 | for (;;) { 25 | error_code ec; 26 | size_t len = co_await socket.async_read_some( 27 | buffer(data), redirect_error(use_awaitable, ec)); 28 | if (ec) { 29 | if (ec == asio::error::eof) { 30 | cerr << "Session done\n"; 31 | } else { 32 | cerr << "Error: " << ec.message() << "\n"; 33 | } 34 | break; 35 | } 36 | co_await async_write( 37 | socket, buffer(data, len), 38 | redirect_error(use_awaitable, ec)); 39 | if (ec) { 40 | cerr << "Error: " << ec.message() << "\n"; 41 | break; 42 | } 43 | } 44 | } 45 | 46 | awaitable listener() 47 | { 48 | try { 49 | auto executor = co_await asio::this_coro::executor; 50 | tcp::acceptor acceptor(executor, 51 | tcp::endpoint(tcp::v4(), 6667)); 52 | int count{}; 53 | for (;;) { 54 | tcp::socket socket = 55 | co_await acceptor.async_accept(use_awaitable); 56 | co_spawn(executor, 57 | processConnection(std::move(socket)), 58 | detached); 59 | if (++count == 1000) { 60 | break; 61 | } 62 | } 63 | } 64 | catch (const exception& e) { 65 | cerr << "Exception: " << e.what() << "\n"; 66 | } 67 | } 68 | 69 | int main() 70 | { 71 | try { 72 | asio::io_context context(1); 73 | co_spawn(context, listener, detached); 74 | context.run(); 75 | } 76 | catch (const exception& e) { 77 | cerr << "Exception: " << e.what() << "\n"; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /17/echo_server_mt.cpp: -------------------------------------------------------------------------------- 1 | #include // std::exception 2 | #include // std::cerr 3 | #include // std::thread 4 | #include // std::move 5 | #include // size_t 6 | #include "asio.h" // asio::* 7 | 8 | using asio::buffer; 9 | using asio::ip::tcp; 10 | using std::cerr; 11 | using std::exception; 12 | using std::thread; 13 | 14 | void processConnection(tcp::socket peer) 15 | { 16 | char data[1024]; 17 | for (;;) { 18 | error_code ec; 19 | size_t len = peer.read_some(buffer(data), ec); 20 | if (ec) { 21 | if (ec == asio::error::eof) { 22 | cerr << "Session done\n"; 23 | } else { 24 | cerr << "Error: " << ec.message() << "\n"; 25 | } 26 | break; 27 | } 28 | write(peer, buffer(data, len), ec); 29 | if (ec) { 30 | cerr << "Error: " << ec.message() << '\n'; 31 | break; 32 | } 33 | } 34 | } 35 | 36 | int main() 37 | { 38 | asio::io_context context; 39 | try { 40 | tcp::acceptor acceptor( 41 | context, tcp::endpoint(tcp::v4(), 6667)); 42 | 43 | for (;;) { 44 | tcp::socket peer(context); 45 | acceptor.accept(peer); 46 | thread(processConnection, std::move(peer)).detach(); 47 | } 48 | } 49 | catch (const exception& e) { 50 | cerr << "Exception: " << e.what() << "\n"; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /17/echo_server_st.cpp: -------------------------------------------------------------------------------- 1 | #include // std::exception 2 | #include // std::cerr 3 | #include // std::move 4 | #include // size_t 5 | #include "asio.h" // asio::* 6 | 7 | using asio::buffer; 8 | using asio::ip::tcp; 9 | using std::cerr; 10 | using std::exception; 11 | 12 | void processConnection(tcp::socket peer) 13 | { 14 | char data[1024]; 15 | for (;;) { 16 | error_code ec; 17 | size_t len = peer.read_some(buffer(data), ec); 18 | if (ec) { 19 | if (ec == asio::error::eof) { 20 | cerr << "Session done\n"; 21 | } else { 22 | cerr << "Error: " << ec.message() << "\n"; 23 | } 24 | break; 25 | } 26 | write(peer, buffer(data, len)); 27 | } 28 | } 29 | 30 | int main() 31 | { 32 | asio::io_context context; 33 | try { 34 | tcp::acceptor acceptor( 35 | context, tcp::endpoint(tcp::v4(), 6667)); 36 | 37 | for (;;) { 38 | tcp::socket peer(context); 39 | acceptor.accept(peer); 40 | processConnection(std::move(peer)); 41 | } 42 | } 43 | catch (const exception& e) { 44 | cerr << "Exception: " << e.what() << "\n"; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /17/fake_io_context.cpp: -------------------------------------------------------------------------------- 1 | #include "fake_io_context.h" // asio::io_context (fake) 2 | #include // std::chrono_literals 3 | #include // std::cout 4 | #include // std::this_thread::sleep_for 5 | #include // std::vector 6 | #include "scoped_thread.h" // scoped_thread 7 | 8 | namespace this_thread = std::this_thread; 9 | using namespace std::chrono_literals; 10 | using std::cout; 11 | using std::vector; 12 | 13 | asio::io_context context; 14 | 15 | void doA(); 16 | void doB(); 17 | void doC(const int& n); 18 | 19 | void doA() 20 | { 21 | cout << "doA starts\n"; 22 | int answer = 42; 23 | post(context, doB); 24 | post(context, [answer] { doC(answer); }); 25 | cout << "doA ends\n"; 26 | } 27 | 28 | void doB() 29 | { 30 | cout << "doB starts\n"; 31 | this_thread::sleep_for(10ms); 32 | cout << "doB ends\n"; 33 | } 34 | 35 | void doC(const int& n) 36 | { 37 | cout << "doC starts with " << n << " \n"; 38 | this_thread::sleep_for(10ms); 39 | cout << "doC ends\n"; 40 | } 41 | 42 | int main() 43 | { 44 | post(context, doA); 45 | vector threads; 46 | threads.emplace_back([] { context.run(); }); 47 | threads.emplace_back([] { context.run(); }); 48 | } 49 | -------------------------------------------------------------------------------- /17/fake_io_context.h: -------------------------------------------------------------------------------- 1 | #ifndef FAKE_IO_CONTEXT_H 2 | #define FAKE_IO_CONTEXT_H 3 | 4 | #include // std::condition_variable 5 | #include // std::function 6 | #include // std::mutex/lock_guard/unique_lock 7 | #include // std::queue 8 | 9 | namespace asio { 10 | 11 | using std::condition_variable; 12 | using std::function; 13 | using std::lock_guard; 14 | using std::mutex; 15 | using std::queue; 16 | using std::unique_lock; 17 | 18 | class io_context { 19 | public: 20 | using task_t = function; 21 | 22 | void post(task_t task) 23 | { 24 | { 25 | lock_guard guard{mtx_}; 26 | task_queue_.push(std::move(task)); 27 | } 28 | state_.notify_one(); 29 | } 30 | 31 | void run() 32 | { 33 | bool is_my_last_task{}; 34 | for (;;) { 35 | task_t task; 36 | { 37 | unique_lock lock{mtx_}; 38 | if (is_my_last_task) { 39 | is_my_last_task = false; 40 | --last_tasks_executing_; 41 | } 42 | state_.wait(lock, [&] { 43 | return !task_queue_.empty() || 44 | last_tasks_executing_ == 0; 45 | }); 46 | if (task_queue_.empty() && 47 | last_tasks_executing_ == 0) { 48 | break; 49 | } 50 | task = std::move(task_queue_.front()); 51 | task_queue_.pop(); 52 | if (task_queue_.empty()) { 53 | is_my_last_task = true; 54 | ++last_tasks_executing_; 55 | } 56 | } 57 | task(); 58 | } 59 | // Wake other threads potentially waiting 60 | state_.notify_one(); 61 | } 62 | 63 | private: 64 | mutex mtx_; 65 | condition_variable state_; 66 | queue task_queue_; 67 | int last_tasks_executing_; 68 | }; 69 | 70 | inline void post(io_context& ctx, io_context::task_t task) 71 | { 72 | ctx.post(std::move(task)); 73 | } 74 | 75 | } // namespace asio 76 | 77 | #endif // FAKE_IO_CONTEXT_H 78 | -------------------------------------------------------------------------------- /17/fibonacci_generator.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::tie/tuple 3 | #include // std::views::* 4 | #include // __cpp_lib_generator 5 | #include // uint64_t 6 | 7 | #if __cpp_lib_generator >= 202207L 8 | #include // std::generator 9 | using std::generator; 10 | #elif __has_include() 11 | #include // cppcoro::generator 12 | using cppcoro::generator; 13 | #else 14 | #error "No generator suport is detected" 15 | #endif 16 | 17 | namespace views = std::views; 18 | using std::cout; 19 | 20 | generator fibonacci() 21 | { 22 | uint64_t a = 0; 23 | uint64_t b = 1; 24 | for (;;) { 25 | co_yield b; 26 | std::tie(a, b) = std::tuple(b, a + b); 27 | } 28 | } 29 | 30 | int main() 31 | { 32 | for (auto i : fibonacci() | views::take(20)) { 33 | cout << i << '\n'; 34 | } 35 | for (auto i : fibonacci() | views::take_while([](uint64_t n) { 36 | return n < 10000; 37 | })) { 38 | cout << i << '\n'; 39 | } 40 | for (auto i : fibonacci() | views::drop_while([](uint64_t n) { 41 | return n < 10000; 42 | })) { 43 | cout << i << '\n'; 44 | break; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /17/fibonacci_manual_iteration.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::input_iterator_tag 3 | #include // std::views::* 4 | #include // std::tie/tuple 5 | #include // ptrdiff_t 6 | #include // uint64_t 7 | 8 | class fibonacci { 9 | public: 10 | class sentinel; 11 | class iterator; 12 | iterator begin() noexcept; 13 | sentinel end() noexcept; 14 | }; 15 | 16 | class fibonacci::sentinel {}; 17 | 18 | class fibonacci::iterator { 19 | public: 20 | typedef ptrdiff_t difference_type; 21 | typedef uint64_t value_type; 22 | typedef const uint64_t* pointer; 23 | typedef const uint64_t& reference; 24 | typedef std::input_iterator_tag iterator_category; 25 | 26 | value_type operator*() const { return b_; } 27 | pointer operator->() const { return &b_; } 28 | iterator& operator++() 29 | { 30 | std::tie(a_, b_) = std::tuple(b_, a_ + b_); 31 | return *this; 32 | } 33 | iterator operator++(int) 34 | { 35 | auto tmp = *this; 36 | ++*this; 37 | return tmp; 38 | } 39 | bool operator==(const sentinel&) const { return false; } 40 | bool operator!=(const sentinel&) const { return true; } 41 | 42 | private: 43 | uint64_t a_{0}; 44 | uint64_t b_{1}; 45 | }; 46 | 47 | inline bool operator==(const fibonacci::sentinel& lhs, 48 | const fibonacci::iterator& rhs) 49 | { 50 | return rhs == lhs; 51 | } 52 | inline bool operator!=(const fibonacci::sentinel& lhs, 53 | const fibonacci::iterator& rhs) 54 | { 55 | return rhs != lhs; 56 | } 57 | 58 | inline fibonacci::iterator fibonacci::begin() noexcept 59 | { 60 | return iterator(); 61 | } 62 | 63 | inline fibonacci::sentinel fibonacci::end() noexcept 64 | { 65 | return sentinel(); 66 | } 67 | 68 | namespace std::ranges { 69 | 70 | template <> 71 | inline constexpr bool enable_borrowed_range = true; 72 | 73 | } // namespace std::ranges 74 | 75 | namespace views = std::views; 76 | using std::cout; 77 | 78 | int main() 79 | { 80 | for (auto i : fibonacci() | views::take(20)) { 81 | cout << i << '\n'; 82 | } 83 | for (auto i : fibonacci() | views::take_while([](uint64_t n) { 84 | return n < 10000; 85 | })) { 86 | cout << i << '\n'; 87 | } 88 | for (auto i : fibonacci() | views::drop_while([](uint64_t n) { 89 | return n < 10000; 90 | })) { 91 | cout << i << '\n'; 92 | break; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /17/stream_client.cpp: -------------------------------------------------------------------------------- 1 | #include // std::exception 2 | #include // std::cout 3 | #include // std::string/getline 4 | #include // std::thread 5 | #include "asio.h" // asio::* 6 | 7 | using asio::ip::tcp; 8 | using std::cin; 9 | using std::cout; 10 | using std::exception; 11 | using std::getline; 12 | using std::string; 13 | using std::thread; 14 | 15 | int main(int argc, char* argv[]) 16 | { 17 | try { 18 | if (argc != 3) { 19 | cout << "Usage: " << argv[0] << " \n"; 20 | return 1; 21 | } 22 | 23 | tcp::iostream s; 24 | s.connect(argv[1], argv[2]); 25 | if (!s) { 26 | cout << "Unable to connect: " << s.error().message() 27 | << "\n"; 28 | return 2; 29 | } 30 | 31 | thread thrd{[&s] { 32 | #ifndef _CPPLIB_VER 33 | cout << s.rdbuf(); 34 | // This simple method does not work properly with the 35 | // MSVC standard library. 36 | #else 37 | // Thus this workaround. 38 | char ch{}; 39 | while (s.get(ch)) { 40 | cout << ch; 41 | } 42 | #endif 43 | }}; 44 | 45 | string line; 46 | for (;;) { 47 | getline(cin, line); 48 | if (!cin) { 49 | break; 50 | } 51 | s << line << "\r\n"; 52 | } 53 | 54 | s.socket().shutdown(tcp::socket::shutdown_send); 55 | thrd.join(); 56 | } 57 | catch (const exception& e) { 58 | cout << "Exception: " << e.what() << "\n"; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /18/clang-tidy-demo.cpp: -------------------------------------------------------------------------------- 1 | #include // NULL 2 | 3 | struct St1 { 4 | int v1; 5 | int v2; 6 | }; 7 | 8 | struct St2 { 9 | int v1{}; 10 | int v2; 11 | }; 12 | 13 | struct St3 { 14 | int v1{}; 15 | int v2{}; 16 | }; 17 | 18 | class Shape { 19 | public: 20 | virtual ~Shape() = default; 21 | }; 22 | 23 | class ShapeWrapper { 24 | public: 25 | explicit ShapeWrapper(Shape* ptr = NULL) : ptr_(ptr) {} 26 | ~ShapeWrapper() { delete ptr_; } 27 | Shape* get() const { return ptr_; } 28 | 29 | private: 30 | Shape* ptr_; 31 | }; 32 | 33 | int main() 34 | { 35 | St1 st1; 36 | St2 st2; 37 | St3 BadName; 38 | } 39 | -------------------------------------------------------------------------------- /18/past_the_end_iterator.cpp: -------------------------------------------------------------------------------- 1 | #include // std::cout 2 | #include // std::string 3 | #include // std::vector 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | cout << boolalpha; 10 | 11 | string s{"Hello"}; 12 | cout << "(*s.end() == '\\0'): " << (*s.end() == '\0') << '\n'; 13 | 14 | vector v{1, 2, 3}; 15 | cout << "*v.end() = " << *v.end() << '\n'; 16 | } 17 | 18 | // MSVC can capture both past-the-end dereferences in debug builds. 19 | // 20 | // Clang/libc++ can capture both past-the-end dereferences by defining the 21 | // macro _LIBCPP_DEBUG (to 1). 22 | // 23 | // GCC/libstdc++ can capture the second dereference (only) by defining the 24 | // macro _GLIBCXX_DEBUG. This is by design: 25 | // https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_design.html 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《C++ 实战:核心技术与最佳实践》 2 | 3 | 这里是《C++ 实战:核心技术与最佳实践》一书的 GitHub 仓库。目前这里存放了该书的代码示例。 4 | 5 | 如果你发现书里的文字或代码有问题,欢迎直接提交 issue。 6 | 7 | 要了解本书的更多内容,可以参考图灵图书网站里关于本书的介绍(其中包含了样章的下载): 8 | 9 | 10 | -------------------------------------------------------------------------------- /common/finally.h: -------------------------------------------------------------------------------- 1 | #ifndef FINALLY_H 2 | #define FINALLY_H 3 | 4 | #include // std::uncaught_exceptions 5 | #include // std::decay 6 | #include // std::exchange/forward/move 7 | 8 | template 9 | class final_action { 10 | public: 11 | explicit final_action(T action) : action_(std::move(action)) {} 12 | 13 | final_action(final_action&& other) noexcept 14 | : action_(std::move(other.action_)), 15 | is_active_(std::exchange(other.is_active_, false)) 16 | { 17 | } 18 | 19 | final_action(const final_action&) = delete; 20 | final_action& operator=(const final_action&) = delete; 21 | final_action& operator=(final_action&&) = delete; 22 | 23 | ~final_action() 24 | { 25 | if (is_active_) { 26 | action_(); 27 | } 28 | } 29 | 30 | void dismiss() noexcept 31 | { 32 | is_active_ = false; 33 | } 34 | 35 | private: 36 | T action_; 37 | bool is_active_{true}; 38 | }; 39 | 40 | template 41 | auto finally(T&& action) 42 | { 43 | return final_action>(std::forward(action)); 44 | } 45 | 46 | #if __cpp_lib_uncaught_exceptions >= 201411L 47 | 48 | // Similar to final_action but with the key difference that the 49 | // contained action will not be invoked during exception propagation. 50 | // Requires C++17 or later. 51 | template 52 | class on_return { 53 | public: 54 | explicit on_return(T action) 55 | : action_(std::move(action)), 56 | uncaught_exceptions_on_entry_(std::uncaught_exceptions()) 57 | { 58 | } 59 | 60 | on_return(const on_return&) = delete; 61 | on_return(on_return&&) = delete; 62 | on_return& operator=(const on_return&) = delete; 63 | on_return& operator=(on_return&&) = delete; 64 | 65 | ~on_return() 66 | { 67 | if (std::uncaught_exceptions() == uncaught_exceptions_on_entry_) { 68 | action_(); 69 | } 70 | } 71 | 72 | private: 73 | T action_; 74 | int uncaught_exceptions_on_entry_; 75 | }; 76 | 77 | #endif 78 | 79 | #endif // FINALLY_H 80 | -------------------------------------------------------------------------------- /common/ostream_range.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Written by Wu Yongwei . 3 | * 4 | * Using this file requires a C++17-compliant compiler. 5 | * 6 | * This is free and unencumbered software released into the public domain. 7 | * 8 | * Anyone is free to copy, modify, publish, use, compile, sell, or 9 | * distribute this software, either in source code form or as a compiled 10 | * binary, for any purpose, commercial or non-commercial, and by any 11 | * means. 12 | * 13 | * In jurisdictions that recognize copyright laws, the author or authors 14 | * of this software dedicate any and all copyright interest in the 15 | * software to the public domain. We make this dedication for the benefit 16 | * of the public at large and to the detriment of our heirs and 17 | * successors. We intend this dedication to be an overt act of 18 | * relinquishment in perpetuity of all present and future rights to this 19 | * software under copyright law. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | * 29 | * For more information, please refer to 30 | * 31 | */ 32 | 33 | #ifndef OSTREAM_RANGE_H 34 | #define OSTREAM_RANGE_H 35 | 36 | #include // std::byte 37 | #include // std::begin/end 38 | #include // std::ostream 39 | #include // std::tuple/tuple_size 40 | #include // std::false_type/true_type/decay_t/is_same_v/remove_... 41 | #include // std::declval/pair 42 | 43 | #ifndef OSTREAM_RANGE_NO_STRING_QUOTE 44 | #include // std::string 45 | #include // std::string_view 46 | #endif 47 | 48 | namespace ostream_range { 49 | 50 | using std::begin; 51 | using std::end; 52 | 53 | template 54 | auto adl_begin(Rng&& rng) -> decltype(begin(rng)) 55 | { 56 | // Intentionally not using std::forward, as std::begin does not 57 | // accept an rvalue reference. 58 | return begin(rng); 59 | } 60 | 61 | template 62 | auto adl_end(Rng&& rng) -> decltype(end(rng)) 63 | { 64 | // Intentionally not using std::forward, as std::end does not 65 | // accept an rvalue reference. 66 | return end(rng); 67 | } 68 | 69 | // Type trait to detect std::pair 70 | template 71 | struct is_pair : std::false_type {}; 72 | template 73 | struct is_pair> : std::true_type {}; 74 | template 75 | inline constexpr bool is_pair_v = is_pair::value; 76 | 77 | // Type trait for tuple-like objects 78 | template 79 | struct is_tuple_like : std::false_type {}; 80 | template 81 | struct is_tuple_like::value)>> 82 | : std::true_type {}; 83 | template 84 | inline constexpr bool is_tuple_like_v = is_tuple_like::value; 85 | 86 | // Type traits for ranges 87 | template 88 | struct is_range : std::false_type {}; 89 | template 90 | struct is_range()), 91 | adl_end(std::declval()))>> 92 | : std::true_type {}; 93 | template 94 | inline constexpr bool is_range_v = is_range::value; 95 | 96 | // Type trait to detect whether an output function already exists 97 | template 98 | struct has_output_function { 99 | template 100 | static auto output(U* ptr) 101 | -> decltype(std::declval() << *ptr, 102 | std::true_type{}); 103 | template 104 | static std::false_type output(...); 105 | static constexpr bool value = 106 | decltype(output(nullptr))::value; 107 | }; 108 | #ifndef OSTREAM_RANGE_NO_ARRAY_OUTPUT 109 | template 110 | struct has_output_function : std::false_type {}; 111 | template 112 | struct has_output_function : std::true_type {}; 113 | template 114 | struct has_output_function : std::true_type {}; 115 | template 116 | struct has_output_function : std::true_type {}; 117 | template 118 | struct has_output_function : std::true_type {}; 119 | template 120 | struct has_output_function : std::true_type {}; 121 | template 122 | struct has_output_function : std::true_type {}; 123 | #endif 124 | template 125 | inline constexpr bool has_output_function_v = 126 | has_output_function::value; 127 | 128 | // Element output function for containers that define a key_type and 129 | // have its value type as std::pair 130 | template 131 | auto output_element(std::ostream& os, const T& element, 132 | const Rng&, std::true_type) 133 | -> decltype(std::declval(), os); 134 | // Element output function for other containers 135 | template 136 | auto output_element(std::ostream& os, const T& element, 137 | const Rng&, ...) 138 | -> decltype(os); 139 | 140 | // Member output function for tuple-like objects 141 | template 142 | void output_tuple_members(std::ostream& os, const Tup& tup, 143 | std::index_sequence); 144 | 145 | } // namespace ostream_range 146 | 147 | // Output function for tuple-like objects 148 | template && 149 | !ostream_range::is_range_v), 150 | bool> = true> 151 | std::ostream& operator<<(std::ostream& os, const T& tup); 152 | 153 | // Main output function, enabled only if no output function already exists 154 | template < 155 | typename Rng, 156 | std::enable_if_t<(ostream_range::is_range_v && 157 | !ostream_range::has_output_function_v< 158 | std::remove_cv_t>>), 159 | bool> = true> 160 | std::ostream& operator<<(std::ostream& os, Rng&& rng) 161 | { 162 | using std::decay_t; 163 | using std::is_same_v; 164 | 165 | using element_type = decay_t; 166 | os << '{'; 167 | auto end = ostream_range::adl_end(rng); 168 | bool on_first_element = true; 169 | for (auto it = ostream_range::adl_begin(rng); it != end; ++it) { 170 | if (!on_first_element) { 171 | os << ", "; 172 | } else { 173 | os << ' '; 174 | on_first_element = false; 175 | } 176 | ostream_range::output_element( 177 | os, *it, rng, ostream_range::is_pair{}); 178 | } 179 | if (!on_first_element) { // Not empty 180 | os << ' '; 181 | } 182 | os << '}'; 183 | return os; 184 | } 185 | 186 | template && 187 | !ostream_range::is_range_v), 188 | bool>> 189 | std::ostream& operator<<(std::ostream& os, const T& tup) 190 | { 191 | os << '('; 192 | ostream_range::output_tuple_members( 193 | os, tup, std::make_index_sequence>{}); 194 | os << ')'; 195 | return os; 196 | } 197 | 198 | namespace ostream_range { 199 | 200 | template 201 | auto output_element(std::ostream& os, const T& element, const Rng&, 202 | std::true_type) 203 | -> decltype(std::declval(), os) 204 | { 205 | output_element(os, element.first, element); 206 | os << " => "; 207 | output_element(os, element.second, element); 208 | return os; 209 | } 210 | 211 | template 212 | auto output_element(std::ostream& os, const T& element, const Rng&, 213 | ...) 214 | -> decltype(os) 215 | { 216 | if constexpr (std::is_same_v || 217 | std::is_same_v) { 218 | os << '\'' << element << '\''; 219 | } else if constexpr (std::is_same_v || 220 | std::is_same_v) { 221 | os << static_cast(element); 222 | } else 223 | #ifndef OSTREAM_RANGE_NO_STRING_QUOTE 224 | { 225 | using DT = std::decay_t; 226 | using PT = std::remove_cv_t>; 227 | if constexpr (std::is_same_v || 228 | std::is_same_v || 229 | (std::is_pointer_v
&& 230 | (std::is_same_v || 231 | std::is_same_v || 232 | std::is_same_v))) { 233 | os << '"' << element << '"'; 234 | } else { 235 | os << element; 236 | } 237 | } 238 | #else 239 | { 240 | os << element; 241 | } 242 | #endif 243 | return os; 244 | } 245 | 246 | template 247 | void output_tuple_members(std::ostream& os, const Tup& tup, 248 | std::index_sequence) 249 | { 250 | using std::get; 251 | ((os << (Is != 0 ? ", " : ""), output_element(os, get(tup), tup)), 252 | ...); 253 | } 254 | 255 | } // namespace ostream_range 256 | 257 | #endif // OSTREAM_RANGE_H 258 | -------------------------------------------------------------------------------- /common/scoped_thread.h: -------------------------------------------------------------------------------- 1 | #ifndef SCOPED_THREAD_H 2 | #define SCOPED_THREAD_H 3 | 4 | #include // std::thread 5 | #include // std::forward 6 | 7 | class scoped_thread { 8 | public: 9 | template 10 | scoped_thread(Arg&&... arg) 11 | : thread_(std::forward(arg)...) 12 | { 13 | } 14 | scoped_thread(const scoped_thread&) = delete; 15 | scoped_thread& operator=(const scoped_thread&) = delete; 16 | scoped_thread(scoped_thread&&) = default; 17 | scoped_thread& operator=(scoped_thread&&) = default; 18 | ~scoped_thread() 19 | { 20 | if (thread_.joinable()) { 21 | thread_.join(); 22 | } 23 | } 24 | 25 | private: 26 | std::thread thread_; 27 | }; 28 | 29 | #endif // SCOPED_THREAD_H 30 | --------------------------------------------------------------------------------