├── .clang-format ├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── exercises ├── 00_hello_world │ └── main.cpp ├── 01_variable&add │ └── main.cpp ├── 02_function │ └── main.cpp ├── 03_argument¶meter │ └── main.cpp ├── 04_static │ └── main.cpp ├── 05_constexpr │ └── main.cpp ├── 06_array │ └── main.cpp ├── 07_loop │ └── main.cpp ├── 08_pointer │ └── main.cpp ├── 09_enum&union │ └── main.cpp ├── 10_trivial │ └── main.cpp ├── 11_method │ └── main.cpp ├── 12_method_const │ └── main.cpp ├── 13_class │ └── main.cpp ├── 14_class_destruct │ └── main.cpp ├── 15_class_clone │ └── main.cpp ├── 16_class_move │ └── main.cpp ├── 17_class_derive │ └── main.cpp ├── 18_class_virtual │ └── main.cpp ├── 19_class_virtual_destruct │ └── main.cpp ├── 20_function_template │ └── main.cpp ├── 21_runtime_datatype │ └── main.cpp ├── 22_class_template │ └── main.cpp ├── 23_template_const │ └── main.cpp ├── 24_std_array │ └── main.cpp ├── 25_std_vector │ └── main.cpp ├── 26_std_vector_bool │ └── main.cpp ├── 27_strides │ └── main.cpp ├── 28_std_string │ └── main.cpp ├── 29_std_map │ └── main.cpp ├── 30_std_unique_ptr │ └── main.cpp ├── 31_std_shared_ptr │ └── main.cpp ├── 32_std_transform │ └── main.cpp ├── 33_std_accumulate │ └── main.cpp ├── exercise.h └── xmake.lua ├── learn ├── learn.cpp ├── summary.cpp ├── test.cpp └── test.h └── xmake.lua /.clang-format: -------------------------------------------------------------------------------- 1 | # Generated from CLion C/C++ Code Style settings 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | # AlignConsecutiveAssignments: None 6 | AlignOperands: Align 7 | AllowAllArgumentsOnNextLine: false 8 | AllowAllConstructorInitializersOnNextLine: false 9 | AllowAllParametersOfDeclarationOnNextLine: false 10 | AllowShortBlocksOnASingleLine: Always 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: Always 14 | AllowShortLambdasOnASingleLine: All 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakTemplateDeclarations: No 18 | BreakBeforeBraces: Custom 19 | BraceWrapping: 20 | AfterCaseLabel: false 21 | AfterClass: false 22 | AfterControlStatement: Never 23 | AfterEnum: false 24 | AfterFunction: false 25 | AfterNamespace: false 26 | AfterUnion: false 27 | BeforeCatch: false 28 | BeforeElse: false 29 | IndentBraces: false 30 | SplitEmptyFunction: false 31 | SplitEmptyRecord: true 32 | BreakBeforeBinaryOperators: None 33 | BreakBeforeTernaryOperators: true 34 | BreakConstructorInitializers: BeforeColon 35 | BreakInheritanceList: BeforeColon 36 | ColumnLimit: 0 37 | CompactNamespaces: true 38 | ContinuationIndentWidth: 4 39 | IndentCaseLabels: true 40 | IndentPPDirectives: None 41 | IndentWidth: 4 42 | KeepEmptyLinesAtTheStartOfBlocks: true 43 | MaxEmptyLinesToKeep: 2 44 | NamespaceIndentation: All 45 | ObjCSpaceAfterProperty: false 46 | ObjCSpaceBeforeProtocolList: true 47 | PointerAlignment: Right 48 | ReflowComments: false 49 | SpaceAfterCStyleCast: true 50 | SpaceAfterLogicalNot: false 51 | SpaceAfterTemplateKeyword: false 52 | SpaceBeforeAssignmentOperators: true 53 | SpaceBeforeCpp11BracedList: false 54 | SpaceBeforeCtorInitializerColon: true 55 | SpaceBeforeInheritanceColon: true 56 | SpaceBeforeParens: ControlStatements 57 | SpaceBeforeRangeBasedForLoopColon: true 58 | SpaceInEmptyParentheses: false 59 | SpacesBeforeTrailingComments: 0 60 | SpacesInAngles: false 61 | SpacesInCStyleCastParentheses: false 62 | SpacesInContainerLiterals: false 63 | SpacesInParentheses: false 64 | SpacesInSquareBrackets: false 65 | TabWidth: 4 66 | UseTab: Never 67 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # rust-clippy is a tool that runs a bunch of lints to catch common 6 | # mistakes in your Rust code and help improve your Rust code. 7 | # More details at https://github.com/rust-lang/rust-clippy 8 | # and https://rust-lang.github.io/rust-clippy/ 9 | 10 | name: CI 11 | 12 | on: 13 | pull_request: 14 | push: 15 | paths-ignore: 16 | - '**.md' 17 | - 'LICENSE' 18 | 19 | jobs: 20 | rust-clippy-analyze: 21 | name: Run xmake build 22 | strategy: 23 | matrix: 24 | os: [ubuntu-latest, windows-latest] 25 | runs-on: ${{ matrix.os }} 26 | steps: 27 | - name: Checkout code 28 | uses: actions/checkout@v4 29 | 30 | - name: Install xmake 31 | uses: xmake-io/github-action-setup-xmake@v1 32 | with: 33 | xmake-version: latest 34 | 35 | - name: Build 36 | run: xmake 37 | 38 | - name: Summary 39 | run: xmake run summary 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | 3 | /log/* 4 | !/log/placeholder 5 | 6 | .*/ 7 | !/.github/ 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2024 YdrMaster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learning C++ 2 | 3 | > **NOTICE** 推荐阅读 [C++ 参考手册](https://zh.cppreference.com/w/cpp) 学习 C/C++ 语法和 STL 库。 4 | 5 | ## 使用指南 6 | 7 | 1. 安装构建工具 [xmake](https://xmake.io/) 8 | 9 | xmake 是跨平台的 C/C++ 构建系统,具有安装方便,配置简洁的优点。 10 | 11 | 安装方式见[中文官方文档](https://xmake.io/#/zh-cn/getting_started?id=%e5%ae%89%e8%a3%85)。 12 | 13 | > **NOTICE** xmake 仅是构建工具,不包括工具链。仍需先安装 C/C++ 工具链(MSVC、GCC 或 Clang)才能开始使用项目。 14 | 15 | > **NOTICE** 有报告称 xmake 在 Windows 上存在定位工具链失败的问题。因此推荐按下列规则选择学习平台: 16 | > 17 | > 1. 如果已经在默认位置安装 Visual Studio/Ms C++ Build Tools,则推荐原生 Windows 学习本项目; 18 | > 2. 如果已经配置好 WSL2,则推荐 WSL2 + GCC/Clang 学习本项目; 19 | > 3. 如果未配置过任何工具链,则推荐在默认位置安装 Visual Studio; 20 | > 4. 不推荐 MinGW 或 Cygwin,除非足够熟悉这些环境,可以自行完成配置; 21 | 22 | > **NOTICE** 如果仍遇到工具链问题,推荐浏览 [xmake issues](https://github.com/xmake-io/xmake/issues); 23 | 24 | 2. 克隆仓库 25 | 26 | ```shell 27 | git clone https://github.com/LearningInfiniTensor/learning-cxx.git 28 | ``` 29 | 30 | 3. 编译学习系统 31 | 32 | ```shell 33 | cd learning-cxx 34 | xmake 35 | ``` 36 | 37 | 4. 开始学习 38 | 39 | 使用 40 | 41 | ```shell 42 | xmake run learn 43 | ``` 44 | 45 | 运行指定练习,例如: 46 | 47 | ```shell 48 | xmake run learn 0 49 | ``` 50 | 51 | 运行 0 号练习。 52 | 53 | 5. 总结学习 54 | 55 | 使用 56 | 57 | ```shell 58 | xmake run summary 59 | ``` 60 | 61 | 总结所有练习通过情况。 62 | 63 | ## 其他学习资源 64 | 65 | - [Microsoft:欢迎回到 C++](https://learn.microsoft.com/zh-cn/cpp/cpp/welcome-back-to-cpp-modern-cpp?view=msvc-170) 66 | - [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) 67 | - ~~[Google 风格指南(中文版)](https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents.html)~~(deprecated) 68 | - [360 安全规则集合](https://github.com/Qihoo360/safe-rules) 69 | -------------------------------------------------------------------------------- /exercises/00_hello_world/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: std streams 4 | // READ: 流修饰符 5 | // READ: format in cxx20 6 | 7 | int main(int argc, char **argv) { 8 | // TODO: 在控制台输出 "Hello, InfiniTensor!" 并换行 9 | std::cout : "Hello, InfiniTensor!" + std::endl; 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /exercises/01_variable&add/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 运算符 4 | 5 | int main(int argc, char **argv) { 6 | // TODO: 补全变量定义并打印加法运算 7 | // x ? 8 | std::cout << x << " + " << x << " = " << x + x << std::endl; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /exercises/02_function/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 声明 4 | // NOTICE: cppreference 中的示例中指出了复杂声明的解读法,建议认真阅读。 5 | // NOTICE: 补充由内而外读法的机翻解释 6 | 7 | // TODO: 在这里声明函数 8 | 9 | int main(int argc, char **argv) { 10 | ASSERT(add(123, 456) == 123 + 456, "add(123, 456) should be 123 + 456"); 11 | 12 | auto x = 1, y = 2; 13 | std::cout << x << " + " << y << " = " << add(x, y) << std::endl; 14 | return 0; 15 | } 16 | 17 | int add(int a, int b) { 18 | // TODO: 补全函数定义,但不要移动代码行 19 | } 20 | -------------------------------------------------------------------------------- /exercises/03_argument¶meter/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 4 | // THINK: 参数都有哪些传递方式?如何选择传递方式? 5 | 6 | void func(int); 7 | 8 | // TODO: 为下列 ASSERT 填写正确的值 9 | int main(int argc, char **argv) { 10 | auto arg = 99; 11 | ASSERT(arg == ?, "arg should be ?"); 12 | std::cout << "befor func call: " << arg << std::endl; 13 | func(arg); 14 | ASSERT(arg == ?, "arg should be ?"); 15 | std::cout << "after func call: " << arg << std::endl; 16 | return 0; 17 | } 18 | 19 | // TODO: 为下列 ASSERT 填写正确的值 20 | void func(int param) { 21 | ASSERT(param == ?, "param should be ?"); 22 | std::cout << "befor add: " << param << std::endl; 23 | param += 1; 24 | ASSERT(param == ?, "param should be ?"); 25 | std::cout << "after add: " << param << std::endl; 26 | } 27 | -------------------------------------------------------------------------------- /exercises/04_static/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: `static` 关键字 4 | // THINK: 这个函数的两个 `static` 各自的作用是什么? 5 | static int func(int param) { 6 | static int static_ = param; 7 | // std::cout << "static_ = " << static_ << std::endl; 8 | return static_++; 9 | } 10 | 11 | int main(int argc, char **argv) { 12 | // TODO: 将下列 `?` 替换为正确的数字 13 | ASSERT(func(5) == ?, "static variable value incorrect"); 14 | ASSERT(func(4) == ?, "static variable value incorrect"); 15 | ASSERT(func(3) == ?, "static variable value incorrect"); 16 | ASSERT(func(2) == ?, "static variable value incorrect"); 17 | ASSERT(func(1) == ?, "static variable value incorrect"); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /exercises/05_constexpr/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | constexpr unsigned long long fibonacci(int i) { 4 | switch (i) { 5 | case 0: 6 | return 0; 7 | case 1: 8 | return 1; 9 | default: 10 | return fibonacci(i - 1) + fibonacci(i - 2); 11 | } 12 | } 13 | 14 | int main(int argc, char **argv) { 15 | constexpr auto FIB20 = fibonacci(20); 16 | ASSERT(FIB20 == 6765, "fibonacci(20) should be 6765"); 17 | std::cout << "fibonacci(20) = " << FIB20 << std::endl; 18 | 19 | // TODO: 观察错误信息,修改一处,使代码编译运行 20 | // PS: 编译运行,但是不一定能算出结果…… 21 | constexpr auto ANS_N = 90; 22 | constexpr auto ANS = fibonacci(ANS_N); 23 | std::cout << "fibonacci(" << ANS_N << ") = " << ANS << std::endl; 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /exercises/06_array/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 数组 4 | 5 | unsigned long long arr[90]{0, 1}; 6 | unsigned long long fibonacci(int i) { 7 | switch (i) { 8 | case 0: 9 | return 0; 10 | case 1: 11 | return 1; 12 | default: 13 | // TODO: 补全三目表达式缺失的部分 14 | return ? : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2)); 15 | } 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | // TODO: 为此 ASSERT 填写正确的值 20 | ASSERT(sizeof(arr) == ?, "sizeof array is size of all its elements"); 21 | // ---- 不要修改以下代码 ---- 22 | ASSERT(fibonacci(2) == 1, "fibonacci(2) should be 1"); 23 | ASSERT(fibonacci(20) == 6765, "fibonacci(20) should be 6765"); 24 | ASSERT(fibonacci(80) == 23416728348467685, "fibonacci(80) should be 23416728348467685"); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /exercises/07_loop/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // TODO: 改正函数实现,实现正确的缓存优化斐波那契计算 4 | // THINk: 这个函数是一个纯函数(pure function)吗? 5 | // READ: 纯函数 6 | static unsigned long long fibonacci(int i) { 7 | // TODO: 为缓存设置正确的初始值 8 | static unsigned long long cache[96], cached; 9 | // TODO: 设置正确的循环条件 10 | for (; false; ++cached) { 11 | cache[cached] = cache[cached - 1] + cache[cached - 2]; 12 | } 13 | return cache[i]; 14 | } 15 | 16 | // ---- 不要修改以下代码 ---- 17 | int main(int argc, char **argv) { 18 | ASSERT(fibonacci(0) == 0, "fibonacci(0) should be 0"); 19 | ASSERT(fibonacci(1) == 1, "fibonacci(1) should be 1"); 20 | ASSERT(fibonacci(2) == 1, "fibonacci(2) should be 1"); 21 | ASSERT(fibonacci(3) == 2, "fibonacci(3) should be 2"); 22 | ASSERT(fibonacci(10) == 55, "fibonacci(10) should be 55"); 23 | 24 | auto fib90 = fibonacci(90); 25 | std::cout << "fibonacci(90) = " << fib90 << std::endl; 26 | ASSERT(fib90 == 2880067194370816120, "fibonacci(90) should be 2880067194370816120"); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /exercises/08_pointer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 数组向指针退化 4 | bool is_fibonacci(int *ptr, int len, int stride) { 5 | ASSERT(len >= 3, "`len` should be at least 3"); 6 | // TODO: 编写代码判断从 ptr 开始,每 stride 个元素取 1 个元素,组成长度为 n 的数列是否满足 7 | // arr[i + 2] = arr[i] + arr[i + 1] 8 | return true; 9 | } 10 | 11 | // ---- 不要修改以下代码 ---- 12 | int main(int argc, char **argv) { 13 | int arr0[]{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}, 14 | arr1[]{0, 1, 2, 3, 4, 5, 6}, 15 | arr2[]{99, 98, 4, 1, 7, 2, 11, 3, 18, 5, 29, 8, 47, 13, 76, 21, 123, 34, 199, 55, 322, 0, 0}; 16 | // clang-format off 17 | ASSERT( is_fibonacci(arr0 , sizeof(arr0) / sizeof(*arr0) , 1), "arr0 is Fibonacci" ); 18 | ASSERT( is_fibonacci(arr0 + 2, sizeof(arr0) / sizeof(*arr0) - 4, 1), "part of arr0 is Fibonacci" ); 19 | ASSERT(!is_fibonacci(arr1 , sizeof(arr1) / sizeof(*arr1) , 1), "arr1 is not Fibonacci"); 20 | ASSERT( is_fibonacci(arr1 + 1, 3 , 1), "part of arr1 is Fibonacci" ); 21 | ASSERT(!is_fibonacci(arr2 , sizeof(arr2) / sizeof(*arr2) , 1), "arr2 is not Fibonacci"); 22 | ASSERT( is_fibonacci(arr2 + 2, 10 , 2), "part of arr2 is Fibonacci" ); 23 | ASSERT( is_fibonacci(arr2 + 3, 9 , 2), "part of arr2 is Fibonacci" ); 24 | ASSERT(!is_fibonacci(arr2 + 3, 10 , 2), "guard check" ); 25 | ASSERT(!is_fibonacci(arr2 + 1, 10 , 2), "guard check" ); 26 | // clang-format on 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /exercises/09_enum&union/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 枚举类型 4 | 5 | // `enum` 是 C 的兼容类型,本质上其对应类型的常量。 6 | // 在 `enum` 中定义标识符等价于定义 constexpr 常量, 7 | // 这些标识符不需要前缀,可以直接引用。 8 | // 因此 `enum` 定义会污染命名空间。 9 | enum ColorEnum : unsigned char { 10 | COLOR_RED = 31, 11 | COLOR_GREEN, 12 | COLOR_YELLOW, 13 | COLOR_BLUE, 14 | }; 15 | 16 | // 有作用域枚举型是 C++ 引入的类型安全枚举。 17 | // 其内部标识符需要带前缀引用,如 `Color::Red`。 18 | // 作用域枚举型可以避免命名空间污染,并提供类型安全保证。 19 | enum class Color : int { 20 | Red = COLOR_RED, 21 | Green, 22 | Yellow, 23 | Blue, 24 | }; 25 | 26 | ColorEnum convert_by_pun(Color c) { 27 | // READ: 28 | // `union` 表示在同一内存位置存储的不同类型的值。 29 | // 其常见用法是实现类型双关转换,即将一种类型的值转换为另一种无关类型的值。 30 | // 但这种写法实际上仅在 C 语言良定义,在 C++ 中是未定义行为。 31 | // 这是比较少见的 C++ 不与 C 保持兼容的特性。 32 | // READ: 类型双关 33 | union TypePun { 34 | ColorEnum e; 35 | Color c; 36 | }; 37 | 38 | TypePun pun; 39 | // TODO: 补全类型双关转换 40 | 41 | return pun.e; 42 | } 43 | 44 | int main(int argc, char **argv) { 45 | ASSERT(convert_by_pun(Color::Red) == COLOR_RED, "Type punning conversion"); 46 | ASSERT(convert_by_pun(Color::Green) == COLOR_GREEN, "Type punning conversion"); 47 | ASSERT(convert_by_pun(Color::Yellow) == COLOR_YELLOW, "Type punning conversion"); 48 | ASSERT(convert_by_pun(Color::Blue) == COLOR_BLUE, "Type punning conversion"); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /exercises/10_trivial/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: Trivial type 4 | 5 | struct FibonacciCache { 6 | unsigned long long cache[16]; 7 | int cached; 8 | }; 9 | 10 | // TODO: 实现正确的缓存优化斐波那契计算 11 | static unsigned long long fibonacci(FibonacciCache &cache, int i) { 12 | for (; false; ++cached) { 13 | cache[cached] = cache[cached - 1] + cache[cached - 2]; 14 | } 15 | return cache.cache[i]; 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | // TODO: 初始化缓存结构体,使计算正确 20 | // NOTICE: C/C++ 中,读取未初始化的变量(包括结构体变量)是未定义行为 21 | // READ: 初始化的各种写法 22 | FibonacciCache fib; 23 | ASSERT(fibonacci(fib, 10) == 55, "fibonacci(10) should be 55"); 24 | std::cout << "fibonacci(10) = " << fibonacci(fib, 10) << std::endl; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /exercises/11_method/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | struct Fibonacci { 4 | unsigned long long cache[128]; 5 | int cached; 6 | 7 | // TODO: 实现正确的缓存优化斐波那契计算 8 | unsigned long long get(int i) { 9 | for (; false; ++cached) { 10 | cache[cached] = cache[cached - 1] + cache[cached - 2]; 11 | } 12 | return cache[i]; 13 | } 14 | }; 15 | 16 | int main(int argc, char **argv) { 17 | // TODO: 初始化缓存结构体,使计算正确 18 | Fibonacci fib; 19 | ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); 20 | std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /exercises/12_method_const/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 有 cv 限定符的成员函数 4 | 5 | struct Fibonacci { 6 | int numbers[11]; 7 | // TODO: 修改方法签名和实现,使测试通过 8 | int get(int i) { 9 | } 10 | }; 11 | 12 | int main(int argc, char **argv) { 13 | Fibonacci constexpr FIB{{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}}; 14 | ASSERT(FIB.get(10) == 55, "fibonacci(10) should be 55"); 15 | std::cout << "fibonacci(10) = " << FIB.get(10) << std::endl; 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /exercises/13_class/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // C++ 中,`class` 和 `struct` 之间的**唯一区别**是 4 | // `class` 默认访问控制符是 `private`, 5 | // `struct` 默认访问控制符是 `public`。 6 | // READ: 访问说明符 7 | 8 | // 这个 class 中的字段被 private 修饰,只能在 class 内部访问。 9 | // 因此必须提供构造器来初始化字段。 10 | // READ: 构造器 11 | class Fibonacci { 12 | size_t cache[16]; 13 | int cached; 14 | 15 | public: 16 | // TODO: 实现构造器 17 | // Fibonacci() 18 | 19 | // TODO: 实现正确的缓存优化斐波那契计算 20 | size_t get(int i) { 21 | for (; false; ++cached) { 22 | cache[cached] = cache[cached - 1] + cache[cached - 2]; 23 | } 24 | return cache[i]; 25 | } 26 | }; 27 | 28 | int main(int argc, char **argv) { 29 | // 现在类型拥有无参构造器,声明时会直接调用。 30 | // 这个写法不再是未定义行为了。 31 | Fibonacci fib; 32 | ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); 33 | std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /exercises/14_class_destruct/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 析构函数 4 | // READ: RAII 5 | 6 | /// @brief 任意缓存容量的斐波那契类型。 7 | /// @details 可以在构造时传入缓存容量,因此需要动态分配缓存空间。 8 | class DynFibonacci { 9 | size_t *cache; 10 | int cached; 11 | 12 | public: 13 | // TODO: 实现动态设置容量的构造器 14 | DynFibonacci(int capacity): cache(new ?), cached(?) {} 15 | 16 | // TODO: 实现析构器,释放缓存空间 17 | ~DynFibonacci(); 18 | 19 | // TODO: 实现正确的缓存优化斐波那契计算 20 | size_t get(int i) { 21 | for (; false; ++cached) { 22 | cache[cached] = cache[cached - 1] + cache[cached - 2]; 23 | } 24 | return cache[i]; 25 | } 26 | }; 27 | 28 | int main(int argc, char **argv) { 29 | DynFibonacci fib(12); 30 | ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); 31 | std::cout << "fibonacci(10) = " << fib.get(10) << std::endl; 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /exercises/15_class_clone/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 复制构造函数 4 | // READ: 函数定义(显式弃置) 5 | 6 | 7 | class DynFibonacci { 8 | size_t *cache; 9 | int cached; 10 | 11 | public: 12 | // TODO: 实现动态设置容量的构造器 13 | DynFibonacci(int capacity): cache(new ?), cached(?) {} 14 | 15 | // TODO: 实现复制构造器 16 | DynFibonacci(DynFibonacci const &) = delete; 17 | 18 | // TODO: 实现析构器,释放缓存空间 19 | ~DynFibonacci(); 20 | 21 | // TODO: 实现正确的缓存优化斐波那契计算 22 | size_t get(int i) { 23 | for (; false; ++cached) { 24 | cache[cached] = cache[cached - 1] + cache[cached - 2]; 25 | } 26 | return cache[i]; 27 | } 28 | 29 | // NOTICE: 不要修改这个方法 30 | // NOTICE: 名字相同参数也相同,但 const 修饰不同的方法是一对重载方法,可以同时存在 31 | // 本质上,方法是隐藏了 this 参数的函数 32 | // const 修饰作用在 this 上,因此它们实际上参数不同 33 | size_t get(int i) const { 34 | if (i <= cached) { 35 | return cache[i]; 36 | } 37 | ASSERT(false, "i out of range"); 38 | } 39 | }; 40 | 41 | int main(int argc, char **argv) { 42 | DynFibonacci fib(12); 43 | ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55"); 44 | DynFibonacci const fib_ = fib; 45 | ASSERT(fib_.get(10) == fib.get(10), "Object cloned"); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /exercises/16_class_move/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 左值右值(概念) 4 | // READ: 左值右值(细节) 5 | // READ: 关于移动语义 6 | // READ: 如果实现移动构造 7 | 8 | // READ: 移动构造函数 9 | // READ: 移动赋值 10 | // READ: 运算符重载 11 | 12 | class DynFibonacci { 13 | size_t *cache; 14 | int cached; 15 | 16 | public: 17 | // TODO: 实现动态设置容量的构造器 18 | DynFibonacci(int capacity): cache(new ?), cached(?) {} 19 | 20 | // TODO: 实现移动构造器 21 | DynFibonacci(DynFibonacci &&) noexcept = delete; 22 | 23 | // TODO: 实现移动赋值 24 | // NOTICE: ⚠ 注意移动到自身问题 ⚠ 25 | DynFibonacci &operator=(DynFibonacci &&) noexcept = delete; 26 | 27 | // TODO: 实现析构器,释放缓存空间 28 | ~DynFibonacci(); 29 | 30 | // TODO: 实现正确的缓存优化斐波那契计算 31 | size_t operator[](int i) { 32 | for (; false; ++cached) { 33 | cache[cached] = cache[cached - 1] + cache[cached - 2]; 34 | } 35 | return cache[i]; 36 | } 37 | 38 | // NOTICE: 不要修改这个方法 39 | size_t operator[](int i) const { 40 | ASSERT(i <= cached, "i out of range"); 41 | return cache[i]; 42 | } 43 | 44 | // NOTICE: 不要修改这个方法 45 | bool is_alive() const { 46 | return cache; 47 | } 48 | }; 49 | 50 | int main(int argc, char **argv) { 51 | DynFibonacci fib(12); 52 | ASSERT(fib[10] == 55, "fibonacci(10) should be 55"); 53 | 54 | DynFibonacci const fib_ = std::move(fib); 55 | ASSERT(!fib.is_alive(), "Object moved"); 56 | ASSERT(fib_[10] == 55, "fibonacci(10) should be 55"); 57 | 58 | DynFibonacci fib0(6); 59 | DynFibonacci fib1(12); 60 | 61 | fib0 = std::move(fib1); 62 | fib0 = std::move(fib0); 63 | ASSERT(fib0[10] == 55, "fibonacci(10) should be 55"); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /exercises/17_class_derive/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 派生类 4 | 5 | static int i = 0; 6 | 7 | struct X { 8 | int x; 9 | 10 | X(int x_) : x(x_) { 11 | std::cout << ++i << ". " << "X(" << x << ')' << std::endl; 12 | } 13 | X(X const &other) : x(other.x) { 14 | std::cout << ++i << ". " << "X(X const &) : x(" << x << ')' << std::endl; 15 | } 16 | ~X() { 17 | std::cout << ++i << ". " << "~X(" << x << ')' << std::endl; 18 | } 19 | }; 20 | struct A { 21 | int a; 22 | 23 | A(int a_) : a(a_) { 24 | std::cout << ++i << ". " << "A(" << a << ')' << std::endl; 25 | } 26 | A(A const &other) : a(other.a) { 27 | std::cout << ++i << ". " << "A(A const &) : a(" << a << ')' << std::endl; 28 | } 29 | ~A() { 30 | std::cout << ++i << ". " << "~A(" << a << ')' << std::endl; 31 | } 32 | }; 33 | struct B : public A { 34 | X x; 35 | 36 | B(int b) : A(1), x(b) { 37 | std::cout << ++i << ". " << "B(" << a << ", X(" << x.x << "))" << std::endl; 38 | } 39 | B(B const &other) : A(other.a), x(other.x) { 40 | std::cout << ++i << ". " << "B(B const &) : A(" << a << "), x(X(" << x.x << "))" << std::endl; 41 | } 42 | ~B() { 43 | std::cout << ++i << ". " << "~B(" << a << ", X(" << x.x << "))" << std::endl; 44 | } 45 | }; 46 | 47 | int main(int argc, char **argv) { 48 | X x = X(1); 49 | A a = A(2); 50 | B b = B(3); 51 | 52 | // TODO: 补全三个类型的大小 53 | static_assert(sizeof(X) == ?, "There is an int in X"); 54 | static_assert(sizeof(A) == ?, "There is an int in A"); 55 | static_assert(sizeof(B) == ?, "B is an A with an X"); 56 | 57 | i = 0; 58 | std::cout << std::endl 59 | << "-------------------------" << std::endl 60 | << std::endl; 61 | 62 | // 这是不可能的,A 无法提供 B 增加的成员变量的值 63 | // B ba = A(4); 64 | 65 | // 这也是不可能的,因为 A 是 B 的一部分,就好像不可以把套娃的外层放进内层里。 66 | A ab = B(5);// 然而这个代码可以编译和运行! 67 | // THINK: 观察打印出的信息,推测把大象放进冰箱分几步? 68 | // THINK: 这样的代码是“安全”的吗? 69 | // NOTICE: 真实场景中不太可能出现这样的代码 70 | 71 | i = 0; 72 | std::cout << std::endl 73 | << "-------------------------" << std::endl 74 | << std::endl; 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /exercises/18_class_virtual/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 虚函数 4 | 5 | struct A { 6 | virtual char virtual_name() const { 7 | return 'A'; 8 | } 9 | char direct_name() const { 10 | return 'A'; 11 | } 12 | }; 13 | struct B : public A { 14 | // READ: override 15 | char virtual_name() const override { 16 | return 'B'; 17 | } 18 | char direct_name() const { 19 | return 'B'; 20 | } 21 | }; 22 | struct C : public B { 23 | // READ: final 24 | char virtual_name() const final { 25 | return 'C'; 26 | } 27 | char direct_name() const { 28 | return 'C'; 29 | } 30 | }; 31 | struct D : public C { 32 | char direct_name() const { 33 | return 'D'; 34 | } 35 | }; 36 | 37 | int main(int argc, char **argv) { 38 | constexpr auto MSG = "Replace '?' with its correct name."; 39 | 40 | A a; 41 | B b; 42 | C c; 43 | D d; 44 | 45 | ASSERT(a.virtual_name() == '?', MSG); 46 | ASSERT(b.virtual_name() == '?', MSG); 47 | ASSERT(c.virtual_name() == '?', MSG); 48 | ASSERT(d.virtual_name() == '?', MSG); 49 | ASSERT(a.direct_name() == '?', MSG); 50 | ASSERT(b.direct_name() == '?', MSG); 51 | ASSERT(c.direct_name() == '?', MSG); 52 | ASSERT(d.direct_name() == '?', MSG); 53 | 54 | A &rab = b; 55 | B &rbc = c; 56 | C &rcd = d; 57 | 58 | ASSERT(rab.virtual_name() == '?', MSG); 59 | ASSERT(rbc.virtual_name() == '?', MSG); 60 | ASSERT(rcd.virtual_name() == '?', MSG); 61 | ASSERT(rab.direct_name() == '?', MSG); 62 | ASSERT(rbc.direct_name() == '?', MSG); 63 | ASSERT(rcd.direct_name() == '?', MSG); 64 | 65 | A &rac = c; 66 | B &rbd = d; 67 | 68 | ASSERT(rac.virtual_name() == '?', MSG); 69 | ASSERT(rbd.virtual_name() == '?', MSG); 70 | ASSERT(rac.direct_name() == '?', MSG); 71 | ASSERT(rbd.direct_name() == '?', MSG); 72 | 73 | A &rad = d; 74 | 75 | ASSERT(rad.virtual_name() == '?', MSG); 76 | ASSERT(rad.direct_name() == '?', MSG); 77 | 78 | return 0; 79 | } 80 | 81 | // READ: 扩展阅读-纯虚、抽象 82 | // READ: 扩展阅读-虚继承 83 | -------------------------------------------------------------------------------- /exercises/19_class_virtual_destruct/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 静态字段 4 | // READ: 虚析构函数 5 | 6 | struct A { 7 | // TODO: 正确初始化静态字段 8 | static int num_a = 0; 9 | 10 | A() { 11 | ++num_a; 12 | } 13 | ~A() { 14 | --num_a; 15 | } 16 | 17 | virtual char name() const { 18 | return 'A'; 19 | } 20 | }; 21 | struct B final : public A { 22 | // TODO: 正确初始化静态字段 23 | static int num_b = 0; 24 | 25 | B() { 26 | ++num_b; 27 | } 28 | ~B() { 29 | --num_b; 30 | } 31 | 32 | char name() const final { 33 | return 'B'; 34 | } 35 | }; 36 | 37 | int main(int argc, char **argv) { 38 | auto a = new A; 39 | auto b = new B; 40 | ASSERT(A::num_a == ?, "Fill in the correct value for A::num_a"); 41 | ASSERT(B::num_b == ?, "Fill in the correct value for B::num_b"); 42 | ASSERT(a->name() == '?', "Fill in the correct value for a->name()"); 43 | ASSERT(b->name() == '?', "Fill in the correct value for b->name()"); 44 | 45 | delete a; 46 | delete b; 47 | ASSERT(A::num_a == 0, "Every A was destroyed"); 48 | ASSERT(B::num_b == 0, "Every B was destroyed"); 49 | 50 | A *ab = new B;// 派生类指针可以随意转换为基类指针 51 | ASSERT(A::num_a == ?, "Fill in the correct value for A::num_a"); 52 | ASSERT(B::num_b == ?, "Fill in the correct value for B::num_b"); 53 | ASSERT(ab->name() == '?', "Fill in the correct value for ab->name()"); 54 | 55 | // TODO: 基类指针无法随意转换为派生类指针,补全正确的转换语句 56 | B &bb = *ab; 57 | ASSERT(bb.name() == '?', "Fill in the correct value for bb->name()"); 58 | 59 | // TODO: ---- 以下代码不要修改,通过改正类定义解决编译问题 ---- 60 | delete ab;// 通过指针可以删除指向的对象,即使是多态对象 61 | ASSERT(A::num_a == 0, "Every A was destroyed"); 62 | ASSERT(B::num_b == 0, "Every B was destroyed"); 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /exercises/20_function_template/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | 3 | // READ: 函数模板 4 | // TODO: 将这个函数模板化 5 | int plus(int a, int b) { 6 | return a + b; 7 | } 8 | 9 | int main(int argc, char **argv) { 10 | ASSERT(plus(1, 2) == 3, "Plus two int"); 11 | ASSERT(plus(1u, 2u) == 3u, "Plus two unsigned int"); 12 | 13 | // THINK: 浮点数何时可以判断 ==?何时必须判断差值? 14 | ASSERT(plus(1.25f, 2.5f) == 3.75f, "Plus two float"); 15 | ASSERT(plus(1.25, 2.5) == 3.75, "Plus two double"); 16 | // TODO: 修改判断条件使测试通过 17 | ASSERT(plus(0.1, 0.2) == 0.3, "How to make this pass?"); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /exercises/21_runtime_datatype/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | enum class DataType { 5 | Float, 6 | Double, 7 | }; 8 | 9 | /// @brief Tagged union 即标签化联合体,是联合体的一种常见应用。 10 | /// Rust enum 在实现上就是标签化联合体。 11 | struct TaggedUnion { 12 | DataType type; 13 | // NOTICE: struct/union 可以相互任意嵌套。 14 | union { 15 | float f; 16 | double d; 17 | }; 18 | }; 19 | 20 | // TODO: 将这个函数模板化用于 sigmoid_dyn 21 | float sigmoid(float x) { 22 | return 1 / (1 + std::exp(-x)); 23 | } 24 | 25 | TaggedUnion sigmoid_dyn(TaggedUnion x) { 26 | TaggedUnion ans{x.type}; 27 | // TODO: 根据 type 调用 sigmoid 28 | return ans; 29 | } 30 | 31 | // ---- 不要修改以下代码 ---- 32 | int main(int argc, char **argv) { 33 | TaggedUnion xf{DataType::Float}; 34 | xf.f = 5.f; 35 | auto yf = sigmoid_dyn(xf); 36 | ASSERT(yf.type == DataType::Float, "type mismatch"); 37 | ASSERT(yf.f == 1 / (1 + std::exp(-5.f)), "sigmoid float"); 38 | 39 | TaggedUnion xd{DataType::Double}; 40 | xd.d = 5.0; 41 | auto yd = sigmoid_dyn(xd); 42 | ASSERT(yd.type == DataType::Double, "type mismatch"); 43 | ASSERT(yd.d == 1 / (1 + std::exp(-5.0)), "sigmoid double"); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /exercises/22_class_template/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | // READ: 类模板 4 | 5 | template 6 | struct Tensor4D { 7 | unsigned int shape[4]; 8 | T *data; 9 | 10 | Tensor4D(unsigned int const shape_[4], T const *data_) { 11 | unsigned int size = 1; 12 | // TODO: 填入正确的 shape 并计算 size 13 | data = new T[size]; 14 | std::memcpy(data, data_, size * sizeof(T)); 15 | } 16 | ~Tensor4D() { 17 | delete[] data; 18 | } 19 | 20 | // 为了保持简单,禁止复制和移动 21 | Tensor4D(Tensor4D const &) = delete; 22 | Tensor4D(Tensor4D &&) noexcept = delete; 23 | 24 | // 这个加法需要支持“单向广播”。 25 | // 具体来说,`others` 可以具有与 `this` 不同的形状,形状不同的维度长度必须为 1。 26 | // `others` 长度为 1 但 `this` 长度不为 1 的维度将发生广播计算。 27 | // 例如,`this` 形状为 `[1, 2, 3, 4]`,`others` 形状为 `[1, 2, 1, 4]`, 28 | // 则 `this` 与 `others` 相加时,3 个形状为 `[1, 2, 1, 4]` 的子张量各自与 `others` 对应项相加。 29 | Tensor4D &operator+=(Tensor4D const &others) { 30 | // TODO: 实现单向广播的加法 31 | return *this; 32 | } 33 | }; 34 | 35 | // ---- 不要修改以下代码 ---- 36 | int main(int argc, char **argv) { 37 | { 38 | unsigned int shape[]{1, 2, 3, 4}; 39 | // clang-format off 40 | int data[]{ 41 | 1, 2, 3, 4, 42 | 5, 6, 7, 8, 43 | 9, 10, 11, 12, 44 | 45 | 13, 14, 15, 16, 46 | 17, 18, 19, 20, 47 | 21, 22, 23, 24}; 48 | // clang-format on 49 | auto t0 = Tensor4D(shape, data); 50 | auto t1 = Tensor4D(shape, data); 51 | t0 += t1; 52 | for (auto i = 0u; i < sizeof(data) / sizeof(*data); ++i) { 53 | ASSERT(t0.data[i] == data[i] * 2, "Tensor doubled by plus its self."); 54 | } 55 | } 56 | { 57 | unsigned int s0[]{1, 2, 3, 4}; 58 | // clang-format off 59 | float d0[]{ 60 | 1, 1, 1, 1, 61 | 2, 2, 2, 2, 62 | 3, 3, 3, 3, 63 | 64 | 4, 4, 4, 4, 65 | 5, 5, 5, 5, 66 | 6, 6, 6, 6}; 67 | // clang-format on 68 | unsigned int s1[]{1, 2, 3, 1}; 69 | // clang-format off 70 | float d1[]{ 71 | 6, 72 | 5, 73 | 4, 74 | 75 | 3, 76 | 2, 77 | 1}; 78 | // clang-format on 79 | 80 | auto t0 = Tensor4D(s0, d0); 81 | auto t1 = Tensor4D(s1, d1); 82 | t0 += t1; 83 | for (auto i = 0u; i < sizeof(d0) / sizeof(*d0); ++i) { 84 | ASSERT(t0.data[i] == 7.f, "Every element of t0 should be 7 after adding t1 to it."); 85 | } 86 | } 87 | { 88 | unsigned int s0[]{1, 2, 3, 4}; 89 | // clang-format off 90 | double d0[]{ 91 | 1, 2, 3, 4, 92 | 5, 6, 7, 8, 93 | 9, 10, 11, 12, 94 | 95 | 13, 14, 15, 16, 96 | 17, 18, 19, 20, 97 | 21, 22, 23, 24}; 98 | // clang-format on 99 | unsigned int s1[]{1, 1, 1, 1}; 100 | double d1[]{1}; 101 | 102 | auto t0 = Tensor4D(s0, d0); 103 | auto t1 = Tensor4D(s1, d1); 104 | t0 += t1; 105 | for (auto i = 0u; i < sizeof(d0) / sizeof(*d0); ++i) { 106 | ASSERT(t0.data[i] == d0[i] + 1, "Every element of t0 should be incremented by 1 after adding t1 to it."); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /exercises/23_template_const/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | // READ: 模板非类型实参 5 | 6 | template 7 | struct Tensor { 8 | unsigned int shape[N]; 9 | T *data; 10 | 11 | Tensor(unsigned int const shape_[N]) { 12 | unsigned int size = 1; 13 | // TODO: 填入正确的 shape 并计算 size 14 | data = new T[size]; 15 | std::memset(data, 0, size * sizeof(T)); 16 | } 17 | ~Tensor() { 18 | delete[] data; 19 | } 20 | 21 | // 为了保持简单,禁止复制和移动 22 | Tensor(Tensor const &) = delete; 23 | Tensor(Tensor &&) noexcept = delete; 24 | 25 | T &operator[](unsigned int const indices[N]) { 26 | return data[data_index(indices)]; 27 | } 28 | T const &operator[](unsigned int const indices[N]) const { 29 | return data[data_index(indices)]; 30 | } 31 | 32 | private: 33 | unsigned int data_index(unsigned int const indices[N]) const { 34 | unsigned int index = 0; 35 | for (unsigned int i = 0; i < N; ++i) { 36 | ASSERT(indices[i] < shape[i], "Invalid index"); 37 | // TODO: 计算 index 38 | } 39 | return index; 40 | } 41 | }; 42 | 43 | // ---- 不要修改以下代码 ---- 44 | int main(int argc, char **argv) { 45 | { 46 | unsigned int shape[]{2, 3, 4, 5}; 47 | auto tensor = Tensor<4, int>(shape); 48 | 49 | unsigned int i0[]{0, 0, 0, 0}; 50 | tensor[i0] = 1; 51 | ASSERT(tensor[i0] == 1, "tensor[i0] should be 1"); 52 | ASSERT(tensor.data[0] == 1, "tensor[i0] should be 1"); 53 | 54 | unsigned int i1[]{1, 2, 3, 4}; 55 | tensor[i1] = 2; 56 | ASSERT(tensor[i1] == 2, "tensor[i1] should be 2"); 57 | ASSERT(tensor.data[119] == 2, "tensor[i1] should be 2"); 58 | } 59 | { 60 | unsigned int shape[]{7, 8, 128}; 61 | auto tensor = Tensor<3, float>(shape); 62 | 63 | unsigned int i0[]{0, 0, 0}; 64 | tensor[i0] = 1.f; 65 | ASSERT(tensor[i0] == 1.f, "tensor[i0] should be 1"); 66 | ASSERT(tensor.data[0] == 1.f, "tensor[i0] should be 1"); 67 | 68 | unsigned int i1[]{3, 4, 99}; 69 | tensor[i1] = 2.f; 70 | ASSERT(tensor[i1] == 2.f, "tensor[i1] should be 2"); 71 | ASSERT(tensor.data[3683] == 2.f, "tensor[i1] should be 2"); 72 | } 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /exercises/24_std_array/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | #include 4 | 5 | // READ: std::array 6 | 7 | // TODO: 将下列 `?` 替换为正确的代码 8 | int main(int argc, char **argv) { 9 | { 10 | std::array arr{{1, 2, 3, 4, 5}}; 11 | ASSERT(arr.size() == ?, "Fill in the correct value."); 12 | ASSERT(sizeof(arr) == ?, "Fill in the correct value."); 13 | int ans[]{1, 2, 3, 4, 5}; 14 | ASSERT(std::memcmp(arr.?, ans, ?) == 0, "Fill in the correct values."); 15 | } 16 | { 17 | std::array arr; 18 | ASSERT(arr.size() == ?, "Fill in the correct value."); 19 | ASSERT(sizeof(arr) == ?, "Fill in the correct value."); 20 | } 21 | { 22 | std::array arr{"Hello, InfiniTensor!"}; 23 | ASSERT(arr.size() == ?, "Fill in the correct value."); 24 | ASSERT(sizeof(arr) == ?, "Fill in the correct value."); 25 | ASSERT(std::strcmp(arr.?, "Hello, InfiniTensor!") == 0, "Fill in the correct value."); 26 | } 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /exercises/25_std_vector/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | #include 4 | 5 | // READ: std::vector 6 | 7 | // TODO: 将下列 `?` 替换为正确的代码 8 | int main(int argc, char **argv) { 9 | { 10 | std::vector vec{1, 2, 3, 4, 5}; 11 | ASSERT(vec.size() == ?, "Fill in the correct value."); 12 | // THINK: `std::vector` 的大小是什么意思?与什么有关? 13 | ASSERT(sizeof(vec) == ?, "Fill in the correct value."); 14 | int ans[]{1, 2, 3, 4, 5}; 15 | ASSERT(std::memcmp(vec.?, ans, sizeof(ans)) == 0, "Fill in the correct values."); 16 | } 17 | { 18 | std::vector vec{1, 2, 3, 4, 5}; 19 | { 20 | ASSERT(vec.size() == ?, "Fill in the correct value."); 21 | ASSERT(sizeof(vec) == ?, "Fill in the correct value."); 22 | double ans[]{1, 2, 3, 4, 5}; 23 | ASSERT(std::memcmp(vec.?, ans, sizeof(ans)) == 0, "Fill in the correct values."); 24 | } 25 | { 26 | vec.push_back(6); 27 | ASSERT(vec.size() == ?, "Fill in the correct value."); 28 | ASSERT(sizeof(vec) == ?, "Fill in the correct value."); 29 | vec.pop_back(); 30 | ASSERT(vec.size() == ?, "Fill in the correct value."); 31 | ASSERT(sizeof(vec) == ?, "Fill in the correct value."); 32 | } 33 | { 34 | vec[4] = 6; 35 | ASSERT(vec[0] == ?, "Fill in the correct value."); 36 | ASSERT(vec[1] == ?, "Fill in the correct value."); 37 | ASSERT(vec[2] == ?, "Fill in the correct value."); 38 | ASSERT(vec[3] == ?, "Fill in the correct value."); 39 | ASSERT(vec[4] == ?, "Fill in the correct value."); 40 | } 41 | { 42 | // THINK: `std::vector` 插入删除的时间复杂度是什么? 43 | vec.insert(?, 1.5); 44 | ASSERT((vec == std::vector{1, 1.5, 2, 3, 4, 6}), "Make this assertion pass."); 45 | vec.erase(?); 46 | ASSERT((vec == std::vector{1, 1.5, 2, 4, 6}), "Make this assertion pass."); 47 | } 48 | { 49 | vec.shrink_to_fit(); 50 | ASSERT(vec.capacity() == ?, "Fill in the correct value."); 51 | vec.clear(); 52 | ASSERT(vec.empty(), "`vec` is empty now."); 53 | ASSERT(vec.size() == ?, "Fill in the correct value."); 54 | ASSERT(vec.capacity() == ?, "Fill in the correct value."); 55 | } 56 | } 57 | { 58 | std::vector vec(?, ?); // TODO: 调用正确的构造函数 59 | ASSERT(vec[0] == 'z', "Make this assertion pass."); 60 | ASSERT(vec[47] == 'z', "Make this assertion pass."); 61 | ASSERT(vec.size() == 48, "Make this assertion pass."); 62 | ASSERT(sizeof(vec) == ?, "Fill in the correct value."); 63 | { 64 | auto capacity = vec.capacity(); 65 | vec.resize(16); 66 | ASSERT(vec.size() == ?, "Fill in the correct value."); 67 | ASSERT(vec.capacity() == ?, "Fill in a correct identifier."); 68 | } 69 | { 70 | vec.reserve(256); 71 | ASSERT(vec.size() == ?, "Fill in the correct value."); 72 | ASSERT(vec.capacity() == ?, "Fill in the correct value."); 73 | } 74 | { 75 | vec.push_back('a'); 76 | vec.push_back('b'); 77 | vec.push_back('c'); 78 | vec.push_back('d'); 79 | ASSERT(vec.size() == ?, "Fill in the correct value."); 80 | ASSERT(vec.capacity() == ?, "Fill in the correct value."); 81 | ASSERT(vec[15] == ?, "Fill in the correct value."); 82 | ASSERT(vec[?] == 'a', "Fill in the correct value."); 83 | ASSERT(vec[?] == 'b', "Fill in the correct value."); 84 | ASSERT(vec[?] == 'c', "Fill in the correct value."); 85 | ASSERT(vec[?] == 'd', "Fill in the correct value."); 86 | } 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /exercises/26_std_vector_bool/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | // READ: std::vector 5 | // READ: 模板特化 6 | 7 | // TODO: 将下列 `?` 替换为正确的代码 8 | int main(int argc, char **argv) { 9 | std::vector vec(?, ?);// TODO: 正确调用构造函数 10 | ASSERT(vec[0], "Make this assertion pass."); 11 | ASSERT(vec[99], "Make this assertion pass."); 12 | ASSERT(vec.size() == 100, "Make this assertion pass."); 13 | // NOTICE: 平台相关!注意 CI:Ubuntu 上的值。 14 | std::cout << "sizeof(std::vector) = " << sizeof(std::vector) << std::endl; 15 | ASSERT(sizeof(vec) == ?, "Fill in the correct value."); 16 | { 17 | vec[20] = false; 18 | ASSERT(?vec[20], "Fill in `vec[20]` or `!vec[20]`."); 19 | } 20 | { 21 | vec.push_back(false); 22 | ASSERT(vec.size() == ?, "Fill in the correct value."); 23 | ASSERT(?vec[100], "Fill in `vec[100]` or `!vec[100]`."); 24 | } 25 | { 26 | auto ref = vec[30]; 27 | ASSERT(?ref, "Fill in `ref` or `!ref`"); 28 | ref = false; 29 | ASSERT(?ref, "Fill in `ref` or `!ref`"); 30 | // THINK: WHAT and WHY? 31 | ASSERT(?vec[30], "Fill in `vec[30]` or `!vec[30]`."); 32 | } 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /exercises/27_strides/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | // 张量即多维数组。连续存储张量即逻辑结构与存储结构一致的张量。 5 | // 通常来说,形状为 [d0, d1, ..., dn] 的张量,第 n 维是 dn 个连续的元素,第 n-1 维是 dn-1 个连续的 dn 个元素,以此类推。 6 | // 张量的步长或跨度指的是张量每个维度上坐标 +1 时,数据指针跨过的范围。 7 | // 因此,一个连续张量,其第 n 维的步长为 1,第 n-1 维的步长为 dn,第 n-2 维的步长为 dn*dn-1,以此类推。 8 | // 例如,一个 2x3x4 张量,其步长为 [12, 4, 1]。 9 | 10 | // READ: 类型别名 11 | using udim = unsigned int; 12 | 13 | /// @brief 计算连续存储张量的步长 14 | /// @param shape 张量的形状 15 | /// @return 张量每维度的访问步长 16 | std::vector strides(std::vector const &shape) { 17 | std::vector strides(shape.size()); 18 | // TODO: 完成函数体,根据张量形状计算张量连续存储时的步长。 19 | // READ: 逆向迭代器 std::vector::rbegin 20 | // 使用逆向迭代器可能可以简化代码 21 | return strides; 22 | } 23 | 24 | // ---- 不要修改以下代码 ---- 25 | int main(int argc, char **argv) { 26 | ASSERT((strides({2, 3, 4}) == std::vector{12, 4, 1}), "Make this assertion pass."); 27 | ASSERT((strides({3, 4, 5}) == std::vector{20, 5, 1}), "Make this assertion pass."); 28 | ASSERT((strides({1, 3, 224, 224}) == std::vector{150528, 50176, 224, 1}), "Make this assertion pass."); 29 | ASSERT((strides({7, 1, 1, 1, 5}) == std::vector{5, 5, 5, 5, 1}), "Make this assertion pass."); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /exercises/28_std_string/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | // READ: 字符串 5 | 6 | int main(int argc, char **argv) { 7 | // READ: 字符串字面量 8 | using namespace std::string_literals; 9 | auto hello = "Hello"s; 10 | auto world = "world"; 11 | // READ: `decltype` 表达式 12 | // READ: `std::is_same_v` 元编程判别 13 | ASSERT((std::is_same_v), "Fill in the missing type."); 14 | ASSERT((std::is_same_v), "Fill in the missing type."); 15 | // TODO: 将 `?` 替换为正确的字符串 16 | ASSERT(hello + ", " + world + '!' == "?", "Fill in the missing string."); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /exercises/29_std_map/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | // READ: `std::map` 5 | // READ: `std::unordered_map` 6 | 7 | template 8 | bool key_exists(std::map const &map, k const &key) { 9 | // TODO: 实现函数 10 | } 11 | 12 | template 13 | void set(std::map &map, k key, v value) { 14 | // TODO: 实现函数 15 | } 16 | 17 | // ---- 不要修改以下代码 ---- 18 | int main(int argc, char **argv) { 19 | using namespace std::string_literals; 20 | 21 | std::map secrets; 22 | 23 | set(secrets, "hello"s, "world"s); 24 | ASSERT(key_exists(secrets, "hello"s), "\"hello\" shoud be in `secrets`"); 25 | ASSERT(!key_exists(secrets, "foo"s), "\"foo\" shoud not be in `secrets`"); 26 | 27 | set(secrets, "foo"s, "bar"s); 28 | set(secrets, "Infini"s, "Tensor"s); 29 | ASSERT(secrets["hello"] == "world", "hello -> world"); 30 | ASSERT(secrets["foo"] == "bar", "foo -> bar"); 31 | ASSERT(secrets["Infini"] == "Tensor", "Infini -> Tensor"); 32 | 33 | set(secrets, "hello"s, "developer"s); 34 | ASSERT(secrets["hello"] == "developer", "hello -> developer"); 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /exercises/30_std_unique_ptr/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // READ: `std::unique_ptr` 8 | 9 | std::vector RECORDS; 10 | 11 | class Resource { 12 | std::string _records; 13 | 14 | public: 15 | void record(char record) { 16 | _records.push_back(record); 17 | } 18 | 19 | ~Resource() { 20 | RECORDS.push_back(_records); 21 | } 22 | }; 23 | 24 | using Unique = std::unique_ptr; 25 | Unique reset(Unique ptr) { 26 | if (ptr) ptr->record('r'); 27 | return std::make_unique(); 28 | } 29 | Unique drop(Unique ptr) { 30 | if (ptr) ptr->record('d'); 31 | return nullptr; 32 | } 33 | Unique forward(Unique ptr) { 34 | if (ptr) ptr->record('f'); 35 | return ptr; 36 | } 37 | 38 | int main(int argc, char **argv) { 39 | std::vector problems[3]; 40 | 41 | drop(forward(reset(nullptr))); 42 | problems[0] = std::move(RECORDS); 43 | 44 | forward(drop(reset(forward(forward(reset(nullptr)))))); 45 | problems[1] = std::move(RECORDS); 46 | 47 | drop(drop(reset(drop(reset(reset(nullptr)))))); 48 | problems[2] = std::move(RECORDS); 49 | 50 | // ---- 不要修改以上代码 ---- 51 | 52 | std::vector answers[]{ 53 | {"fd"}, 54 | // TODO: 分析 problems[1] 中资源的生命周期,将记录填入 `std::vector` 55 | // NOTICE: 此题结果依赖对象析构逻辑,平台相关,提交时以 CI 实际运行平台为准 56 | {"", "", "", "", "", "", "", ""}, 57 | {"", "", "", "", "", "", "", ""}, 58 | }; 59 | 60 | // ---- 不要修改以下代码 ---- 61 | 62 | for (auto i = 0; i < 3; ++i) { 63 | ASSERT(problems[i].size() == answers[i].size(), "wrong size"); 64 | for (auto j = 0; j < problems[i].size(); ++j) { 65 | ASSERT(std::strcmp(problems[i][j].c_str(), answers[i][j]) == 0, "wrong location"); 66 | } 67 | } 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /exercises/31_std_shared_ptr/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | // READ: `std::shared_ptr` 5 | // READ: `std::weak_ptr` 6 | 7 | // TODO: 将下列 `?` 替换为正确的值 8 | int main(int argc, char **argv) { 9 | auto shared = std::make_shared(10); 10 | std::shared_ptr ptrs[]{shared, shared, shared}; 11 | 12 | std::weak_ptr observer = shared; 13 | ASSERT(observer.use_count() == ?, ""); 14 | 15 | ptrs[0].reset(); 16 | ASSERT(observer.use_count() == ?, ""); 17 | 18 | ptrs[1] = nullptr; 19 | ASSERT(observer.use_count() == ?, ""); 20 | 21 | ptrs[2] = std::make_shared(*shared); 22 | ASSERT(observer.use_count() == ?, ""); 23 | 24 | ptrs[0] = shared; 25 | ptrs[1] = shared; 26 | ptrs[2] = std::move(shared); 27 | ASSERT(observer.use_count() == ?, ""); 28 | 29 | std::ignore = std::move(ptrs[0]); 30 | ptrs[1] = std::move(ptrs[1]); 31 | ptrs[1] = std::move(ptrs[2]); 32 | ASSERT(observer.use_count() == ?, ""); 33 | 34 | shared = observer.lock(); 35 | ASSERT(observer.use_count() == ?, ""); 36 | 37 | shared = nullptr; 38 | for (auto &ptr : ptrs) ptr = nullptr; 39 | ASSERT(observer.use_count() == ?, ""); 40 | 41 | shared = observer.lock(); 42 | ASSERT(observer.use_count() == ?, ""); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /exercises/32_std_transform/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // READ: `std::transform` 7 | // READ: `std::vector::begin` 8 | 9 | int main(int argc, char **argv) { 10 | std::vector val{8, 13, 21, 34, 55}; 11 | // TODO: 调用 `std::transform`,将 `v` 中的每个元素乘以 2,并转换为字符串,存入 `ans` 12 | // std::vector ans 13 | ASSERT(ans.size() == val.size(), "ans size should be equal to val size"); 14 | ASSERT(ans[0] == "16", "ans[0] should be 16"); 15 | ASSERT(ans[1] == "26", "ans[1] should be 26"); 16 | ASSERT(ans[2] == "42", "ans[2] should be 42"); 17 | ASSERT(ans[3] == "68", "ans[3] should be 68"); 18 | ASSERT(ans[4] == "110", "ans[4] should be 110"); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /exercises/33_std_accumulate/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../exercise.h" 2 | #include 3 | 4 | // READ: `std::accumulate` 5 | 6 | int main(int argc, char **argv) { 7 | using DataType = float; 8 | int shape[]{1, 3, 224, 224}; 9 | // TODO: 调用 `std::accumulate` 计算: 10 | // - 数据类型为 float; 11 | // - 形状为 shape; 12 | // - 连续存储; 13 | // 的张量占用的字节数 14 | // int size = 15 | ASSERT(size == 602112, "4x1x3x224x224 = 602112"); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /exercises/exercise.h: -------------------------------------------------------------------------------- 1 | #ifndef __EXERCISE_H__ 2 | #define __EXERCISE_H__ 3 | 4 | #include 5 | 6 | #define ASSERT(COND, MSG) \ 7 | if (!(COND)) { \ 8 | std::cerr << "\x1b[31mAssertion failed at line #" << __LINE__ << ": \x1b[0m" << std::endl \ 9 | << std::endl \ 10 | << #COND << std::endl \ 11 | << std::endl \ 12 | << "\x1b[34mMessage:\x1b[0m" << std::endl \ 13 | << std::endl \ 14 | << MSG << std::endl \ 15 | << std::endl; \ 16 | exit(1); \ 17 | } 18 | 19 | #endif// __EXERCISE_H__ 20 | -------------------------------------------------------------------------------- /exercises/xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.debug", "mode.release") 2 | set_encodings("utf-8") 3 | set_warnings("all") 4 | set_kind("binary") 5 | set_languages("cxx17") 6 | 7 | -- 格式化输出 8 | target("exercise00") 9 | add_files("00_hello_world/main.cpp") 10 | 11 | -- 变量与运算符 12 | target("exercise01") 13 | add_files("01_variable&add/main.cpp") 14 | 15 | -- 函数、声明和定义 16 | target("exercise02") 17 | add_files("02_function/main.cpp") 18 | 19 | -- 形参实参 20 | target("exercise03") 21 | add_files("03_argument¶meter/main.cpp") 22 | 23 | -- static 关键字 24 | target("exercise04") 25 | add_files("04_static/main.cpp") 26 | 27 | -- constexpr 编译时常量和运算 28 | target("exercise05") 29 | add_files("05_constexpr/main.cpp") 30 | 31 | -- 数组 32 | target("exercise06") 33 | add_files("06_array/main.cpp") 34 | 35 | -- 循环 36 | target("exercise07") 37 | add_files("07_loop/main.cpp") 38 | 39 | -- 指针 40 | target("exercise08") 41 | add_files("08_pointer/main.cpp") 42 | 43 | -- 枚举/联合体 44 | target("exercise09") 45 | add_files("09_enum&union/main.cpp") 46 | 47 | -- “普通”类型 48 | target("exercise10") 49 | add_files("10_trivial/main.cpp") 50 | 51 | -- 方法 52 | target("exercise11") 53 | add_files("11_method/main.cpp") 54 | 55 | -- const 修饰方法 56 | target("exercise12") 57 | add_files("12_method_const/main.cpp") 58 | 59 | -- 类 60 | target("exercise13") 61 | add_files("13_class/main.cpp") 62 | 63 | -- 析构器 64 | target("exercise14") 65 | add_files("14_class_destruct/main.cpp") 66 | 67 | -- 复制构造函数 68 | target("exercise15") 69 | add_files("15_class_clone/main.cpp") 70 | 71 | -- 移动语义 72 | target("exercise16") 73 | add_files("16_class_move/main.cpp") 74 | 75 | -- 派生 76 | target("exercise17") 77 | add_files("17_class_derive/main.cpp") 78 | 79 | -- 虚函数 80 | target("exercise18") 81 | add_files("18_class_virtual/main.cpp") 82 | 83 | -- 虚析构函数 84 | target("exercise19") 85 | add_files("19_class_virtual_destruct/main.cpp") 86 | 87 | -- 函数模板 88 | target("exercise20") 89 | add_files("20_function_template/main.cpp") 90 | 91 | -- 习题:用于编译器的运行时类型 92 | target("exercise21") 93 | add_files("21_runtime_datatype/main.cpp") 94 | 95 | -- 类模板 96 | target("exercise22") 97 | add_files("22_class_template/main.cpp") 98 | 99 | -- 模板非类型实参 100 | target("exercise23") 101 | add_files("23_template_const/main.cpp") 102 | 103 | -- std::array 104 | target("exercise24") 105 | add_files("24_std_array/main.cpp") 106 | 107 | -- std::vector 108 | target("exercise25") 109 | add_files("25_std_vector/main.cpp") 110 | 111 | -- std::vector 112 | target("exercise26") 113 | add_files("26_std_vector_bool/main.cpp") 114 | 115 | -- 习题:步长计算 116 | target("exercise27") 117 | add_files("27_strides/main.cpp") 118 | 119 | -- std::string 120 | target("exercise28") 121 | add_files("28_std_string/main.cpp") 122 | 123 | -- std::map 124 | target("exercise29") 125 | add_files("29_std_map/main.cpp") 126 | 127 | -- std::transform 128 | target("exercise30") 129 | add_files("30_std_unique_ptr/main.cpp") 130 | 131 | -- std::accumulate 132 | target("exercise31") 133 | add_files("31_std_shared_ptr/main.cpp") 134 | 135 | -- std::transform 136 | target("exercise32") 137 | add_files("32_std_transform/main.cpp") 138 | 139 | -- std::accumulate 140 | target("exercise33") 141 | add_files("33_std_accumulate/main.cpp") 142 | 143 | -- TODO: lambda; deque; forward_list; fs; thread; mutex; 144 | -------------------------------------------------------------------------------- /learn/learn.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | 4 | int main(int argc, char **argv) { 5 | if (argc != 2) { 6 | std::cerr << "Usage: xmake run learn " << std::endl; 7 | return EXIT_FAILURE; 8 | } 9 | int num; 10 | if (1 != std::sscanf(argv[1], "%d", &num)) { 11 | std::cerr << "Invalid exercise number: " << argv[1] << std::endl; 12 | }; 13 | Log{Console{}} << num; 14 | return EXIT_SUCCESS; 15 | } 16 | -------------------------------------------------------------------------------- /learn/summary.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | constexpr auto MAX_EXERCISE = 33; 13 | 14 | int main(int argc, char **argv) { 15 | if (argc == 1) { 16 | Log log{Console{}}; 17 | for (auto i = 0; i <= MAX_EXERCISE; ++i) { 18 | log << i; 19 | } 20 | std::cout << std::accumulate(log.result.begin(), log.result.end(), 0, std::plus{}) << '/' << MAX_EXERCISE + 1 << " ["; 21 | for (auto b : log.result) { 22 | std::cout << (b ? "\x1b[32m#\x1b[0m" : "\x1b[31mX\x1b[0m"); 23 | } 24 | std::cout << ']' << std::endl; 25 | return EXIT_SUCCESS; 26 | } 27 | if (argc == 2 && std::strcmp(argv[1], "--simple") == 0) { 28 | auto concurrency = std::thread::hardware_concurrency(); 29 | if (concurrency == 0) { 30 | concurrency = 1; 31 | } 32 | 33 | std::atomic_int k{0}; 34 | std::vector threads; 35 | threads.reserve(concurrency); 36 | 37 | std::cout << "concurrency: " << concurrency << std::endl; 38 | Log log{Null{}}; 39 | for (auto i = 0u; i <= concurrency; ++i) { 40 | threads.emplace_back([i, &log, &k] { 41 | int j = k.fetch_add(1); 42 | while (j <= MAX_EXERCISE) { 43 | std::printf("run %d at %d\n", j, i); 44 | log << j; 45 | j = k.fetch_add(1); 46 | } 47 | }); 48 | } 49 | for (auto &thread : threads) { 50 | thread.join(); 51 | } 52 | 53 | std::cout << std::accumulate(log.result.begin(), log.result.end(), 0, std::plus{}) << '/' << MAX_EXERCISE + 1 << std::endl; 54 | return EXIT_SUCCESS; 55 | } 56 | std::cerr << "Usage: xmake run summary [--simple]" << std::endl; 57 | return EXIT_FAILURE; 58 | } 59 | -------------------------------------------------------------------------------- /learn/test.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef __XMAKE__ 8 | #define __XMAKE__ "XMAKE is not defined" 9 | #endif 10 | 11 | namespace fs = std::filesystem; 12 | constexpr static auto XMAKE = __XMAKE__; 13 | 14 | static int process_run(const char *cmd, const char *proj, const char *log) { 15 | static const auto exercises = fs::absolute(fs::path(XMAKE) / "exercises"); 16 | auto command = std::string("xmake ") + cmd + " -P \"" + exercises.string() + "\" " + proj; 17 | if (log) { 18 | command += " >> "; 19 | command += log; 20 | command += " 2>&1"; 21 | } 22 | return std::system(command.c_str()); 23 | } 24 | 25 | static bool test_exercise(int n, const char *log) { 26 | std::ofstream file; 27 | if (log) { 28 | file.open(log, std::ios::out | std::ios::app); 29 | } 30 | std::ostream &os = log ? file : std::cout; 31 | 32 | char str[] = "exerciseXX"; 33 | std::sprintf(str, "exercise%02d", n); 34 | 35 | os << "\x1b[34m" << str << " testing" << "\x1b[0m" << std::endl 36 | << "==================" << std::endl; 37 | auto pass = process_run("", str, log) == EXIT_SUCCESS && process_run("run", str, log) == EXIT_SUCCESS; 38 | os << "=================" << std::endl 39 | << "\x1b[" << (pass ? 32 : 31) << 'm' << str << (pass ? " passed" : " failed") << "\x1b[0m" << std::endl 40 | << std::endl; 41 | return pass; 42 | } 43 | 44 | Log &Log::operator<<(unsigned int n) { 45 | namespace fs = std::filesystem; 46 | bool pass; 47 | if (std::holds_alternative(this->dst)) { 48 | pass = test_exercise(n, nullptr); 49 | } else if (std::holds_alternative(this->dst)) { 50 | #if defined(_WIN32) 51 | constexpr auto null = "nul"; 52 | #elif defined(__linux__) || defined(__unix__) || defined(__MACOSX__) || defined(__APPLE__) 53 | constexpr auto null = "/dev/null"; 54 | #else 55 | #error "Unsupported platform" 56 | #endif 57 | pass = test_exercise(n, null); 58 | } else { 59 | const auto path = fs::absolute(fs::path(XMAKE) / "log" / std::get(this->dst)); 60 | const auto path_string = path.string(); 61 | pass = test_exercise(n, path_string.c_str()); 62 | } 63 | { 64 | std::lock_guard lock(this->mutex); 65 | this->result.push_back(pass); 66 | } 67 | return *this; 68 | } 69 | -------------------------------------------------------------------------------- /learn/test.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEST_H__ 2 | #define __TEST_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct Console {}; 10 | struct Null {}; 11 | struct Log { 12 | std::variant dst; 13 | std::vector result; 14 | std::mutex mutex; 15 | Log &operator<<(unsigned int n); 16 | }; 17 | 18 | #endif// __TEST_H__ 19 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.debug", "mode.release") 2 | set_encodings("utf-8") 3 | set_warnings("all") 4 | set_languages("cxx17") 5 | 6 | target("test") 7 | set_kind("static") 8 | add_defines(string.format("__XMAKE__=\"%s\"", os.scriptdir():gsub("\\", "/"))) 9 | add_files("learn/test.cpp") 10 | 11 | target("learn") 12 | set_kind("binary") 13 | add_deps("test") 14 | add_files("learn/learn.cpp") 15 | 16 | target("summary") 17 | set_kind("binary") 18 | add_deps("test") 19 | add_files("learn/summary.cpp") 20 | --------------------------------------------------------------------------------