{"id":19408627,"url":"https://github.com/morglod/cpp_traits","last_synced_at":"2025-04-24T10:30:35.027Z","repository":{"id":55614406,"uuid":"523140086","full_name":"Morglod/cpp_traits","owner":"Morglod","description":"rust-like traits (type erasure) on plain C++","archived":false,"fork":false,"pushed_at":"2023-06-30T15:02:12.000Z","size":25,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-01-29T10:32:37.514Z","etag":null,"topics":["cpp","cpp20","rust","rust-like","traits","type-erasure"],"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/Morglod.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":"2022-08-09T23:43:05.000Z","updated_at":"2023-12-14T04:02:36.000Z","dependencies_parsed_at":"2024-01-13T03:30:37.627Z","dependency_job_id":"752c2a4c-342c-473f-8145-9f37024af7eb","html_url":"https://github.com/Morglod/cpp_traits","commit_stats":{"total_commits":10,"total_committers":1,"mean_commits":10.0,"dds":0.0,"last_synced_commit":"2e7164553decd1984bc8c716a39bcf4f435dcf78"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Fcpp_traits","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Fcpp_traits/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Fcpp_traits/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morglod%2Fcpp_traits/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Morglod","download_url":"https://codeload.github.com/Morglod/cpp_traits/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223948575,"owners_count":17230132,"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":["cpp","cpp20","rust","rust-like","traits","type-erasure"],"created_at":"2024-11-10T12:07:01.307Z","updated_at":"2024-11-10T12:07:01.717Z","avatar_url":"https://github.com/Morglod.png","language":"C++","readme":"# Traits\n\nUse rust-like traits without any struct modifications \u0026 templates!\n\n```cpp\nstruct Square {\n    int counter = 0;\n    void add(int x) { counter += x; }\n};\n\n// define trait\nTRAIT_STRUCT(Addable,\n    TRAIT_METHOD(void, add, int)\n)\n\nvoid add_10(Addable x) {\n    x.add(10);\n}\n\nint main() {\n    Square s;\n    add_10(s);\n}\n```\n\n### How it works\n\nBasic traits may be used as type-erased references. To store values, use shared_ptr version.\n\nIn example above, `Addable` type has constructor:\n\n```cpp\ntemplate\u003ctypename T\u003e Addable(T\u0026 t);\n```\n\nWhich saves pointer to T and picks specific trait's implementation for type T.\n\n\"Trait\" structure will hold pointer to initial object \u0026 pointer to implementation  \nstatic cost will be: 1 pointer per method per type\n\n### Performance\n\nhttps://quick-bench.com/q/RRZUoW5AVvuqjyzKvL_O2B5gU0E\n\nMethod call:\n* GCC 11.2 -O3   virtual call is 10% faster\n* Clang 13 -O3   equal to virtual call\n* MSVC 2022      +- same as virtual call\n\n### Build example\n\nRun `example/build.cmd / sh` or use `example/CMakeLists.txt`\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\n## Whats inside macro?\n\n\u003c/summary\u003e\n\nTrait structure under macro:\n\n```cpp\ntemplate \u003ctypename T\u003e\nstruct Addable_impl_T {\n  using Self = Addable_impl_T\u003cT\u003e;\n  void (*add)(void *self, int) = \u0026Self::static_add;\n  static void static_add(void *self, int _1) { return ((T *)self)-\u003eadd(_1); };\n};\n\nstruct Addable_impl {\n  void (*add)(void *self, int);\n};\n\nstruct Addable {\n  void *self = nullptr;\n  Addable() = delete;\n  inline void add(int _1) { return _impl-\u003eadd(_get_self(), _1); }\n\n  template \u003ctypename T\u003e\n  Addable(T \u0026t) : self(\u0026t) {\n    static Addable_impl_T\u003cT\u003e impl;\n    _impl = (Addable_impl *)(void *)\u0026impl;\n  }\n\nprivate:\n  inline void *_get_self() { return self; }\n  Addable_impl *_impl;\n};\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\n## How to own shared_ptr through trait?\n\n\u003c/summary\u003e\n\nStrange question, but why not\n\nBetter check \"how to store shared_ptr in trait\"\n\n[real example](./example/example2_get_ptr.cpp)\n\n```cpp\nstruct MyObject : public std::enable_shared_from_this\u003cMyObject\u003e {\n    inline std::shared_ptr\u003cvoid\u003e get_ptr() {\n        return shared_from_this(); // comes from enable_shared_from_this\n    }\n};\n\nTRAIT_STRUCT(DataHandler,\n    TRAIT_METHOD(std::shared_ptr\u003cvoid\u003e, get_ptr)\n)\n\nvoid take_data(DataHandler dh) {\n    std::shared_ptr\u003cvoid\u003e ptr_to_my_object = dh.get_ptr();\n}\n\nvoid do_stuff() {\n    auto obj = std::make_shared\u003cMyObject\u003e();\n    take_data(*(obj.get()));\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\n## How to store shared_ptr inside trait?\n\n\u003c/summary\u003e\n\n```cpp\n#define TRAITS_SHARED_PTR // \u003c--------------------- add shared_ptr\n#include \u003cmemory\u003e        // \u003c---------------- or just simply include \u003cmemory\u003e before traits\n#include \"traits.hpp\"\n\nstruct Storage {\n    char* _data;\n    void print();\n};\n\nTRAIT_STRUCT(DataHandler,\n    TRAIT_METHOD(void, print)\n)\n\nDataHandler_ptr take_data(DataHandler_ptr dh) { // \u003c------ we use _ptr version here which stores shared_ptr as self\n    return dh;\n}\n\nvoid do_stuff() {\n    DataHandler_ptr data_trait; // \u003c------ also _ptr version could be initialized with `nullptr`\n    {\n        auto obj = std::make_shared\u003cStorage\u003e();\n        obj-\u003e_data = new char[] { \"Hello world!\" };\n\n        // get as return value\n        data_trait = take_data(obj);\n\n        // or just cast\n        data_trait = obj;\n    }\n    data_trait.print();\n}\n```\n\n\u003c/details\u003e\n\n---\n\n## Requirements\n\n* `__VA_OPT__` (currently for C++20 only)\n\n## todo\n\n* Remove `__VA_OPT__`, than C++11 may be supported\n* More benchmarks \u0026 tests\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorglod%2Fcpp_traits","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmorglod%2Fcpp_traits","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorglod%2Fcpp_traits/lists"}