{"id":18021257,"url":"https://github.com/ho-cooh/sugarpp","last_synced_at":"2025-03-26T22:30:42.916Z","repository":{"id":56640298,"uuid":"268195806","full_name":"HO-COOH/SugarPP","owner":"HO-COOH","description":"C++ syntactic sugar collection","archived":false,"fork":false,"pushed_at":"2022-07-05T23:08:51.000Z","size":162,"stargazers_count":92,"open_issues_count":3,"forks_count":6,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-22T14:22:15.056Z","etag":null,"topics":["syntatic-sugar"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HO-COOH.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-31T02:31:27.000Z","updated_at":"2024-09-27T02:29:21.000Z","dependencies_parsed_at":"2022-08-15T22:30:43.769Z","dependency_job_id":null,"html_url":"https://github.com/HO-COOH/SugarPP","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HO-COOH%2FSugarPP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HO-COOH%2FSugarPP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HO-COOH%2FSugarPP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HO-COOH%2FSugarPP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HO-COOH","download_url":"https://codeload.github.com/HO-COOH/SugarPP/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245747422,"owners_count":20665789,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["syntatic-sugar"],"created_at":"2024-10-30T06:09:14.092Z","updated_at":"2025-03-26T22:30:42.657Z","avatar_url":"https://github.com/HO-COOH.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SugarPP: syntactic 🍬 for programming in C++\n![Build](https://github.com/HO-COOH/SugarPP/workflows/ClangBuild/badge.svg)\n\nSugarPP is a collection of syntactic sugar for C++ code.\n\n- [SugarPP: syntactic 🍬 for programming in C++](#sugarpp-syntactic--for-programming-in-c)\n  - [How to Use](#how-to-use)\n  - [Requirements](#requirements)\n  - [Features](#features)\n    - [Kotlin-style `when` syntax](#kotlin-style-when-syntax)\n      - [Usage](#usage)\n      - [Documentation](#documentation)\n      - [Read More](#read-more)\n    - [Simpler IO](#simpler-io)\n      - [Features](#features-1)\n      - [Usage](#usage-1)\n      - [Documentation](#documentation-1)\n    - [Range](#range)\n      - [Features](#features-2)\n      - [Usage](#usage-2)\n      - [Documentation](#documentation-2)\n    - [Type conversion](#type-conversion)\n      - [Features](#features-3)\n      - [Usage](#usage-3)\n    - [Lazy](#lazy)\n      - [Features](#features-4)\n  - [Motivation](#motivation)\n\n## How to Use\n1. SugarPP is **header only** and **each header file is independent**. Just clone this repository and go to [./include/sugarpp](./include/sugarpp) or copy the corresponding header file you want to use.\n\n    Or, if you want to use it hassle free, copy this Cmake snippet to your root ``CMakeLists.txt`` to automatically download \u0026 use this library in your project. No need to clone the project manually!\n    ```cmake\n    include(FetchContent)\n    FetchContent_Declare(\n        SugarPP\n        GIT_REPOSITORY https://github.com/HO-COOH/SugarPP.git\n        GIT_TAG origin/master\n    )\n    FetchContent_MakeAvailable(SugarPP)\n\n    #Use for your target\n    add_executable(\u003cYour target\u003e main.cpp)\n    target_link_libraries(\u003cYour target\u003e PRIVATE SugarPP)\n    ```\n\n    Or, if you want to use the library globally, copy this Cmake snippet to your roor ``CMakeLists.txt`` to automatically download \u0026 use this library in your project. No need to clone the project!\n    ```cmake\n    include(FetchContent)\n    FetchContent_Declare(\n        SugarPP\n        GIT_REPOSITORY https://github.com/HO-COOH/SugarPP.git\n        GIT_TAG origin/master\n    )\n    FetchContent_MakeAvailable(SugarPP)\n\n    #Use globally\n    link_libraries(SugarPP)\n    ```\n\n2. Then add ``#include \u003csugarpp/xxx/xxx.hpp\u003e``. Also **Note: Everything in SugarPP is now inside ``namespace SugarPP``!** So you may want ``using namespace SugarPP;``\n\nYou can find **quick** documentation for every modules in [./docs](./docs/)\n\nYou can find examples for every modules in [./test/source](./test/source/)\n\nAlternatively, see [generated doxygen document here.](https://ho-cooh.github.io/SugarPPDoc/html/index.html)\n## Requirements\nSugarPP uses various C++17 language features; thus, it requires a C++17 compatible compiler to use.\n\n~~**GCC 10.1 and older has [a known bug](https://stackoverflow.com/questions/64158484/ambiguous-call-after-adding-a-template-parameter-that-has-a-default-type), which causes issues on the overload resolution of ``detail::when_impl``; consider upgrading to GCC 10.2 or newer**~~ Nope, it is compatible with GCC 9.2 now :)\n\nTested with:\n- GCC 10.2 \u0026 GCC 9.2\n- Clang 10.0\n- Visual Studio 16.7\n\n## Features\n\n### Kotlin-style `when` syntax\n\n[Kotlin](https://kotlinlang.org/) has the `when` expression for matching values,\nreplacing the traditional `switch/case` in most languages.\nC/C++'s native `switch/case` has the drawback of only\nmatching **integer** values.\n\nSugarPP ``when``\n\n- Value matching (for *any comparable type*, not just integers):\n```kotlin\n/* Kotlin */\nwhen (x) {\n    1 -\u003e print(\"x == 1\")\n    2 -\u003e print(\"x == 2\")\n    else -\u003e { // Note the block here\n        print(\"x is neither 1 nor 2\")\n    }\n}\n```\n\n```cpp\n/* SugarPP */\nwhen (x,\n    1,      []{ print(\"x == 1\"); },\n    2,      []{ print(\"x == 2\"); },\n    Else(), []{ print(\"x is neither 1 nor 2\"); }\n)(); //returns a function object, use () to call it\n```\n\n- Type matching:\n```kotlin\n/*kotlin*/\nfun describe(obj: Any): String =\n    when (obj) {\n        1          -\u003e \"One\"\n        \"Hello\"    -\u003e \"Greeting\"\n        is Long    -\u003e \"Long\"\n        !is String -\u003e \"Not a string\"\n        else       -\u003e \"Unknown\"\n    }\n```\n```cpp\n/*SugarPP*/\nauto describe = [](auto\u0026\u0026 obj) {\n    return when(obj,\n        1,                      \"One\",\n        \"hello\",                \"Greeting\",\n        is\u003clong\u003e(),             \"long\",\n        is_not\u003cconst char*\u003e(),  \"Not a string\",\n        Else(),                 \"Unknown string\"\n    );\n};\n```\n\n- Polymorphic type matching:\n```cpp\n/*SugarPP*/\nstruct Shape { virtual ~Shape() = default; };\nstruct Circle :Shape {};\nstruct Square :Shape{};\n\nstd::unique_ptr\u003cShape\u003e pt{ new Circle{} };\nwhen(*pt,\n    is_actually\u003cCircle\u003e(), [] { print(\"Circle* pt\"); },\n    is_actually\u003cSquare\u003e(), [] { print(\"Square* pt\"); },\n    Else(),                [] { print(\"Unknown type\"); }\n)();    //\"Circle* pt\"\n```\n\n- Range matching:\n```kotlin\n/*kotlin*/\nval validNumbers = arrayOf(11, 13, 17, 19)\nwhen (x) {\n    in 1..10 -\u003e         print(\"x is in the range\")\n    in validNumbers -\u003e  print(\"x is valid\")\n    !in 10..20 -\u003e       print(\"x is outside the range\")\n    else -\u003e             print(\"none of the above\")\n}\n```\n\n```cpp\n/*SugarPP*/\nstd::array validNumbers{11,13,17,19};\nwhen(x,\n    Range(1, 10),       []{ print(\"x is in the range\"); },\n    Range(validNumbers),[]{ print(\"x is valid\"); },\n    NOT{Range(10, 20)}, []{ print(\"x is outside the range\"); },\n    Else(),             []{ print(\"none of the above\"); }\n)();\n```\n\n- Argument-less switches\n```kotlin\n/*kotlin*/\nwhen {\n    x.isOdd() -\u003e    print(\"x is odd\")\n    y.isEven() -\u003e   print(\"y is even\")\n    else -\u003e         print(\"x+y is even.\")\n}\n```\n\n```cpp\n/*SugarPP*/\nint x = 1, y = 2;\nwhen(\n    isOdd(x),   []{ print(\"x is odd\"); },\n    isEven(y),  []{ print(\"y is even\"); },\n    Else(),     []{ print(\"x+y is even\");}\n)();//\"x is odd\"\n```\nNote: Unlike C/C++ switch-case, ``kotlin`` ``when`` is short-circuiting; the execution terminates at the first satisfied branch. ``SugarPP`` ``when`` has the same behavior.\n\n- Pattern matching\n\nKotlin doesn't seems to support ``_`` as a place holder.\n```rust\n/*Rust*/\nfor i in 1..=100 {\n    match (i % 3, i % 5) {\n        (0, 0) =\u003e println!(\"FizzBuzz\"),\n        (0, _) =\u003e println!(\"Fizz\"),\n        (_, 0) =\u003e println!(\"Buzz\"),\n        (_, _) =\u003e println!(\"{}\", i),\n    }\n}\n```\n\n```cpp\n/*SugarPP*/\nfor(auto i:Range(1, 101))\n{\n    when(std::tuple{ i % 3, i % 5 },\n        std::tuple{ 0, 0 }, [] { print(\"fizzbuzz\"); },\n        std::tuple{ 0, _ }, [] { print(\"fizz\"); },\n        std::tuple{ _, 0 }, [] { print(\"buzz\"); },\n        Else(),             [i] { print(i); }\n    )();\n}\n```\n\n#### Usage\nJust copy [./include/sugarpp/when/when.hpp](./include/sugarpp/when/when.hpp) and add `#include \"when.hpp\"` in your project.\n\n#### Documentation\nSee [docs/When.md](./docs/When.md)\n\n#### Read More\nAt the time of writing this library, I was not aware of the [C++23 pattern matching proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1371r3.pdf). And yes, ``SugarPP::when`` will have performance penality compared with what can be done with ``switch-case`` statement. ``SugarPP::when`` works as recursively comparing the condition to each branch, so I am not sure whether this has performance penalty compared with the pattern matching proposal.\n\nAs I am still an early learner, I will update this part to give you more insight. You can find the original implementation of that proposal [here](https://github.com/mpark/patterns)\n\n-------------------------------------------------------------------------\n### Simpler IO\nIO in C++ should work how you expect it to. SugarPP's IO functions are simple\nand much more intuitive than native C++ IO. No more messing with `getchar()` and\n`getline()` nonsense, and `print()` anything!\n\n#### Features\nThe `input` template function is similar to Python's `input`. It prints a prompt message\nand does automatic error handling - for example, if the input is bad, it will\nclear the bad bit and re-prompt until an acceptable input is given (this behavior can\nbe disabled).\n- If the type is a **primitive**, the function will work the same as `std::cin \u003e\u003e`\n    - If the type given is `unsigned`, `input` will automatically convert the input to an absolute value.\n- If the type is `std::string`, it will behave the same as `std::getline`, getting the whole line at once.\n```cpp\nauto name = input\u003cstd::string\u003e(\"Enter your name: \");\nprint(\"Hello,\", name, \"How old are you?\");\nauto age = input\u003cint\u003e(\"Enter your age: \");\nprint(name, \"is\", age, \"years old\");\n```\n\nThe `print` function also behaves similar to Python's `print`; it can print any number of arguments of any type, separated by a specified delimiter (defaulting to space). SugarPP's `print` can print almost anything:\n- Anything `std::cout` has an overload for\n- Anything that is iterable (i.e. has a `.begin()` or can be called with ``std::begin``)\n- Nested iterables (at any depth)\n- `std::tuple` and `std::pair`\n- ``bool`` will be printed as ``True`` or ``False``\n\n`printLn` behaves similarly, but prints each argument on a new line.\n```cpp\n/*print any iterable*/\nstd::array arr{ 1,2,3 };\nprint(arr); //[1, 2, 3]\n\n/*print a tuple*/\nstd::tuple t{ \"SugarPP\", 123, 45.6f };\nprint(t); //(SugarPP, 123, 45.6)\n\n/*print any nested printable*/\nstd::vector\u003cstd::vector\u003cint\u003e\u003e v1{ {1,2,3}, {5,6,7,8}, {9,10} };\nstd::vector\u003cstd::vector\u003cstd::vector\u003cint\u003e\u003e\u003e v2{ {{1,2,3}, {5,6,7,8}, {9,10}}, {{10,11},{12,13}, {}} };\nprintLn(v1, v2); //[[1, 2, 3], [5, 6, 7, 8], [9, 10]]...\n\n/*print a bool*/\nprint(0.1 + 0.2 == 0.3); //\"False\", you should know why :P\n```\n\nThere are additional ``ThreadSafe`` versions of these functions with the same name, under ``namespace ThreadSafe``.\n\n#### Usage\nJust copy [./include/sugarpp/io/io.hp](./include/sugarpp/io/io.hpp) and add ``#include \"io.hpp\"``.\n\nMore examples in [./test/source/io/io.cpp](./test/source/io/io.cpp).\n\n\n#### Documentation\nSee [docs/IO.md](./docs/IO.md).\n\n------------------------------------------------------------\n### Range\nUse numerical ranges to simplify your range-based `for` loop!\n~~Not to be confused with C++20 ranges.~~ Container Ranges are working in progress towards providing C++20 ranges functionality in C++17.\n\nMany other programming languages have a *range syntax* for iteration:\n```rust\n// e.g. in Rust\nfor n in 0..10 {\n    println!(n);\n}\n```\n```python\n# or in Python\nfor i in range(0, 10):\n    print(i)\n```\n#### Features\nSugarPP defines 3 types of Ranges in some sort of \"class overloading\" way\n- Numerical ranges\n  - `start`, `end`, and `step (default = 1)` with a C++ foreach loop. Type will be inferred and automatically converted if needed.\n    ```cpp\n    for (auto i : Range(2.0, 10.0, 3))\n        print(i);\n    /*\n        2\n        5\n        8\n    */\n    ```\n  - Multiple-dimension ranges\n    ```cpp\n    for (auto [i, j] : Range(-5, 1) | Range(0, 3))\n        print(i, '\\t', j);\n    /*\n        -5       0\n        -5       1\n        -5       2\n        -4       0\n        -4       1\n        -4       2\n        ...\n        0        0\n        0        1\n        0        2\n    */\n    ```\n  - Generating a random number within the range\n    ```cpp\n    /*use range for a random number*/\n    Range r(-1, 100000);\n    print(\"Random number in \", r, \" is \", r.rand());\n\n    /*use range to generate several random numbers*/\n    auto [num1, num2, num3] = Range(1, 10).rand\u003c3\u003e();\n    ```\n  - Filling a container with random numbers\n    ```cpp\n    /*use range to fill a C style array*/\n    double arr[10];\n    Range(-500.0, 500.0).fillRand(arr);\n\n    /*use range to fill a STL container*/\n    std::array\u003cchar, 20\u003e arr2;\n    Range('A', 'z').fillRand(arr2);\n\n    /*Alternatively .randFast() provides a faster way for generating random number using rand() in C*/\n    int arr3[10];\n    Range(-200, 300).fillRandFast(arr3);\n    ```\n- Letter ranges\n\n  Similar functionality with numerical ranges, but works correctly when it is incremented it skips non letter characters\n\n- Container ranges(In progress)\n\nSugarPP also has an `Enumerate` class, which returns a pair of index (default to start at 0) and a reference to the content of iterable, similar to Python's `enumerate()`.\n```python\n# Python\na = [\"enumerate\", \"in\", \"python\"]\nfor i, content in enumerate(a):\n    print(i, content)\n```\n```cpp\n/*SugarPP*/\nstd::array a{\"Enumerate\", \"in\", \"SugarPP\"};\nfor(auto [i, content] : Enumerate(a))\n    print(i, content);\n```\n\n#### Usage\n\nJust copy [./include/sugarpp/range/range.hpp](./include/sugarpp/range/range.hpp) and add ``#include \"range.hpp\"`` for ``Range``.\n\nJust copy [./include/sugarpp/range/enumerate.hpp](./include/sugarpp/range/enumerate.hpp) and add `#include \"enumerate.hpp\"` for ``Enumerate``.\n\nMore examples in [./test/source/range/range.cpp](./test/source/range/range.cpp)\n\n\n#### Documentation\nSee [docs/Range.md](./docs/Range.md).\n\n-----\n### Type conversion\n\n#### Features\n- To \u0026 from string\n  + -\u003e number\n\n    Admit it, ``atoi()``, ``atof()``, ``wcstold()``, ``strtof()`` are some of the ugliest function name in C, and C++ makes it worse by adding more obsecure names like ``std::stoi()``, ``std::stolld()``. That's why SugarPP provides a uniform way of getting numbers from string, which is ``SugarPP::to_num\u003cType\u003e()``, which accepts both normal strings and wide-strings.\n    ```cpp\n    /*SugarPP*/\n    auto str1 = \"42\";\n    auto num1 = to_num\u003cint\u003e(str1);\n\n    auto str2 = \"3.14159\";\n    auto num2 = to_num\u003cdouble\u003e(str2);\n    ```\n  + -\u003e string\n\n    Isn't it wired that something printable can't be converted to string? Isn't it wired that there is a ``std::to_string()`` only works for numerical values?\n    ``SugarPP::to_string`` not only works with anything that can be converted with ``std::to_string()``, but also anything that is printable. Additionally, you can also specify whether it's normal character or wide-character using one template argument.\n    ```cpp\n    auto f_str = to_string(23.43);\n\n    std::ostream\u0026 operator\u003c\u003c(std::ostream\u0026 os, const MyPrintableClass\u0026);\n    auto my_class_str = to_string(my_printable);\n    ```\n#### Usage\nJust copy [./include/sugarpp/types/types.hpp](./include/sugarpp/types/types.hpp) and add ``#include \"types.hpp\"``.\n\nMore example in [./test/source/types/to_string.cpp](./test/source/types/to_string.cpp)\n\n\n-----\n### Lazy\n\n#### Features\nA C++ implementation for [Kotlin](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-lazy/)'s `Lazy`, which represents a value with lazy initialization, \nwith type inference from the initializer and multi-threading synchronization support.\n\n```cpp\nLazy lazyInt{ [] { return 1; } }; //Lazy\u003cInt\u003e\nLazy lazyDouble\n{\n\t[] {\n        /*Some computation*/\n\t\treturn 2.0;\n\t},\n\tThreadSafetyMode::Synchronized\n};\n```\nPossible thread-safety modes are also equivalent to [Kotlin](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-lazy-thread-safety-mode/)\n```cpp\nenum class ThreadSafetyMode\n{\n\tSynchronized,\n\tPublication,\n\tNone\n};\n```\n\n-----\n## Motivation\nI had so much fun writing these and learned so much. ~~Such a great language that gives you nightmare everytime you want to add stuff. C++ itself is difficult enough, yet you realize that you can't even have a compiler to trust with when 3 different compilers (Visual studio, Clang, GCC) gives you different results.~~\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fho-cooh%2Fsugarpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fho-cooh%2Fsugarpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fho-cooh%2Fsugarpp/lists"}