From 28929344c009cda3832fe66d2869afd2744c8381 Mon Sep 17 00:00:00 2001 From: Awais Mirza <47096947+awaismirza92@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:05:30 +0200 Subject: [PATCH] Preserve state left by Jeremy --- docs/concepts/field_syntax.md | 13 +- docs/concepts/structs.md | 19 +- .../concepts/supported_formats/json_schema.md | 8 +- docs/concepts/types.md | 49 +- docs/index.md | 6 +- docs/json_schema.md | 7 +- docs/reflect-cpp-head.png | Bin 0 -> 13941 bytes docs/reflect-cpp-white.png | Bin 0 -> 2138 bytes docs/reflect-cpp.png | Bin 0 -> 1552 bytes docs/stylesheets/extra.css | 176 ++++- flake.nix | 31 +- index.md | 717 ++++++++++++++++++ mkdocs.yaml | 38 +- 13 files changed, 986 insertions(+), 78 deletions(-) create mode 100644 docs/reflect-cpp-head.png create mode 100644 docs/reflect-cpp-white.png create mode 100644 docs/reflect-cpp.png create mode 100644 index.md diff --git a/docs/concepts/field_syntax.md b/docs/concepts/field_syntax.md index 3afe15f4..830d885e 100644 --- a/docs/concepts/field_syntax.md +++ b/docs/concepts/field_syntax.md @@ -16,9 +16,10 @@ These annotations are required if you are planning to use a compiler other than There are a two things you have to keep in mind: -!!! tip 1. If you annotate *some* of the fields using `rfl::Field`, then you must annotate *all* of them. +!!! note + 1. If you annotate *some* of the fields using `rfl::Field`, then you must annotate *all* of them. -2. If you are combining structs using `rfl::Flatten`, then they structs combined like this must either be annotated or not. + 2. If you are combining structs using `rfl::Flatten`, then they structs combined like this must either be annotated or not. You can initilize your struct like this: @@ -90,7 +91,7 @@ all fields will be flattened to a single JSON object. If there are duplicate field names, you will get a compile-time error. -** Example: Every employee is a person ** +**Example: Every employee is a person** ```cpp struct Person { @@ -121,7 +122,7 @@ This flattens all fields into a single JSON object: {"first_name":"Homer","last_name":"Simpson","age":45,"salary":60000.0} ``` -** Example, using the `rfl::Field`-syntax.** +**Example, using the `rfl::Field`-syntax.** ```cpp struct Person { @@ -168,8 +169,8 @@ This can be fully serialized and deserialized using `rfl::json::read` and `rfl:: However, there are a two things you have to keep in mind: -1) If you annotate *some* of the fields using either `rfl::Field`, then you must annotate *all* of them using one of these two. -2) If you are combining structs using `rfl::Flatten`, then they structs combined like this must either be annotated or not. +1. If you annotate *some* of the fields using either `rfl::Field`, then you must annotate *all* of them using one of these two. +2. If you are combining structs using `rfl::Flatten`, then they structs combined like this must either be annotated or not. ### Struct flattening with anonymous fields diff --git a/docs/concepts/structs.md b/docs/concepts/structs.md index ae96813d..a10fa9f4 100644 --- a/docs/concepts/structs.md +++ b/docs/concepts/structs.md @@ -69,20 +69,19 @@ And you can parse it back into a struct: const auto homer = rfl::json::read(json_string).value(); ``` -## Important note +!!! note + **Do not create custom constructors on the structs.** -**Do not create custom constructors on the structs.** + reflect-cpp needs to be able to create the structs like this: -reflect-cpp needs to be able to create the structs like this: + ```cpp + Person{"Bart", "Simpson", ...}; + ``` -```cpp -Person{"Bart", "Simpson", ...}; -``` - -But if you create a custom constructor, then C++ will no longer allow this kind of constructions. + But if you create a custom constructor, then C++ will no longer allow this kind of constructions. -If you want to create the struct from one of your classes (the most like reason, you want to create custom constructors in the first place), -you might want to check out the section on [custom classes](https://github.com/getml/reflect-cpp/blob/main/docs/custom_classes.md) or [custom parsers](https://github.com/getml/reflect-cpp/blob/main/docs/custom_parser.md). + If you want to create the struct from one of your classes (the most like reason, you want to create custom constructors in the first place), + you might want to check out the section on [custom classes](https://github.com/getml/reflect-cpp/blob/main/docs/custom_classes.md) or [custom parsers](https://github.com/getml/reflect-cpp/blob/main/docs/custom_parser.md). ## Utility functions diff --git a/docs/concepts/supported_formats/json_schema.md b/docs/concepts/supported_formats/json_schema.md index 6c995155..6c749bfc 100644 --- a/docs/concepts/supported_formats/json_schema.md +++ b/docs/concepts/supported_formats/json_schema.md @@ -1,12 +1,12 @@ -# JSON schema JSON schemata are a powerful tool for expressing the expected structure of your input. You can use it to validate your input before you even send it to your C++ backend, which will result in better UX. -It can also be used for code generation. For instance, tools such as https://app.quicktype.io/ allow you to generate code in multiple programming languages from the JSON schema (even though the validations are usually omitted). +!!! tip + It can also be used for code generation. For instance, tools such as [quicktype.io](https://app.quicktype.io/) allow you to generate code in multiple programming languages from the JSON schema (even though the validations are usually omitted). -If you are interacting with Python, we warmly recommend https://docs.pydantic.dev/latest/integrations/datamodel_code_generator/. This allows you to generate Pydantic dataclasses, including the validation, from the JSON schema. + If you are interacting with Python, we warmly recommend the [pydantic data model code generator](https://docs.pydantic.dev/latest/integrations/datamodel_code_generator/). This allows you to generate Pydantic dataclasses, including the validation, from the JSON schema. -Note that this is only supported for JSON, not for other formats. + Note that this is only supported for JSON, not for other formats. ## Basic idea diff --git a/docs/concepts/types.md b/docs/concepts/types.md index 7f871298..f547e88e 100644 --- a/docs/concepts/types.md +++ b/docs/concepts/types.md @@ -218,36 +218,37 @@ This results in the following JSON string: {"radius":2.0,"color":"green"} ``` -However, some limitations apply: +!!! note + However, some limitations apply: -1. They must be be scoped enumerations. + 1. They must be be scoped enumerations. -```cpp -/// OK - scoped enumeration -enum class Color1 { red, green, blue, yellow }; + ```cpp + /// OK - scoped enumeration + enum class Color1 { red, green, blue, yellow }; -/// OK - scoped enumeration -enum struct Color2 { red, green, blue, yellow }; + /// OK - scoped enumeration + enum struct Color2 { red, green, blue, yellow }; -/// unsupported - unscoped enumerations -enum Color3 { red, green, blue, yellow }; -``` + /// unsupported - unscoped enumerations + enum Color3 { red, green, blue, yellow }; + ``` -2. You cannot have more than 128 values and if you explicitly assign values, they must be between 0 and 127. + 2. You cannot have more than 128 values and if you explicitly assign values, they must be between 0 and 127. -```cpp -/// OK -enum class Color1 { red, green, blue, yellow }; + ```cpp + /// OK + enum class Color1 { red, green, blue, yellow }; -/// OK -enum struct Color2 { red, green, blue = 50, yellow }; + /// OK + enum struct Color2 { red, green, blue = 50, yellow }; -/// unsupported - red is a negative value -enum Color3 { red = -10, green, blue, yellow }; + /// unsupported - red is a negative value + enum Color3 { red = -10, green, blue, yellow }; -/// unsupported - red is greater than 127 -enum Color3 { red = 200, green, blue, yellow }; -``` + /// unsupported - red is greater than 127 + enum Color3 { red = 200, green, blue, yellow }; + ``` ### Flag enums @@ -503,7 +504,7 @@ is a key-value-pair will be represented as arrays of pairs. ## Variants and tagged unions -### untagged +### Untagged Sometimes you know that the JSON object can be one of several alternatives. For example, you might have several shapes like `Circle`, `Rectangle` or `Square`. For these kind of @@ -541,7 +542,7 @@ several problems with this: 3. It is dangerous. Imagine we had written `std::variant` instead of `std::variant`. This would mean that `Rectangle` could never be matched, because the fields in `Square` are a subset of `Rectangle`. This leads to very confusing bugs. -### internally tagged +### Internally tagged One way to solve this problem is to add a tag inside the class. That is why we have provided a helper class for these purposes: `rfl::TaggedUnion`. @@ -660,7 +661,7 @@ const auto r2 = rfl::json::read(json_string); Note that in this case the type of the field `shape` MUST be `rfl::Literal`. Also note that this is exactly how tagged unions work in Pydantic. -### externally tagged +### Externally tagged Another approach is to add external tags. diff --git a/docs/index.md b/docs/index.md index 088b150e..9c6237b6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,6 +15,7 @@ As the aforementioned libraries are among the most widely used in the respective languages, reflect-cpp fills an important gap in C++ development. It reduces boilerplate code and increases code safety. ## Why use reflect-cpp? + - Close integration with containers from the C++ standard library - Close adherence to C++ idioms - Out-of-the-box support for JSON @@ -39,9 +40,10 @@ The following table lists the serialization formats currently supported by refle | XML | [pugixml](https://github.com/zeux/pugixml) | >= 1.14 | MIT | Textual format used in many legacy projects | | YAML | [yaml-cpp](https://github.com/jbeder/yaml-cpp) | >= 0.8.0 | MIT | Textual format with an emphasis on readability | -Support for more serialization formats is in development. Refer to the [issues](https://github.com/getml/reflect-cpp/issues) for details. +!!! note + Support for more serialization formats is in development. Refer to the [issues](https://github.com/getml/reflect-cpp/issues) for details. -Please also refer to the *vcpkg.json* in this repository. + Please also refer to the *vcpkg.json* in this repository. ## Simple Example diff --git a/docs/json_schema.md b/docs/json_schema.md index 6c995155..6530488f 100644 --- a/docs/json_schema.md +++ b/docs/json_schema.md @@ -1,12 +1,13 @@ -# JSON schema JSON schemata are a powerful tool for expressing the expected structure of your input. You can use it to validate your input before you even send it to your C++ backend, which will result in better UX. It can also be used for code generation. For instance, tools such as https://app.quicktype.io/ allow you to generate code in multiple programming languages from the JSON schema (even though the validations are usually omitted). -If you are interacting with Python, we warmly recommend https://docs.pydantic.dev/latest/integrations/datamodel_code_generator/. This allows you to generate Pydantic dataclasses, including the validation, from the JSON schema. +!!! note + If you are interacting with Python, we warmly recommend https://docs.pydantic.dev/latest/integrations/datamodel_code_generator/. + This allows you to generate Pydantic dataclasses, including the validation, from the JSON schema. -Note that this is only supported for JSON, not for other formats. + Note that this is only supported for JSON, not for other formats. ## Basic idea diff --git a/docs/reflect-cpp-head.png b/docs/reflect-cpp-head.png new file mode 100644 index 0000000000000000000000000000000000000000..153e38f183ff00210ee9f70a63bad13dbd579107 GIT binary patch literal 13941 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 MjEQdPZ}zwT7f}E{G5`Po literal 0 HcmV?d00001 diff --git a/docs/reflect-cpp-white.png b/docs/reflect-cpp-white.png new file mode 100644 index 0000000000000000000000000000000000000000..e80ba8567a15bf0d359be18062380d3e36769731 GIT binary patch literal 2138 zcmV-g2&MOlP){Ad&lWHAY=84YS;qs%0fq%q2`&CHuO zGiu&=z4xTaR@o{_Z2VgA7qC^M}_>GHF~8J9mDHsY};cp8QMnO~gz#Y)!DgyrRQ zYPl8V=RVF$>ec+rw8Reac1&W762Z?nBSZ<)%b?6*=}sR#vZ73W=sSOsWyw`!)+u1H zM20LD0Z~_^x+*a(x+)__!Ri0szMAR&KDT;(1gf5F`?U+Ix1hLb+aHIvT|5HsBV6Q5 z`O60|@Fx0ZN$cDP-#VOKmbBy!oNqwOgPt%F-;!6{lTO3S2{g<>(>2sCxIO#X=lCAt zYw}fBu)7D{a^AQ21#-w|>sJO^zyJUM32;bRa{vGf6951U69E94oEQKA27gIJK~#90 z?VDR@990;{ede-wHm7w6jWM6K~bVJ_#K zZ@&MWGv_*Eh!9C};C^5g5ChJ(?Z<%QK+?AV7jy~PfDts zYO!}n`Zk>#Q8OaxD@m)SO6(Jo`idab$w5i?6;o)1q-~OtQy@0OpOT&_hS(}e@5?G) zQ_TrUJ0ykP(%1#>lr%cEIwx>aQfpLVAD47|iUqpSFX^_RIsz33NvnZ3fW?JAu!#8? zxWyRrt6xN4!II_zUjp?ZI2I)bfa{Ghr*cKA2wH3ShmYvvNi7;wfNB=q^RiCBTDT zfw=^)2;Y~Y;aTsv3YN5x(t3BDESEIPEx;>>HvkJFb0>Q0ux4Dc@DhAuRGt)xD!_FJ z=_UBQ=zJ*}i`*_|36?e7niIJ@(c`MYEr-tm&WzTVq7ZYt=r$>~knyL}jWu5k-UAK$ zb-Bbdwy?~(Y~k|QJCBWR!?Cwtufl4J&yrMQj5+Dyclr*}+VnVcMA8j8BTqvlT`MUi zX+Y8i&TzLPof-2cfy0q{7Y*IOQLG|8&l%`eq&tBhfs;UQq^?DPG3GBxM}dEVzn$Tg zl3r!}oi)I7KpaT=mjCICfHQzol6Ej&7YtPb>wsmp$RChhu}rikJ%m-0J1Y_A`Cenp zXL)%lT?ecT7=4>DCgo-(egCV02W;05dM#b#6b!s=i{sp|9ghTb+|D;ZSHQSl_T5?w z!2KQe#TYYKNV|wPn%L z(sD&tSJyv>4jme4ZEd~0v$J!kzrTNU*REZk1n}#Cq(9PH?DTjE9+338kI}i3x+Hy< z?tdfc9$70BJ#LMDP!fqmMMpl+;$930!ab?Zw$zU`NE z#7(f*XemgMej2z4n9KO+>ME?x%rc-Qs6Z#RwY8h->+554=FF*x$K$sH#A2~qW3gC8 zJRUa<4Go@CIBCZ47zs{B@WtuU2HYHDf#h{xkKi9{mT^i(93mc3K5g6ui4f;%lkLqmsw27tP{x>YN~KbxeSLk$ckkZ4uDQASYoFwKx5LLJ`L=fsJ8?y{8sE~= zvN(}QROR8zr;?7jHA|E3X`N0GQt!%*=H}+#^Gs6EJDiu`X+~LahDzWB@Ll@68W?2e z2p$A}0y1M}Q)!I3EGxE3Su2nhY>fE`tNVMJ|6P$T3rDAaMX51!`^g`U`l)CrzhQY{AI-IqQ&P`lR4l$^iHK-f^#=3bhsF193O(Pja%h zf)H8-8m5!c{Z2DY1UsE5i5xEU=S8<7T`lR}^s%$Kfbj|YF~{{>VHrDT6zFFbVg9Xo zDb{L4F(^34qb!EglF8K4B}qAlXF6eaJW7I6&EYAjrAu{Ad&lWHAY=84YS;qs%0fq%q2`&CHuO zGiu&=z4xTaR@o{_Z2VgA7qC^M}_>GHF~8J9mDHsY};cp8QMnO~gz#Y)!DgyrRQ zYPl8V=RVF$>ec+rw8Reac1&W762Z?nBSZ<)%b?6*=}sR#vZ73W=sSOsWyw`!)+u1H zM20LD0Z~_^x+*a(x+)__!Ri0szMAR&KDT;(1gf5F`?U+Ix1hLb+aHIvT|5HsBV6Q5 z`O60|@Fx0ZN$cDP-#VOKmbBy!oNqwOgPt%F-;!6{lTO3S2{g<>(>2sCxIO#X=lCAt zYw}fBu)7D{a^AQ21#-w|>sJO^zyJUM32;bRa{vGf6951U69E94oEQKA1T{%SK~!jg z?b=I-97Pxh@ZW4Eni!+5VvHWdO9aIi>Om0%AsWz=2#N|(JKeM0Grcpd**|>D^w-r@|9{n2 z&+eA`unG_00*vE~#Afhk%C_T8{8nSxePbnF##aKlPD$H~O?U>U51loZB!%xRL3S?t z2^-KJ7Wq5zZ4JcJHiIo#GvDN&!oHfxr)@XZ&Ij=}-p15Cl8<%(&!bfr`4-+2nt8s- zN1Mj0bqRO^hlfZ$-4YXnlD`FijDUQ!UvOo=lqIO8Qv24PtF=_NQp0zzVCrdW31@00SYfZxn z+>>?Y3Ak+Dsq~L)vyMCgD~CX&U#!SFvI3T1`Ea=Oh2u6q*-gaCB++5~*dwE|E5(-3 z0nPCn^BYS&*UDUrtjuaJmeUjnV6-W*XpW`o_pLS+LXfO5)8;g!+VV|%jIy3ET z<0{>0dWW#~_;RS!%HccQfL*vI@lCh!u@GPIqR=W+IT>7zi*!S)m9hQ!q$HU&dY?x8 zDy%}&m=ZQs>+xvf+uas+KM^x#cf2&V<7eHOk=ZllOiA*vS^f|nF`eU!ojqO7bly=$ zk$@@rEz=JU_VG-5v^s{O#ayoryc}QP9igdT5r(#Y@VHRhyKqR@z80Yt0WD!T+k?CD z7}nvf(v-V+2^+B*=izTWEgVngp%wwFafPtqS%gcI^f%$G($oWPPoBL^n4p#>e4Z>w zUyFcSg>YFdoX$Ox_|L|LC5cBw!Rv&BgHx01Wd-ST1yntcjLVBy`w_eFL&dx%h018* z|_GU%CZ)37E#sctM!dCh-}bo15=(STA(k zhj5Be0c(@|@=%Ka!h-7lDolG|V`61c+dMETbmZ{;FA0dtoLOjNTVj&mrV5B5v<;v3 zN_YhK4uereOvqV_6@^d}QMwby#7)EhVic-n9+(SDaY>K+TZIX*DuyPYBY#JgzTa=Y zLlH0~Y{2WriP4FhI)qYCsae8t*Z;e)I?SFvEEd8phS|Z){k+qP>~+>hcQX_F&!L?3 zD{!$;>Bq!m`#=Zx37^cQ&wQe|3Eh_rllV}#!{(*$<%`$jxI;G@_TZzO^gHps0mFN< z*>6nh_*7<<*)_hY%EP@5m-5ic{O{}iZ3giZhQ3f$K=#=>1Y}S3Dv257Bcn0l&Efv! zI+7^rs6`!lpf^H;2pGq^gA`r|1__}?;Di&Mf0000 * { + --md-typeset-a-color: #6241be; + /* #00B1BB; */ + --md-banner-bg: var(--md-typeset-a-color); +} +[data-md-color-scheme="slate"] > * { + --md-typeset-a-color: var(--md-primary-fg-color--light); + --md-banner-bg: #784ff4; +} + +/* Hide shadow under navigation */ +.md-header[data-md-state="shadow"], +.md-header--shadow { + box-shadow: none; +} + +/* Logo mod */ + +#logo_light_mode { + display: var(--md-footer-logo-light-mode); +} + +#logo_dark_mode { + display: var(--md-footer-logo-dark-mode); +} + +[data-md-color-scheme="default"] { + --md-footer-logo-dark-mode: none; + --md-footer-logo-light-mode: block; +} + +[data-md-color-scheme="slate"] { + --md-footer-logo-dark-mode: block; + --md-footer-logo-light-mode: none; +} + +.md-header__button.md-logo img, +.md-header__button.md-logo svg { + height: 1.1rem; + margin-top: 0.2rem; +} + +/* header font */ + +.md-typeset h2 { + font-weight: 600; +} + +.md-typeset h2, +.md-typeset h3 { + color: var(--md-typeset-color--light); +} + +/* content partial */ + +.md-typeset { + font-size: 0.85rem; +} + +.md-typeset .admonition, +.md-typeset details { + font-size: 0.675rem; +} + +.md-typeset code { + font-size: 0.9em; +} + +.md-typeset h2 code { + font-size: 0.75em; +} + +.md-typeset h1.title-description { + margin-bottom: 0em !important; +} + +.md-typeset p.description { + margin-bottom: 1.25em; + color: var(--md-typeset-color--mute); + font-size: 1.15em; + /* font-weight: */ +} + +.md-typeset hr.description { + height: 1px; + margin-top: 0; + margin-bottom: 3em; + border: none; + background-color: var(--pg-light-border); +} + +/* Header - background */ + :root { - --md-primary-fg-color: #42A5B9; - --md-primary-fg-color--light: #42A5B9; - --md-primary-fg-color--dark: #7A6A6E; - --md-accent-fg-color: #F0685C; - --md-accent-fg-color--transparent: #F0685C; + --header-backdrop-blur: 10px; +} + +.md-header, +.md-tabs { + /* firefox fallback */ + background-color: rgba(255, 255, 255, 0.95); +} + +[data-md-color-scheme="slate"] .md-header, +[data-md-color-scheme="slate"] .md-tabs { + /* firefox fallback */ + background-color: rgba(9, 9, 9, 0.95); +} + +@supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none)) { + .md-header, + .md-tabs { + background-color: rgba(255, 255, 255, 0.5); + -webkit-backdrop-filter: blur(var(--header-backdrop-blur)); + backdrop-filter: blur(var(--header-backdrop-blur)); + } + [data-md-color-scheme="slate"] .md-header, + [data-md-color-scheme="slate"] .md-tabs { + background-color: hsla(0, 0%, 100%, 0.03); + -webkit-backdrop-filter: blur(var(--header-backdrop-blur)); + backdrop-filter: blur(var(--header-backdrop-blur)); + } +} + +/* Nav */ + +.md-nav { + font-size: 0.73rem; + line-height: 1.25; +} + +.md-tabs__link { + font-size: 0.75rem; } diff --git a/flake.nix b/flake.nix index f04fe064..7730e976 100644 --- a/flake.nix +++ b/flake.nix @@ -11,10 +11,10 @@ extra-substituters = "https://devenv.cachix.org"; }; - outputs = { self, nixpkgs, devenv, getml_flake, ... } @ inputs: + outputs = { self, nixpkgs, devenv, ... } @ inputs: let system = "x86_64-linux"; - #pkgs = nixpkgs.legacyPackages.${system}; + pkgs = nixpkgs.legacyPackages.${system}; in { packages.${system}.devenv-up = self.devShells.${system}.default.config.procfileScript; @@ -24,15 +24,28 @@ modules = [ ({ pkgs, config, ... }: { # This is your devenv configuration - languages.python = { - enable = true; - version = "3.11.3"; + languages = { + python = { + enable = true; + version = "3.11.3"; - venv.enable = true; - venv.requirements = '' - mkdocs mkdocs-material mkdocsstrings - ''; + venv.enable = true; + venv.requirements = '' + mkdocs + mkdocs-material + mkdocstrings + mkdoxy + pymdown-extensions + ''; + }; + cplusplus = { + enable = true; + }; }; + packages = with pkgs; [ + ninja + zip + ]; #enterShell = '' # export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath [pkgs.zlib]}:$LD_LIBRARY_PATH diff --git a/index.md b/index.md new file mode 100644 index 00000000..09a2c0a8 --- /dev/null +++ b/index.md @@ -0,0 +1,717 @@ +# ![C++](https://img.shields.io/badge/c++-%2300599C.svg?style=for-the-badge&logo=c%2B%2B&logoColor=white) reflect-cpp + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity) +[![Generic badge](https://img.shields.io/badge/C++-20-blue.svg)](https://shields.io/) +[![Generic badge](https://img.shields.io/badge/gcc-11+-blue.svg)](https://shields.io/) +[![Generic badge](https://img.shields.io/badge/clang-16+-blue.svg)](https://shields.io/) +[![Generic badge](https://img.shields.io/badge/MSVC-17+-blue.svg)](https://shields.io/) + +![image](banner1.png) + +**reflect-cpp** is a C++-20 library for **fast serialization, deserialization and validation** using reflection, similar to [pydantic](https://github.com/pydantic/pydantic) in Python, [serde](https://github.com/serde-rs) in Rust, [encoding](https://github.com/golang/go/tree/master/src/encoding) in Go or [aeson](https://github.com/haskell/aeson/tree/master) in Haskell. + +As the aforementioned libraries are among the most widely used in the respective languages, reflect-cpp fills an important gap in C++ development. It reduces boilerplate code and increases code safety. + +## Why use reflect-cpp? +- Close integration with containers from the C++ standard library +- Close adherence to C++ idioms +- Out-of-the-box support for JSON +- Simple installation: If no JSON support is required, reflect-cpp is header-only. For JSON support, only a single source file needs to be compiled. +- Simple extendability to other serialization formats +- Simple extendability to custom classes + +## Simple Example + +```cpp +#include +#include + +struct Person { + std::string first_name; + std::string last_name; + int age; +}; + +const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .age = 45}; + +// We can now write into and read from a JSON string. +const std::string json_string = rfl::json::write(homer); +auto homer2 = rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"first_name":"Homer","last_name":"Simpson","age":45} +``` + +You can transform the field names from `snake_case` to `camelCase` like this: + +```cpp +const std::string json_string = + rfl::json::write(homer); +auto homer2 = + rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"firstName":"Homer","lastName":"Simpson","age":45} +``` + +Or you can use another format, such as YAML. + +```cpp +#include + +// ... (same as above) + +const std::string yaml_string = rfl::yaml::write(homer); +auto homer2 = rfl::yaml::read(yaml_string).value(); +``` + +The resulting YAML string looks like this: + +```yaml +first_name: Homer +last_name: Simpson +age: 45 +``` + +This will work for just about any example in the entire documentation +and any supported format, except where explicitly noted otherwise: + +```cpp +rfl::bson::write(homer); +rfl::cbor::write(homer); +rfl::flexbuf::write(homer); +rfl::msgpack::write(homer); +rfl::toml::write(homer); +rfl::xml::write(homer); + +rfl::bson::read(bson_bytes); +rfl::cbor::read(cbor_bytes); +rfl::flexbuf::read(flexbuf_bytes); +rfl::msgpack::read(msgpack_bytes); +rfl::toml::read(toml_string); +rfl::xml::read(xml_string); +``` +## Serialization formats + +reflect-cpp provides a unified reflection-based interface across different serialization formats. It is deliberately designed in a very modular way, using [concepts](https://en.cppreference.com/w/cpp/language/constraints), to make it as easy as possible to interface various C or C++ libraries related to serialization. Refer to the [documentation](https://github.com/getml/reflect-cpp/tree/main/docs) for details. + +The following table lists the serialization formats currently supported by reflect-cpp and the underlying libraries used: + +| Format | Library | Version | License | Remarks | +|--------------|------------------------------------------------------|--------------|------------| -----------------------------------------------------| +| JSON | [yyjson](https://github.com/ibireme/yyjson) | >= 0.8.0 | MIT | out-of-the-box support, included in this repository | +| BSON | [libbson](https://github.com/mongodb/libbson) | >= 1.25.1 | Apache 2.0 | JSON-like binary format | +| CBOR | [tinycbor](https://github.com/intel/tinycbor) | >= 0.6.0 | MIT | JSON-like binary format | +| flexbuffers | [flatbuffers](https://github.com/google/flatbuffers) | >= 23.5.26 | Apache 2.0 | Schema-less version of flatbuffers, binary format | +| msgpack | [msgpack-c](https://github.com/msgpack/msgpack-c) | >= 6.0.0 | BSL 1.0 | JSON-like binary format | +| TOML | [toml++](https://github.com/marzer/tomlplusplus) | >= 3.4.0 | MIT | Textual format with an emphasis on readability | +| XML | [pugixml](https://github.com/zeux/pugixml) | >= 1.14 | MIT | Textual format used in many legacy projects | +| YAML | [yaml-cpp](https://github.com/jbeder/yaml-cpp) | >= 0.8.0 | MIT | Textual format with an emphasis on readability | + +Support for more serialization formats is in development. Refer to the [issues](https://github.com/getml/reflect-cpp/issues) for details. + +Please also refer to the *vcpkg.json* in this repository. + +## Simple Example + +```cpp +#include +#include + +struct Person { + std::string first_name; + std::string last_name; + int age; +}; + +const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .age = 45}; + +// We can now write into and read from a JSON string. +const std::string json_string = rfl::json::write(homer); +auto homer2 = rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"first_name":"Homer","last_name":"Simpson","age":45} +``` + +You can transform the field names from `snake_case` to `camelCase` like this: + +```cpp +const std::string json_string = + rfl::json::write(homer); +auto homer2 = + rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"firstName":"Homer","lastName":"Simpson","age":45} +``` + +Or you can use another format, such as YAML. + +```cpp +#include + +// ... (same as above) + +const std::string yaml_string = rfl::yaml::write(homer); +auto homer2 = rfl::yaml::read(yaml_string).value(); +``` + +The resulting YAML string looks like this: + +```yaml +first_name: Homer +last_name: Simpson +age: 45 +``` + +This will work for just about any example in the entire documentation +and any supported format, except where explicitly noted otherwise: + +```cpp +rfl::bson::write(homer); +rfl::cbor::write(homer); +rfl::flexbuf::write(homer); +rfl::msgpack::write(homer); +rfl::toml::write(homer); +rfl::xml::write(homer); + +rfl::bson::read(bson_bytes); +rfl::cbor::read(cbor_bytes); +rfl::flexbuf::read(flexbuf_bytes); +rfl::msgpack::read(msgpack_bytes); +rfl::toml::read(toml_string); +rfl::xml::read(xml_string); +``` + +## More Comprehensive Example + +```cpp +#include +#include +#include + +// Age must be a plausible number, between 0 and 130. This will +// be validated automatically. +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + +const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + +const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + +const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + +// We can now transform this into a JSON string. +const std::string json_string = rfl::json::write(homer); +std::cout << json_string << std::endl; + +// We can also directly write into std::cout (or any other std::ostream). +rfl::json::write(homer, std::cout) << std::endl; +``` + +This results in the following JSON string: + +```json +{"firstName":"Homer","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":45,"email":"homer@simpson.com","children":[{"firstName":"Bart","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":10,"email":"bart@simpson.com","children":[]},{"firstName":"Lisa","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":8,"email":"lisa@simpson.com","children":[]},{"firstName":"Maggie","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":0,"email":"maggie@simpson.com","children":[]}]} +``` + +We can also create structs from the string: + +```cpp +auto homer2 = rfl::json::read(json_string).value(); + +// Fields can be accessed like this: +std::cout << "Hello, my name is " << homer.first_name() << " " + << homer.last_name() << "." << std::endl; + +// Since homer2 is mutable, we can also change the values like this: +homer2.first_name = "Marge"; + +std::cout << "Hello, my name is " << homer2.first_name() << " " + << homer2.last_name() << "." << std::endl; +``` + +## Error messages + +reflect-cpp returns clear and comprehensive error messages: + +```cpp +const std::string faulty_json_string = + R"({"firstName":"Homer","lastName":12345,"town":"Springfield","birthday":"04/19/1987","age":145,"email":"homer(at)simpson.com"})"; +const auto result = rfl::json::read(faulty_json_string); +``` + +Yields the following error message: + +``` +Found 5 errors: +1) Failed to parse field 'lastName': Could not cast to string. +2) Failed to parse field 'birthday': String '04/19/1987' did not match format '%Y-%m-%d'. +3) Failed to parse field 'age': Value expected to be less than or equal to 130, but got 145. +4) Failed to parse field 'email': String 'homer(at)simpson.com' did not match format 'Email': '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'. +5) Field named 'children' not found. +``` + +## JSON schema + +reflect-cpp also supports generating JSON schemata: + +```cpp +struct Person { + std::string first_name; + std::string last_name; + rfl::Description<"Must be a proper email in the form xxx@xxx.xxx.", + rfl::Email> + email; + rfl::Description< + "The person's children. Pass an empty array for no children.", + std::vector> + children; + float salary; +}; + +const std::string json_schema = rfl::json::to_schema(); +``` + +The resulting JSON schema looks like this: + +```json +{"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/definitions/Person","definitions":{"Person":{"type":"object","properties":{"children":{"type":"array","description":"The person's children. Pass an empty array for no children.","items":{"$ref":"#/definitions/Person"}},"email":{"type":"string","description":"Must be a proper email in the form xxx@xxx.xxx.","pattern":"^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$"},"first_name":{"type":"string"},"last_name":{"type":"string"},"salary":{"type":"number"}},"required":["children","email","first_name","last_name","salary"]}}} +``` + +Note that this is currently supported for JSON only, since most other formats do not support schemata in the first place. + +## Enums + +reflect-cpp supports scoped enumerations: + +```cpp +enum class Shape { circle, square, rectangle }; + +enum class Color { red = 256, green = 512, blue = 1024, yellow = 2048 }; + +struct Item { + float pos_x; + float pos_y; + Shape shape; + Color color; +}; + +const auto item = Item{.pos_x = 2.0, + .pos_y = 3.0, + .shape = Shape::square, + .color = Color::red | Color::blue}; + +rfl::json::write(item); +``` + +This results in the following JSON string: + +```json +{"pos_x":2.0,"pos_y":3.0,"shape":"square","color":"red|blue"} +``` + +You can also directly convert between enumerator values and strings with `rfl::enum_to_string()` and `rfl::string_to_enum()`, or +obtain list of enumerator name and value pairs with `rfl::get_enumerators()` or `rfl::get_enumerator_array()`. + +## Algebraic data types + +reflect-cpp supports Pydantic-style tagged unions, which allow you to form algebraic data types: + +```cpp +struct Circle { + double radius; +}; + +struct Rectangle { + double height; + double width; +}; + +struct Square { + double width; +}; + +using Shapes = rfl::TaggedUnion<"shape", Circle, Square, Rectangle>; + +const Shapes r = Rectangle{.height = 10, .width = 5}; + +const auto json_string = rfl::json::write(r); +``` + +This results in the following JSON string: + +```json +{"shape":"Rectangle","height":10.0,"width":5.0} +``` + +Other forms of tagging are supported as well. Refer to the [documentation](https://github.com/getml/reflect-cpp/tree/main/docs) for details. + +## Reflective programming + +Beyond serialization and deserialization, reflect-cpp also supports reflective programming in general. + +For instance: + +```cpp +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + unsigned int age; + std::vector children; +}; + +for (const auto& f : rfl::fields()) { + std::cout << "name: " << f.name() << ", type: " << f.type() << std::endl; +} +``` + +You can also create a view and then access these fields using `std::get` or `rfl::get`, or iterate over the fields at compile-time: + +```cpp +auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8}; + +const auto view = rfl::to_view(lisa); + +// view.values() is a std::tuple containing +// pointers to the original fields. +// This will modify the struct `lisa`: +*std::get<0>(view.values()) = "Maggie"; + +// All of this is supported as well: +*view.get<1>() = "Simpson"; +*view.get<"age">() = 0; +*rfl::get<0>(view) = "Maggie"; +*rfl::get<"first_name">(view) = "Maggie"; + +view.apply([](const auto& f) { + // f is a an rfl::Field pointing to the original field. + std::cout << f.name() << ": " << rfl::json::write(*f.value()) << std::endl; +}); +``` + +It also possible to replace fields: + +```cpp +struct Person { + std::string first_name; + std::string last_name; + std::vector children; +}; + +const auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson"}; + +// Returns a deep copy of "lisa" with the first_name replaced. +const auto maggie = rfl::replace( + lisa, rfl::make_field<"first_name">(std::string("Maggie"))); +``` + +Or you can create structs from other structs: + +```cpp +struct A { + std::string f1; + std::string f2; +}; + +struct B { + std::string f3; + std::string f4; +}; + +struct C { + std::string f1; + std::string f2; + std::string f4; +}; + +const auto a = A{.f1 = "Hello", .f2 = "World"}; + +const auto b = B{.f3 = "Hello", .f4 = "World"}; + +// f1 and f2 are taken from a, f4 is taken from b, f3 is ignored. +const auto c = rfl::as(a, b); +``` + +You can also replace fields in structs using fields from other structs: + +```cpp +const auto a = A{.f1 = "Hello", .f2 = "World"}; + +const auto c = C{.f1 = "C++", .f2 = "is", .f4 = "great"}; + +// The fields f1 and f2 are replaced with the fields f1 and f2 in a. +const auto c2 = rfl::replace(c, a); +``` + + +## Support for containers + +### C++ standard library + +reflect-cpp supports the following containers from the C++ standard library: + +- `std::array` +- `std::deque` +- `std::forward_list` +- `std::map` +- `std::multimap` +- `std::multiset` +- `std::list` +- `std::optional` +- `std::pair` +- `std::set` +- `std::shared_ptr` +- `std::string` +- `std::tuple` +- `std::unique_ptr` +- `std::unordered_map` +- `std::unordered_multimap` +- `std::unordered_multiset` +- `std::unordered_set` +- `std::variant` +- `std::vector` +- `std::wstring` + +### Additional containers + +In addition, it supports the following custom containers: + +- `rfl::Box`: Similar to `std::unique_ptr`, but (almost) guaranteed to never be null. +- `rfl::Literal`: An explicitly enumerated string. +- `rfl::NamedTuple`: Similar to `std::tuple`, but with named fields that can be retrieved via their name at compile time. +- `rfl::Ref`: Similar to `std::shared_ptr`, but (almost) guaranteed to never be null. +- `rfl::Result`: Allows for exception-free programming. +- `rfl::TaggedUnion`: Similar to `std::variant`, but with explicit tags that make parsing more efficient. +- `rfl::Validator`: Allows for automatic input validation. +- `rfl::Variant`: An alternative to `std::variant`. + +### Custom classes + +Finally, it is very easy to extend full support to your own classes, refer to the [documentation](https://github.com/getml/reflect-cpp/tree/main/docs) for details. + +## Why do we need this? + +Suppose your C++ program has complex data structures it needs to save and load. Or maybe it needs to interact with some kind of external API. If you do this the traditional way, you will have a lot of boilerplate code. This is annoying and error-prone. + +reflect-cpp is not just a reflection library, it is for **serialization, deserialization and validation** through reflection. + +That means that you can encode your requirements about the input data in the type system and have them validated upfront. This is why the library also includes algebraic data types like tagged unions and numerous validation routines. Having your requirements encoded in the type system is the most reliable way of ensuring they are met. If your requirements are not met, the user of your software gets a very clear error message. Encoding your requirements in the type system also makes it a lot easier for anyone reading your code. + +This increases user experience and developer experience, it makes your code safer (fewer bugs) and more secure (less prone to malicious attacks). + +For a more in-depth theoretical discussions of these topics, the following books are warmly recommended: + +- *Category Theory for Programmers* by Bartosz Milewski (https://github.com/hmemcpy/milewski-ctfp-pdf/releases) +- *Domain Modeling Made Functional* by Scott Wlaschin + +## Documentation + +Click [here](https://github.com/getml/reflect-cpp/tree/main/docs). + +## Benchmarks + +reflect-cpp conducts continuuous benchmarking across different operating systems, compilers and architectures +and publishes the results on its [Actions tab](https://github.com/getml/reflect-cpp/actions). +Refer to the [benchmarks folder](https://github.com/getml/reflect-cpp/tree/main/benchmarks) for details. + +The benchmarks show that reflect-cpp is not only very convenient, but also one the fastest JSON libraries for C++. +It is faster than RapidJSON and about 10 times faster than nlohmann/json. It can be even faster than that, +if you choose to use a different format supported by reflect-cpp, such as msgpack. + +## Installation + +The following compilers are supported: +- GCC 11.4 or higher +- Clang 16.0 or higher +- MSVC 17.8 (19.38) or higher + +### Option 1: Header-only + +If you **do not** need JSON support or you want to link YYJSON yourself, then reflect-cpp is header-only. Simply copy the contents of the folder `include` into your source repository or add it to your include path. + +### Option 2: Include source files into your own build + +Simply copy the contents of the folder `include` into your source repository or add it to your include path and also add `src/yyjson.c` to your source files for compilation. + +If you need support for other serialization formats like flexbuffers or XML, you should also include and link the respective libraries, as listed in the section on serialization formats. + +### Option 3: Compilation using cmake + +This will simply compile YYJSON, which is the JSON library underlying reflect-cpp. You can then include reflect-cpp in your project and link to the binary +to get reflect-cpp with JSON support. + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -j 4 # gcc, clang +cmake --build build --config Release -j 4 # MSVC +``` + +### Option 4: Compilation using cmake and vcpkg + +If you want serialization formats other than JSON, you can either install them manually or use vcpkg. + +To install vcpkg: + +```bash +git submodule update --init +./vcpkg/bootstrap-vcpkg.sh # Linux, macOS +./vcpkg/bootstrap-vcpkg.bat # Windows +# You may be prompted to install additional dependencies. +``` + +To use reflect-cpp in your project: + +```cmake +add_subdirectory(reflect-cpp) # Add this project as a subdirectory + +set(REFLECTCPP_BSON ON) # Optional +set(REFLECTCPP_CBOR ON) # Optional +set(REFLECTCPP_FLEXBUFFERS ON) # Optional +set(REFLECTCPP_MSGPACK ON) # Optional +set(REFLECTCPP_TOML ON) # Optional +set(REFLECTCPP_XML ON) # Optional +set(REFLECTCPP_YAML ON) # Optional + +target_link_libraries(your_project PRIVATE reflectcpp) # Link against the library +``` + +## Troubleshooting vcpkg + +vcpkg is a great, but very ambitious and complex project (just like C++ is a great, but very ambitious and complex language). Here are some of the you might run into and how to resolve them: + +1. A lot of problems can simply be resolved by deleting the build directory using `rm -rf build`. + +2. *Environment variable VCPKG_FORCE_SYSTEM_BINARIES must be set on arm, s390x, ppc64le and riscv platforms.* - This usually happens on arm platforms like the Apple Silicon chips and can be resolved by simply preceding your build with `export VCPKG_FORCE_SYSTEM_BINARIES=arm` or `export VCPKG_FORCE_SYSTEM_BINARIES=1`. + +3. On some occasions you might be asked to specify a compiler. You can do so by simply adding it to the cmake command as follows: `cmake -S . -B build ... -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++` or `cmake -S . -B build ... -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17` (or whatever supported compiler you would like to use). + +## Compiling and running the tests + +reflect-cpp uses vcpkg for dependency management, including +gtest, which is required for the tests. + +```bash +# bootstrap vcpkg if you haven't done so already +git submodule update --init +./vcpkg/bootstrap-vcpkg.sh # Linux, macOS +./vcpkg/bootstrap-vcpkg.bat # Windows +# You may be prompted to install additional dependencies. +``` + +### JSON only + +To compile the tests, do the following: + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DREFLECTCPP_BUILD_TESTS=ON +cmake --build build -j 4 # gcc, clang +cmake --build build --config Release -j 4 # MSVC +``` + +To run the tests, do the following: + +``` +./build/tests/json/reflect-cpp-json-tests +``` + +### All serialization formats + +To compile the tests with serialization formats other than JSON, do the following: + +```bash +cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_CBOR=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_MSGPACK=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_TOML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release +cmake --build build -j 4 # gcc, clang +cmake --build build --config Release -j 4 # MSVC +``` + +To run the tests, do the following: + +``` +./build/tests/bson/reflect-cpp-bson-tests +./build/tests/cbor/reflect-cpp-cbor-tests +./build/tests/flexbuffers/reflect-cpp-flexbuffers-tests +./build/tests/msgpack/reflect-cpp-msgpack-tests +./build/tests/json/reflect-cpp-json-tests +./build/tests/toml/reflect-cpp-toml-tests +./build/tests/xml/reflect-cpp-xml-tests +./build/tests/yaml/reflect-cpp-yaml-tests +``` + +## How to contribute + +### Make sure includes are relative + +In order for the library to be able to function header-only, we need internal includes to be relative and not depend on any externally set include directory. + +That is, for example, if you are within any file in `rfl/internal`, prefer +```cpp +#include "to_ptr_named_tuple.hpp" +``` +over +```cpp +#include "rfl/internal/to_ptr_named_tuple.hpp" +``` +For further details and reasoning, please refer to [#30](https://github.com/getml/reflect-cpp/issues/30). + +## Related projects + +reflect-cpp was originally developed for [getml-community](https://github.com/getml/getml-community), the fastest open-source tool for feature engineering on relational data and time series. If you are interested in Data Science and/or Machine Learning, please check it out. + +## Authors + +reflect-cpp has been developed by [scaleML](https://www.scaleml.de), a company specializing in software engineering and machine learning for enterprise applications. It is extensively used for [getML](https://getml.com), a software for automated feature engineering using relational learning. + +## License + +reflect-cpp is released under the MIT License. Refer to the LICENSE file for details. + +reflect-cpp includes [YYJSON](https://github.com/ibireme/yyjson), the fastest JSON library currently in existence. YYJSON is written by YaoYuan and also released under the MIT License. + +reflect-cpp includes [compile-time-regular-expressions](https://github.com/hanickadot/compile-time-regular-expressions). CTRE is written by Hana Dusíková and released under the Apache-2.0 License with LLVM exceptions. diff --git a/mkdocs.yaml b/mkdocs.yaml index 4561c108..6b537720 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -7,20 +7,27 @@ theme: name: 'material' #custom_dir: 'docs/theme' palette: - #- media: "(prefers-color-scheme: light)" - # scheme: default - # primary: #F0685C - # accent: #F0685C - # toggle: - # icon: material/lightbulb-outline - # name: "Switch to dark mode" - #- media: "(prefers-color-scheme: dark)" - # scheme: slate - # primary: pink - # accent: pink - # toggle: - # icon: material/lightbulb - # name: "Switch to light mode" + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-5 + name: "Switch to light mode" + - media: "(prefers-color-scheme: light)" + scheme: default + primary: deep purple + accent: deep purple + toggle: + icon: material/brightness-2 + name: "Switch to dark mode" + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: black + accent: deep purple + toggle: + icon: material/brightness-6 + name: "Switch to system preference" + font: + text: Roboto + code: Roboto Mono features: - content.tabs.link - content.code.annotate @@ -36,7 +43,8 @@ theme: - navigation.tracking - search.suggest - toc.follow - #logo: 'banner2.png' + logo: 'reflect-cpp-head.png' + logo_dark_mode: "reflect-cpp-white.png" favicon: 'banner2.png' extra_css: