{"id":28717411,"url":"https://github.com/wookeybiscotti/poly_value","last_synced_at":"2025-10-06T11:19:00.817Z","repository":{"id":297407432,"uuid":"996627591","full_name":"WookeyBiscotti/poly_value","owner":"WookeyBiscotti","description":"C++ library for storing polymorphic objects on the stack","archived":false,"fork":false,"pushed_at":"2025-06-12T20:34:36.000Z","size":367,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-12T21:28:20.764Z","etag":null,"topics":["cpp20","optimization","polymorphic","polymorphism","stack","variant","virtual"],"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/WookeyBiscotti.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,"zenodo":null}},"created_at":"2025-06-05T08:15:43.000Z","updated_at":"2025-06-12T20:34:39.000Z","dependencies_parsed_at":"2025-06-05T10:39:42.023Z","dependency_job_id":null,"html_url":"https://github.com/WookeyBiscotti/poly_value","commit_stats":null,"previous_names":["wookeybiscotti/poly_value"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/WookeyBiscotti/poly_value","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WookeyBiscotti%2Fpoly_value","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WookeyBiscotti%2Fpoly_value/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WookeyBiscotti%2Fpoly_value/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WookeyBiscotti%2Fpoly_value/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WookeyBiscotti","download_url":"https://codeload.github.com/WookeyBiscotti/poly_value/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WookeyBiscotti%2Fpoly_value/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278599586,"owners_count":26013510,"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-10-06T02:00:05.630Z","response_time":65,"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":["cpp20","optimization","polymorphic","polymorphism","stack","variant","virtual"],"created_at":"2025-06-15T03:15:38.529Z","updated_at":"2025-10-06T11:19:00.768Z","avatar_url":"https://github.com/WookeyBiscotti.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# poly_value \u003c!-- omit from toc --\u003e\n\n![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/WookeyBiscotti/poly_value/cmake-ubuntu.yml?logo=github\u0026label=Ubuntu) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/WookeyBiscotti/poly_value/cmake-macos.yml?logo=github\u0026label=MacOS) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/WookeyBiscotti/poly_value/cmake-windows.yml?logo=github\u0026label=Windows)\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n\nPolymorphic C++ Classes on the Stack\n\n- [Quick start](#quick-start)\n- [Problem](#problem)\n- [A solution option](#a-solution-option)\n  - [std::variant](#stdvariant)\n    - [Pros](#pros)\n    - [Cons](#cons)\n- [Solution](#solution)\n- [API](#api)\n  - [Definition](#definition)\n  - [Copy/Move](#copymove)\n\n## Quick start\n\n```c++\nstruct A {\n    virtual ~A() = default;\n    virtual void print() = 0;\n};\n\nstruct B: A {\n    void print() override {\n        std::cout \u003c\u003c \"B::print \" \u003c\u003c value \u003c\u003c std::endl;\n    }\n    int value = 42;\n};\n\nstruct C: A {\n    void print() override {\n        std::cout \u003c\u003c \"C::print \" \u003c\u003c value \u003c\u003c std::endl;\n    }\n\n    float value = 3.1415f;\n};\n\nusing poly_value = pv::poly_value\u003cA, 16\u003e;\n\npoly_value pv1 = B{};\npoly_value pv2 = C{};\n\npv1-\u003eprint(); // `B::print 42`\npv2-\u003eprint(); // `C::print 3.1415`\n\npv1 = pv2;\npv1-\u003eprint(); // `C::print 3.1415`\n\npv2 = B{};\npv1 = std::move(pv2); // pv2 value was moved\npv1-\u003eprint();         // `B::print 42`\n\nstd::cout \u003c\u003c pv2.has_value() \u003c\u003c std::endl; // 0\n```\n\n## Problem\n\nYou have small polymorphic classes and you want to store them in a container, but since they can be of different lengths, you are forced to store them as pointers. This leads to greater data indirection and, as a consequence, worse performance due to cache misses.\n\n```c++\nclass A {...};\nclass B: public A {...};\nclass C: public A {...};\nclass D: public A {...};\n\nstd::array\u003cpv::poly_value\u003cA, 16\u003e, 4\u003e values;\n// No heap memory allocation\nvalues[0].emplace\u003cA\u003e(...);\nvalues[1].emplace\u003cB\u003e(...);\nvalues[2].emplace\u003cC\u003e(...);\nvalues[3].emplace\u003cD\u003e(...);\n\nstd::array\u003cstd::unique_ptr\u003cA\u003e\u003e values;\n// Heap memory allocations\nvalues[0] = std::make_unique\u003cA\u003e(...);\nvalues[1] = std::make_unique\u003cB\u003e(...);\nvalues[2] = std::make_unique\u003cC\u003e(...);\nvalues[3] = std::make_unique\u003cD\u003e(...);\n\n```\n\n![Memory layout](docs/memory_layout.png)\n\n## A solution option\n\n### std::variant\n\n#### Pros\n\n- A variant object can store different classes\n- No heap allocation\n\n#### Cons\n\n- To access a class method, you need to write a visitor for each class.\n- In order for a variant to store a class, it must be declared as a parameter of the variant template\n\n## Solution\n\nCreate a suitable storage for objects inherited from the base class, knowing the base class, you can call its methods from the created object.\n\n## API\n\n### Definition\n\nTo create a `poly_value` you must know:\n\n- base class\n- estimated storage size for the base class descendants\n\n```c++\nclass Base {...};\n\nusing poly_value = pv::poly_value\u003cBase, 32\u003e;\n\nclass Derived: public Base {...};\n\npoly_value value;\nvalue = Derived();\n```\n\nAt the time of defining a poly_value you do not need to know about the descendants of the base class.\n\n### Copy/Move\n\nSince values ​​are stored on the stack, we may want to assign one value to another.\n\n```c++\nclass Base {...};\n\nclass Derived: public Base {...};\n\nusing poly_value = pv::poly_value\u003cBase, 32\u003e;\n\npoly_value v1(Base{});\npoly_value v2(Derived{});\n\nv1 = v2; // At this point, v1 will call the Base destructor, then the Derived copy constructor.\n\nv2 = std::move(v1); // At this point, v2 will call the Derived destructor, then the Derived move constructor.\n```\n\nFor all this to work, it is necessary for classes to implement:\n\n- a copy constructor\n\nor\n\n- copy assignment operator and a default constructor\n\nIn the case of move semantics, it is necessary for classes to implement:\n\n- a move constructor\n\nor\n\n- move assignment operator and a default constructor.\n\nYou can control the presence of a copy or move operator using additional flags.\n\n```c++\n    pv::poly_value\u003cBase, 16, pv::flags::Copyable\u003e value;\n    pv::poly_value\u003cBase, 16, pv::flags::Moveable\u003e value;\n    pv::poly_value\u003cBase, 16, pv::flags::Copyable | pv::flags::Moveable\u003e value;\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwookeybiscotti%2Fpoly_value","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwookeybiscotti%2Fpoly_value","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwookeybiscotti%2Fpoly_value/lists"}