{"id":33003024,"url":"https://github.com/fabian-jung/tsmp","last_synced_at":"2025-11-18T08:03:20.425Z","repository":{"id":45116149,"uuid":"420512580","full_name":"fabian-jung/tsmp","owner":"fabian-jung","description":null,"archived":false,"fork":false,"pushed_at":"2024-05-23T19:59:40.000Z","size":225,"stargazers_count":91,"open_issues_count":9,"forks_count":10,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-06-21T18:09:19.200Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/fabian-jung.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}},"created_at":"2021-10-23T20:09:14.000Z","updated_at":"2024-06-02T17:43:34.000Z","dependencies_parsed_at":"2024-01-03T01:20:05.891Z","dependency_job_id":"a6c597b0-f277-48de-9140-1584b9494d9f","html_url":"https://github.com/fabian-jung/tsmp","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/fabian-jung/tsmp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabian-jung%2Ftsmp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabian-jung%2Ftsmp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabian-jung%2Ftsmp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabian-jung%2Ftsmp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabian-jung","download_url":"https://codeload.github.com/fabian-jung/tsmp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabian-jung%2Ftsmp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285028340,"owners_count":27102545,"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","status":"online","status_checked_at":"2025-11-18T02:00:05.759Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2025-11-13T14:00:38.909Z","updated_at":"2025-11-18T08:03:20.414Z","avatar_url":"https://github.com/fabian-jung.png","language":"C++","funding_links":[],"categories":["Reflection"],"sub_categories":[],"readme":"# Tool supported meta programming\n\nTSMP is a static reflection library that is implemented with C++20 and code generation with the help of libclang and cmake.\n\nThe current implementation supports the following example application:\n```cpp\n#include \u003cfmt/core.h\u003e\n#include \u003cfmt/ranges.h\u003e\n#include \u003ctsmp/introspect.hpp\u003e\n\n#include \u003cconcepts\u003e\n#include \u003cranges\u003e\n#include \u003ctype_traits\u003e\n\ntemplate \u003cclass T\u003e\nconcept Arithmetic = std::floating_point\u003cT\u003e || std::integral\u003cT\u003e;\n\nstd::string to_json(const auto\u0026 value);\n\nstd::string to_json(const char* const\u0026 cstr) {\n    return fmt::format(\"\\\"{}\\\"\", cstr);\n}\n\nstd::string to_json(const std::string\u0026 str) {\n    return fmt::format(\"\\\"{}\\\"\", str);\n}\n\nstd::string to_json(const Arithmetic auto\u0026 number) {\n    return std::to_string(number);\n}\n\ntemplate \u003cstd::ranges::input_range Range\u003e\nstd::string to_json(const Range\u0026 range) {\n    using value_type = typename Range::value_type;\n    using signature_t = std::string(*)(const value_type\u0026);\n    const auto elements = std::ranges::transform_view(range, static_cast\u003csignature_t\u003e(to_json));\n    return fmt::format(\"[{}]\", fmt::join(elements, \",\"));\n}\n\nstd::string to_json(const auto\u0026 value) {\n    tsmp::introspect introspect { value };\n    const auto fields = introspect.visit_fields([](size_t id, std::string_view name, const auto\u0026 field) {\n        return fmt::format(\"\\\"{}\\\":{}\", name, to_json(field));\n    });\n    return fmt::format(\"{{{}}}\", fmt::join(fields, \",\")); \n}\n\nint main(int, char*[]) {\n\n    struct foo_t {\n        int i { 42 };\n        float f { 1337.0f };\n        const char* s = \"Hello World!\";\n        struct bar_t {\n            int i { 0 };\n        } bar;\n        std::array\u003cint, 4\u003e numbers { 1, 2, 3, 4 };\n    } foo;\n\n    fmt::print(\"{}\\n\", to_json(foo));\n    // Prints: {\"i\":42,\"f\":1337.000000,\"s\":\"Hello World!\",\"bar\":{\"i\":0},\"numbers\":[1,2,3,4]}\n    return 0;\n}\n```\n\nBecause as of now there is no support for static reflection in C++, the meta-data from the type system needs to be made accessible to your application. This is done via a helper tool, that will parse your source code and generates a special type trait, that are used by the library. \n\nThis step does not need any user intervention or addition of macros to your source code. The code generator needs to be integrated into the build system, but can be done pretty easy for cmake with the help of the function ```enable_reflection()```.\n\n```cmake\nadd_executable(example)\nenable_reflection(example) # This line will add code generation for this target\n```\n\n# Dependencies\n\nThe ```tsmp::reflect\u003c\u003e``` trait is specialized using concepts. Therefore a c++20 compliant compiler is required. gcc-11 is used in the CI-Pipeline. Additionaly libclang and the llvm runtime needs to be installed. The code generator is implemented with the help of the fmt lib. In addition to the reflection tsmp has a json encoder and decoder module. This can be used\nby enabling reflection on your target and linking against tsmp::json. The json module has addition dependencies to libfmt and nlohmann-json.\nThe [ci pipeline](.github/workflows/ctest_pipeline.yml) shows a complete workflow including setup, build and test execution based on a ubuntu 20.04 image.\n\n# How to install and use TSMP\n\n## With CMake FetchContents\n\nYou can integrate TSMP into your project with the help of the FetchContent function. Below is a listing of the relevant parts of a CMakeLists.txt file.\n```\ninclude(FetchContent)\nFetchContent_Declare(\n    tsmp\n    GIT_REPOSITORY https://github.com/fabian-jung/tsmp.git\n    GIT_TAG main\n)\nFetchContent_MakeAvailable(tsmp)\n\n\nadd_executable(main main.cpp)\nenable_reflection(main)\ntarget_link_libraries(main PRIVATE tsmp::json)\n```\n\n# API\n\nCurrently there are two ways to interact with tsmp. You can fetch the raw field and function descriptions via ```tsmp::reflect``` or get a more user friendly interface via ```tsmp::introspect```\n\n## Get raw reflection data\n\nThe access to reflection is done via the ```tsmp::reflect\u003cT\u003e``` type trait. This trait exports a ```name()```, ```fields()``` and ```functions()``` member functions. The name will does return const char* of the unmangled typename T, if the type can be identified via its members. Otherwise it will retunr the string ```\"\u003cunknown\u003e\"```. The ```fields()``` and ```functions()``` returns a tuple of speciallized ```field_description_t```. This type holds fields with a id, name of the member and pointer to member. Its not guranteed that the id of attributes will relate to the position in the binary layout of the type.\n\nThe definition of the field_description_t can be found here [reflect.hpp](include/tsmp/reflect.hpp).\n\n```cpp\n// Pseudo code of the interface\n\nnamespace tsmp {\n\ntemplate \u003cclass T, class V\u003e\nstruct field_description_t {\n    using value_type = V;\n    std::size_t id;\n    std::string_view name;\n    V T::* ptr;\n};\n\ntemplate \u003cclass T\u003e\nstruct reflect {\n    constexpr static const char* name();\n    constexpr static std::tuple\u003cfield_description_t...\u003e fields();\n    constexpr static std::tuple\u003cfield_description_t...\u003e functions();\n};\n\n}\n```\n\nHere is a short example of the basic usage:\n```cpp\n\nint main() {\n    struct foo_t {\n        int a, b;\n    }\n    constexpr auto fields = tsmp::reflect\u003cfoo_t\u003e::fields();\n    constexpr expr a_decl = std::get\u003c0\u003e(fields);\n    constexpr expr b_decl = std::get\u003c1\u003e(fields);\n    std::cout \u003c\u003c \"name of first field in foo_t: \" \u003c\u003c a_decl.name  \u003c\u003c std::endl; // prints \"a\";\n    std::cout \u003c\u003c \"name of second field in foo_t: \" \u003c\u003c b_decl.name  \u003c\u003c std::endl; // prints \"b\";\n}\n\n```\n\n## Introspect by reference\n\nWorking with tuples and pointer-to-member(functions) can be quite cumbersome. For that reason there is a helper class that can be used to directly access members of lvalues. This utility class is called ```tsmp::introspect``` and the implementation can be found here [introspect.hpp](include/tsmp/introspect.hpp). The interface looks more or less like this:\n\n```cpp\n\nnamespace tsmp {\n\ntemplate \u003cclass T\u003e\nstruct introspect {\n    T\u0026 internal;\n\n    constexpr introspect(T\u0026 lvalue) noexcept;\n\n    static constexpr auto field_id(const std::string_view name);\n    static constexpr auto function_id(const std::string_view name);\n\n    template \u003csize_t id, class... Args\u003e\n    constexpr decltype(auto) call(Args... args) const ;\n\n    template \u003cstring_literal_t name, class... Args\u003e\n    constexpr decltype(auto) call(Args... args) const;\n\n    template \u003csize_t id\u003e\n    constexpr auto\u0026 get() const;\n\n    template \u003cstring_literal_t name\u003e\n    constexpr auto\u0026 get() const;\n\n    constexpr auto fetch(const std::string_view name) const;\n\n    template \u003cclass Arg\u003e\n    constexpr auto set(const std::string_view name, Arg arg) const;\n\n    template \u003cclass Visitor\u003e\n    constexpr std::array\u003cauto\u003e visit_fields(Visitor\u0026\u0026 visitor) const;\n};\n\n}\n\n```\n\nThe functions do pretty much, what the name and signature suggest. ```field_id()``` and ```function()``` return the ids of either a field of function. A member with the requested name does not exist, the function will throw in runtime contexts and do not compile in ```constexpr``` contexts.\n\nThe ```call()``` lets you call a function on the passed reference. The ```get()``` will return a reference to a member variable. The ```fetch()``` and The ```set()``` can be used to retreive or set a member variable in runtime-contexts. The ```fetch()``` function will return a ```std::variant``` containing all member types.\n\nThe The ```visit_fields()``` function lets you iterate over all member variables. It'll take a visitor with the signature ```auto(size_t id, std::string_view name, auto\u0026 value)```. The first parameter is a unique id for ever field. The second parameter contains the name of the member. The third parameter must be overloaded for every member type can be a (const-)reference or pass-by-value and will return the value of the corresponding field. The variant can return ```void``` for all overloads, then the ```visit_fields()``` will also return void or it can return an arbitraty, but same type T for every overload. In that case the result will be an ```std::array\u003cT\u003e```.\n\nThe usage of  ```tsmp::introspect``` could look like this:\n\n```cpp\nint main() {\n    foo_t {\n        int a;\n        void bar() {};    \n    } foo;\n\n    tsmp::introspect intro { foo };\n\n    intro.get\u003c\"a\"\u003e = 42;\n    assert(std::get\u003cint\u003e(intro.fetch(\"a\")\u003e == 42);\n\n    intro.call\u003c\"bar\"\u003e();\n\n    std::cout \u003c\u003c \"foo fields:\" \u003c\u003c std::endl;\n    intro.visit_fields([](size_t id, std::string_view name, const auto\u0026 field){\n        std::cout \u003c\u003c \"name:\" \u003c\u003c field \u003c\u003c std::endl;\n    });\n}\n```\n\n# How does it work?\n\nThe source file `bin/introspect.cpp` is compiled into a tool that can parse source files and traverse the abstract syntax tree of your build. The tool will search the syntax tree for all occurrences of specializations of `tsmp::reflect`. Once found the template argument will be visited and scanned for fields and functions. With this data a header file is generated with `tsmp::reflect` specialization for the type, that looks somewhat like this:\n\n```cpp\n// ...\ntemplate \u003cclass T\u003e\nrequires requires(T) { // specialize for every type that has the following members\n    T::i;\n    \u0026T::bar;\n}\nstruct reflect\u003cT\u003e {\n    constexpr static auto name() {\n        return \"foo_t\";\n    }\n    constexpr static auto fields() {\n        return std::make_tuple(\n               // this is the field description that will be passed to the user\n               field_description_t{ 0, \"i\", \u0026T::i }\n        );\n    }\n    constexpr static auto functions() {\n        return std::make_tuple(\n               // this is the field description that will be passed to the user\n               field_description_t{ 0, \"bar\", \u0026T::bar }\n        );\n    }\n};\n//...\n```\n\nThe cmake build system will take care of generating this header, setting the include path to it and correctly tracking the dependencies.\n\n\n# Discussion\n\nThis library is not production ready and needs to handle a lot more of the corner cases. The basic idea seems viable, but all the corner cases needs to be addressed. If you want a further read into the code base I recommend starting with the [examples](examples), [reflect](test/reflect.cpp) and [introspect](test/introspect.cpp) tests and work your way up from there. If you are interested in the source tree parsing take a look at the [bin/introspect.cpp](tooling/bin/introspect.cpp)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabian-jung%2Ftsmp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabian-jung%2Ftsmp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabian-jung%2Ftsmp/lists"}