{"id":38525240,"url":"https://github.com/yosh-matsuda/field-reflection","last_synced_at":"2026-01-17T06:45:59.872Z","repository":{"id":220626678,"uuid":"751915821","full_name":"yosh-matsuda/field-reflection","owner":"yosh-matsuda","description":"Compile-time reflection for C++ to get field names and types from a struct/class.","archived":false,"fork":false,"pushed_at":"2024-12-27T05:20:55.000Z","size":27,"stargazers_count":22,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-27T06:20:21.836Z","etag":null,"topics":["cpp","cpp20","header-only","reflection","static-reflection"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yosh-matsuda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-02T15:56:20.000Z","updated_at":"2024-12-27T05:20:59.000Z","dependencies_parsed_at":"2024-12-15T13:22:32.004Z","dependency_job_id":"8d44430d-1725-4c01-a8a5-153f9d60d89e","html_url":"https://github.com/yosh-matsuda/field-reflection","commit_stats":null,"previous_names":["yosh-matsuda/field-reflection"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/yosh-matsuda/field-reflection","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Ffield-reflection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Ffield-reflection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Ffield-reflection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Ffield-reflection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yosh-matsuda","download_url":"https://codeload.github.com/yosh-matsuda/field-reflection/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yosh-matsuda%2Ffield-reflection/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28502871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T04:31:57.058Z","status":"ssl_error","status_checked_at":"2026-01-17T04:31:45.816Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["cpp","cpp20","header-only","reflection","static-reflection"],"created_at":"2026-01-17T06:45:59.576Z","updated_at":"2026-01-17T06:45:59.860Z","avatar_url":"https://github.com/yosh-matsuda.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# field-reflection C++\n\nCompile-time reflection for C++ to get field names and types from a struct/class.\n\n[![CI](https://github.com/yosh-matsuda/field-reflection/actions/workflows/tests.yml/badge.svg)](https://github.com/yosh-matsuda/field-reflection/actions/workflows/tests.yml)\n\n## Features\n\n* compile-time reflection\n* header-only single file\n* no user-side macros\n* no dependencies\n\n## Requirements\n\nC++20 compilers are required to use this library.\n\n* GCC \u003e= 11\n* Clang \u003e= 15\n    * with libc++-16 or later\n* MSVC \u003e= 19.37\n    * clang-cl \u003e= 17\n\n## Usage\n\n```cpp\n#include \u003carray\u003e\n#include \u003ccstdint\u003e\n#include \u003cmap\u003e\n#include \u003cprint\u003e\n#include \u003cstring\u003e\n#include \"field_reflection.hpp\"\n\nusing namespace field_reflection;\n\nstruct my_struct\n{\n    int i = 287;\n    double d = 3.14;\n    std::string hello = \"Hello World\";\n    std::array\u003cstd::uint64_t, 3\u003e arr = {1, 2, 3};\n    std::map\u003cstd::string, int\u003e map{{\"one\", 1}, {\"two\", 2}};\n};\n\n// get field names\nconstexpr auto my_struct_n0 = field_name\u003cmy_struct, 0\u003e;  // \"i\"sv\nconstexpr auto my_struct_n1 = field_name\u003cmy_struct, 1\u003e;  // \"d\"sv\nconstexpr auto my_struct_n2 = field_name\u003cmy_struct, 2\u003e;  // \"hello\"sv\nconstexpr auto my_struct_n3 = field_name\u003cmy_struct, 3\u003e;  // \"arr\"sv\nconstexpr auto my_struct_n4 = field_name\u003cmy_struct, 4\u003e;  // \"map\"sv\n\n// get field types\nusing my_struct_t0 = field_type\u003cmy_struct, 0\u003e;  // int\nusing my_struct_t1 = field_type\u003cmy_struct, 1\u003e;  // double\nusing my_struct_t2 = field_type\u003cmy_struct, 2\u003e;  // std::string\nusing my_struct_t3 = field_type\u003cmy_struct, 3\u003e;  // std::array\u003cuint64_t, 3\u003e\nusing my_struct_t4 = field_type\u003cmy_struct, 4\u003e;  // std::map\u003cstd::string, int\u003e\n\n// get field values with index\nauto s = my_struct{};\nauto\u0026 my_struct_v0 = get_field\u003c0\u003e(s);  // s.i\nauto\u0026 my_struct_v1 = get_field\u003c1\u003e(s);  // s.d\nauto\u0026 my_struct_v2 = get_field\u003c2\u003e(s);  // s.hello\nauto\u0026 my_struct_v3 = get_field\u003c3\u003e(s);  // s.arr\nauto\u0026 my_struct_v4 = get_field\u003c4\u003e(s);  // s.map\n\n// visit each field\nfor_each_field(s, [](std::string_view field, auto\u0026 value) {\n    // i: 287\n    // d: 3.14\n    // hello: Hello World\n    // arr: [1, 2, 3]\n    // map: {\"one\": 1, \"two\": 2}\n    std::println(\"{}: {}\", field, value);\n});\n```\n\n## API References\n\n### Concepts\n\n```cpp\ntemplate\u003ctypename T\u003e\nconcept field_countable;\ntemplate\u003ctypename T\u003e\nconcept field_referenceable;\ntemplate\u003ctypename T\u003e\nconcept field_namable;\n```\n\nThe `field_countable` is a concept that checks if the type `T` is a field-countable struct. Internally, it is equivalent to that `T` is [aggregate type](https://en.cppreference.com/w/cpp/types/is_aggregate) and the number of the field is less than or equal to `100`.\n\nThe `field_referenceable` is a concept that checks if a field of the type `T` can be referenced by index. This includes the `field_countable` concept. The implementation of the `field_referenceable` concept is the condition that the `field_countable` type `T` has no base class.\n\nThe `field_namable` is a concept that checks if a field name of the type `T` can be obtained by index statically. This includes the `field_referenceable` concept and also requires that the type `T` has a field and (practically) there is no reference type member.\n\n### `field_count`\n\n```cpp\ntemplate \u003cfield_countable T\u003e\nconstexpr std::size_t field_count;\n```\n\nGet the number of fields from the `field_countable` type `T`.\n\n### `field_name`\n\n```cpp\ntemplate \u003cfield_namable T, std::size_t N\u003e\nconstexpr std::string_view field_name;\n```\n\nGet the name of the `N`-th field as `std::string_view` from the `field_namable` type `T`.\n\n### `field_type`\n\n```cpp\ntemplate \u003cfield_referenceable T, std::size_t N\u003e\nusing field_type;\n```\n\nGet the type of the `N`-th field from the `field_referenceable` type `T`.\n\n### `get_field`\n\n```cpp\n// reference\ntemplate \u003cstd::size_t N, field_referenceable T\u003e\nconstexpr auto\u0026 get_field(T\u0026 t) noexcept;\n\n// const reference\ntemplate \u003cstd::size_t N, field_referenceable T\u003e\nconstexpr const auto\u0026 get_field(const T\u0026 t) noexcept;\n\n// rvalue reference\ntemplate \u003cstd::size_t N, field_referenceable T\u003e\nconstexpr auto get_field(T\u0026\u0026 t) noexcept;\n```\n\nExtracts the `N`-th element from the `field_referenceable` type `T` and returns a reference to it. It behaves like `std::get` for `std::tuple` but returns a lvalue value instead of a rvalue reference.\n\n### `type_name`\n\n```cpp\ntemplate \u003cclass T\u003e\nconstexpr std::string_view type_name;\n```\n\nGet the name of the type `T`.\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```cpp\n#include \u003cfield_reflection.hpp\u003e\n#include \u003cformat\u003e\n#include \u003ctype_traits\u003e\n#include \u003cutility\u003e // std::exchange\n#include \u003cvariant\u003e\n\nusing Token = std::variant\u003cstruct Number, struct Identifier\u003e;\n\nstruct Number {\n  int value;\n};\n\nstruct Identifier {\n  std::string name;\n};\n\ntemplate \u003ctypename T, typename Variant\u003e\ninline constexpr bool alternative_of = false;\n\ntemplate \u003ctypename T, typename... Types\u003e\ninline constexpr bool alternative_of\u003cT, std::variant\u003cTypes...\u003e\u003e =\n  (std::is_same_v\u003cT, Types\u003e || ...);\n\ntemplate \u003ctypename T\u003e\n  requires alternative_of\u003cT, Token\u003e\nstruct std::formatter\u003cT\u003e {\n  constexpr auto parse(auto\u0026 ctx) -\u003e decltype(ctx.begin()) {\n    auto it = ctx.begin();\n    if (it != ctx.end() and *it != '}') {\n      throw std::format_error(\"invalid format\");\n    }\n    return it;\n  }\n\n  auto format(const T\u0026 t, auto\u0026 ctx) const -\u003e decltype(ctx.out()) {\n    auto out = ctx.out();\n    out = std::format_to(out, \"{} {{\", field_reflection::type_name\u003cT\u003e);\n    const char* dlm = \"\";\n    field_reflection::for_each_field(\n      t, [\u0026](std::string_view name, const auto\u0026 value) {\n        std::format_to(\n          out, \"{}\\n  .{}={}\", std::exchange(dlm, \",\"), name, value);\n      });\n    out = std::format_to(out, \"\\n}}\");\n    return out;\n  }\n};\n\n#include \u003ciostream\u003e\n\nint main() {\n  Number num{42};\n  Identifier ident{\"ident\"};\n  std::cout \u003c\u003c std::format(\"{}\", num) \u003c\u003c std::endl;\n  std::cout \u003c\u003c std::format(\"{}\", ident) \u003c\u003c std::endl;\n  // Expected Output\n  // ===============\n  // Number {\n  //   .value=42\n  // }\n  // Identifier {\n  //   .name=ident\n  // }\n}\n```\n\n🔗[Execution example in Compiler Explorer](https://godbolt.org/z/94fPc895o)\n\u003c/details\u003e\n\n### `for_each_field`, `all_of_field`, `any_of_field`\n\n```cpp\n// unary operation\ntemplate \u003cfield_referenceable T, typename Func\u003e\nvoid for_each_field(T\u0026\u0026 t, Func\u0026\u0026 func);\ntemplate \u003cfield_referenceable T, typename Func\u003e\nbool all_of_field(T\u0026\u0026 t, Func\u0026\u0026 func);\ntemplate \u003cfield_referenceable T, typename Func\u003e\nbool any_of_field(T\u0026\u0026 t, Func\u0026\u0026 func);\n\n// binary operation\ntemplate \u003cfield_referenceable T, typename Func\u003e\nvoid for_each_field(T\u0026\u0026 t1, T\u0026\u0026 t2, Func\u0026\u0026 func);\ntemplate \u003cfield_referenceable T, typename Func\u003e\nbool all_of_field(T\u0026\u0026 t1, T\u0026\u0026 t2, Func\u0026\u0026 func);\ntemplate \u003cfield_referenceable T, typename Func\u003e\nbool any_of_field(T\u0026\u0026 t1, T\u0026\u0026 t2, Func\u0026\u0026 func);\n```\n\nVisits each field of the type `T` and applies the unary or binary operation `func`. The `func` must be a callable object that takes one of the following kinds of arguments:\n\n* Arguments of one or two references to the field for the `field_referenceable` type `T`.\n* Arguments of `std::string_view` and one or two references to the field for the `field_namable` type `T`.\n\nThe `for_each_field` just applies the `func` and returns `void`, while the `all_of_field` and `any_of_field` return `bool` indicating whether all or any of the `func` returns `true`.\n\nFor example, the following code prints the field names and values of the `my_struct` `s`:\n\n```cpp\nconstexpr auto func = [](std::string_view field, auto\u0026 value) {\n    std::println(\"{}: {}\", field, value);\n};\nfor_each_field(s, func);\n```\n\nThe above is equivalent to:\n\n```cpp\nfunc(\"i\"sv, s.i);\nfunc(\"d\"sv, s.d);\nfunc(\"hello\"sv, s.hello);\nfunc(\"arr\"sv, s.arr);\nfunc(\"map\"sv, s.map);\n```\n\nThe first argument in the definition of the `func` can be omitted if it is not needed.\n\nThe binary operation version of `for_each_field` is useful for comparing each field of two objects of the same type:\n\n```cpp\nconstexpr auto func = [](std::string_view field, auto\u0026 value1, auto\u0026 value2) {\n    if (value1 != value2) {\n        std::println(\"s1 and s2 have a different value: s1.{} = {}, s2.{} = {}\",\n                      field, value1, field, value2);\n    }\n};\nfor_each_field(s1, s2, func);\n```\n\n### `to_tuple`\n\n```cpp\ntemplate \u003cfield_referenceable T\u003e\nconstexpr std::tuple\u003c...\u003e to_tuple(T\u0026\u0026 t);\n```\n\nCopy a `field_referenceable` type `T` object and convert it to `std::tuple` where each field has the same type as `T`. For example, `my_struct` object can be converted to  object of type `std::tuple\u003cint, double, std::string, std::array\u003cstd::uint64_t, 3\u003e, std::map\u003cstd::string, int\u003e\u003e`.\n\n## Acknowledgments\n\nThis project is strongly inspired by the following and stands as\n\n* an alternative to [visit_struct](https://github.com/cbeck88/visit_struct) without macros,\n* a reflection library that is a partial reimplementation of [reflect-cpp](https://github.com/getml/reflect-cpp).\n\nThe C++20 implementation of the counting field in this library is partially referenced to [Boost.PFR](https://github.com/boostorg/pfr).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyosh-matsuda%2Ffield-reflection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyosh-matsuda%2Ffield-reflection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyosh-matsuda%2Ffield-reflection/lists"}