{"id":26368785,"url":"https://github.com/bindreams/poly","last_synced_at":"2025-03-16T22:49:01.672Z","repository":{"id":167685070,"uuid":"100971767","full_name":"bindreams/poly","owner":"bindreams","description":"A library for easier polymorphism in C++.","archived":false,"fork":false,"pushed_at":"2019-07-14T11:58:29.000Z","size":175,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2024-03-30T13:26:17.728Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bindreams.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2017-08-21T16:25:27.000Z","updated_at":"2024-03-30T13:26:20.912Z","dependencies_parsed_at":null,"dependency_job_id":"280a39cb-d87c-498f-a58a-c659fcfb717d","html_url":"https://github.com/bindreams/poly","commit_stats":null,"previous_names":["andreasxp/poly","bindreams/poly"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bindreams%2Fpoly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bindreams%2Fpoly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bindreams%2Fpoly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bindreams%2Fpoly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bindreams","download_url":"https://codeload.github.com/bindreams/poly/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243945479,"owners_count":20372894,"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":[],"created_at":"2025-03-16T22:49:01.257Z","updated_at":"2025-03-16T22:49:01.660Z","avatar_url":"https://github.com/bindreams.png","language":"C++","readme":"# poly\nA library for easier (and faster!) polymorphism in C++.\nAvoid memory leaks, enforce correct polymorphic behavior, clone without CRTP, and quickly switch between Base and Derived classes without using `dynamic_cast`.\n## Installation\nPoly is a single-header library. You can download the latest release [here](https://github.com/andreasxp/poly/releases).\nTo use `poly`, your compiler must be up to ISO C++11 standard.\n\nPoly was verified to work with GCC 4.9.0, clang 3.4, Visual C++ 14 or higher.\n## Usage\nThis section contains only a basic explanation. For a more complete documentation, refer [here](#documentation).\n#### Include header:\n```c++\n#include \"poly.hpp\"\nusing pl::poly;    //For convenience\nusing pl::factory; //For convenience\n```\n**Warning:** Do not use `using namespace pl` and `using namespace std` together in one program. You will get name collisions.\n\n### `poly`\n`poly` is the main class of this library. It's a smart pointer, that understands and supports polymorphic objects.\n\n#### Create a `poly`:\n```c++\npoly\u003cAnimal\u003e p1; //empty\npoly\u003cAnimal\u003e p2(new Dog); //holds a default-constructed derived object\npoly\u003cAnimal\u003e p3 = pl::make\u003cpoly\u003cAnimal\u003e, Dog\u003e(4, \"ears\", true); //Using a constructor function\npoly\u003cMammal\u003e p4 = pl::transform\u003cpoly\u003cMammal\u003e, Dog\u003e(p3); //Transforming from another poly\n```\nWhere `Dog` is derived from `Animal`.\n\n#### Use `poly`:\n```c++\nAnimal* animal_ptr = p1.release(); //Release the stored pointer\np1 = new Dog; //Assign a new pointer\n\nif (p1) { //Check if poly holds an object\n    p1.reset(); //Clear stored value\n}\n```\n\n```c++\nbool is_dog = p2.is\u003cDog\u003e(); //true only if p2 holds exactly a Dog\nif (is_dog)\n    p2.as\u003cDog\u003e()-\u003epet(); //Cast to dog (only works for Dog)\n```\n\n#### Create a `poly`, vol. 2:\nAssume you have the following data structure:\n```c++\nstruct Animal;\n\nstruct Mammal : virtual Animal;\nstruct Fish : virtual Animal;\nstruct Carnivorous : virtual Animal;\n\nstruct Dog : Mammal, Carnivorous;\nstruct Cow : Mammal;\n```\nThis is a fairly complex data structure, but `poly.hpp` provides tools to down- and sidecast anywhere you need.\n```c++\npoly\u003cMammal\u003e poly_mamm1(new Dog); //Construct a mammal\npoly\u003cMammal\u003e poly_mamm2(new Cow); //Construct a different mammal\n\nauto         poly_carn1 = pl::transform\u003cpoly\u003cCarnivorous\u003e, Dog\u003e(poly_mamm1); //Side-cast Mammal to Carnivorous (dogs only!)\nauto         poly_carn2 = pl::transform\u003cpoly\u003cCarnivorous\u003e, Dog\u003e(poly_mamm2); //Compile error (Cow is not a Dog)\nauto         poly_carn3 = pl::transform\u003cpoly\u003cCarnivorous\u003e, Cow\u003e(poly_mamm2); //Compile error (Cow is not Carnivorous)\n\npoly\u003cDog\u003e    poly_doggo = pl::transform\u003cpoly\u003cDog\u003e, Dog\u003e(poly_mammal); //Down-cast mammal to Dog\npoly\u003cFish\u003e   poly_fishy = pl::transform\u003cpoly\u003cDog\u003e, Dog\u003e(poly_mammal); //Compile error (doggo is not a fish)\n```\n\n#### Using policies to customize the behavior of `poly`\n`poly` has an optional second template parameter called CopyDeletePolicy. It defines the behavior of `poly` when a copy-constructor or a destructor is invoked. `poly.hpp` contains 2 pre-built policies:  \n* `pl::deep` (default): When `poly` is copied, internal object is copied as well. `pl::deep` invokes the proper copy-constructor of the derived object. That means you don't need to add the `clone` method to your class. `pl::deep` has a static check for a virtual destructor in base class to prevent memory leaks.\n`pl::deep` adds a memory overhead of one pointer.\n* `pl::unique`: With this policy, `poly` behaves like a `unique_ptr` (a.k.a. is not copy-constructible). `pl::unique` also checks for memory leaks.  \n`pl::unique` adds no memory overhead.\n\n```c++\npoly\u003cAnimal, pl::deep\u003cAnimal\u003e\u003e p1 = \n    pl::make\u003cpoly\u003cAnimal, pl::deep\u003cAnimal\u003e\u003e, Dog\u003e(); //Same as poly\u003cAnimal\u003e p1 = pl::make\u003cpoly\u003cAnimal\u003e, Dog\u003e()\npoly\u003cAnimal, pl::unique\u003cAnimal\u003e\u003e p2 = \n    pl::make\u003cpoly\u003cAnimal, pl::unique\u003cAnimal\u003e\u003e, Dog\u003e();\n\nauto p3 = p1; //Works, internal object is also copied\nauto p4 = p2; //Error: p2 is not copy-constructible\n```\nYou can make your own policies, too. For a type `T`, a valid policy class provides:\n1. A default constructor;\n2. A constructor from `const T* ptr`;\n3. A `clone` method that clones the provided pointer;\n4. A `destroy` method that deletes the pointer.  \n\nA basic policy looks like the following:\n```c++\nclass my_policy {\n    my_policy(); //Default constructor\n    my_policy(const T* ptr); //Construct a policy for operating on the type T\n    \n    T* clone(const T* ptr); //Clones the object held by ptr.\n    void destroy(T* ptr); //Destroys the object held by ptr.\n};\n```\nYou can also group copy- and delete policies with `pl::compound`. See the [documentation](#class-compound) for more details.\n\n*Note: if `poly` is copyable but the derived object is not, on copy a runtime exception will occur.*\n\n### `factory`\n`poly.hpp` also contains class `factory`. `factory` lets you create poly by passing a string, representing the derived class. \n#### Create a `factory`:\n```c++\nfactory\u003cAnimal\u003e animal_farm;\n//Or factory\u003cAnimal, pl::unique\u003cAnimal\u003e\u003e for a non-copyable alternative.\n```\n\n#### Register a class in the factory:\n```c++\nanimal_farm.insert\u003cDog\u003e();\n```\n\n#### Make a dog:\n```c++\nauto doggo = animal_farm.make(\"Dog\"); //Here, auto is poly\u003cAnimal, pl::deep\u003cAnimal\u003e\u003e\n```\n*Note: Different compilers will require a diffrent string to create a class, depending on what typeid(Dog).name() returns. Consider using [some other rtti library](https://github.com/andreasxp/prindex) along with defining POLY_CUSTOM_TYPE_NAME(type) macro before including for a cross-compiler result.*\n\n## Documentation\nAll described classes are in the namespace `pl`.\n### `class poly`\n```c++\ntemplate\u003cclass Base, class CopyDeletePolicy = deep\u003cBase\u003e\u003e\nclass poly;\n```\n\n`poly` is a smart pointer that owns and manages a polymorphic object through a pointer-to-base and disposes of that object when the `poly` goes out of scope.\n\nThe object is disposed of when either of the following happens:\n* the managing `poly` is destroyed\n* the managing `poly` is assigned another pointer via operator= or reset().\nThe object is disposed of using the selected policy's `destroy` function.\n\n`poly` may alternatively own no object, in which case it is called empty.\n\nThe class satisfies the requirements of MoveConstructible, MoveAssignable, and CopyConstructible, CopyAssignable if the selected policy  provides a `clone` method. The default policy - `deep` - does provide this method, but can alternatively be switched to `unique` which does not.\n\n#### Member types\n`base_type` - Base type, from which every polymorphic object is derived. Equivalent to `Base`.\n`policy` - Policy, used to manage internal pointer's behavior. Equivalent to `CopyDeletePolicy`.\n\n#### Member functions\n##### Constructors and assignment operators\n```c++\nconstexpr poly() noexcept;                                          | (1)\nconstexpr poly(std::nullptr_t) noexcept;                            | \npoly\u0026 operator=(std::nullptr_t) noexcept;                           |\n-------------------------------------------------------------------------\npoly(const poly\u0026 other);                                            | (2)\npoly\u0026 operator=(const poly\u0026 other);                                 |        \n-------------------------------------------------------------------------\npoly(poly\u0026\u0026) noexcept;                                              | (3)\npoly\u0026 operator=(poly\u0026\u0026) noexcept;                                   |\t\n-------------------------------------------------------------------------\ntemplate \u003cclass Base2, class CopyDeletePolicy2,                     | (4)\n    class = typename std::enable_if\u003c                                |\n    detail::is_stronger_qualified\u003cBase, Base2\u003e::value\u003e::type\u003e\u003e      |\npoly(const poly\u003cBase2, CopyDeletePolicy2\u003e\u0026 other);                  |\n-------------------------------------------------------------------------\ntemplate \u003cclass Base2, class CopyDeletePolicy2,                     | (5)\n    class = typename std::enable_if\u003c                                |\n    detail::is_stronger_qualified\u003cBase, Base2\u003e::value\u003e::type\u003e\u003e      |\npoly(poly\u003cBase2, CopyDeletePolicy2\u003e\u0026\u0026 other) noexcept;              |\n-------------------------------------------------------------------------\ntemplate \u003cclass Derived\u003e                                            | (6)\nexplicit poly(Derived* obj);                                        |    \ntemplate \u003cclass Derived\u003e                                            |\npoly\u0026 operator=(Derived* obj);                                      |      \n```\n1. Constructs an empty `poly` that owns nothing. Default-constructs the internal policy object.\n2. Copy-constructs `poly` from another `poly`. Internal policy is copied, and the internal pointer is *cloned* using the policy's `clone` method.\n3. Move-constructs `poly` from another `poly`. Internal policy is moved, and the internal pointer simply move as pointer (shallow move).\n4. Copy-constructs `poly` from a different `poly`. This converting constructor is enabled if new base is more strongly qualified than old base. For example, using this contructor, `poly\u003cconst Base\u003e` is implicitly constructible from `poly\u003cBase\u003e`, but not `poly\u003cvolatile Base\u003e`.\n5. Move-constructs `poly` from a different `poly`. Same rules as for (4) apply.\n6. Constructs a poly by adopting a raw pointer to a derived class. Besides pointing to a class, derived from Base, the pointer must also *not* be a polymorhphic pointer to a different object (i.e. `Derived*` that points to `struct Derived2 : Derived`). Attemping to adopt such a pointer will result in a runtime exception.\n##### Destructor\n```c++\n~poly();\n```\nDestructs the managed pointer using selected policy's `destroy()` function.\n##### Observers\n```c++\ntemplate \u003cclass T\u003e constexpr bool is() const noexcept;\n```\nChecks if the stored pointer holds an object is of type `T`. Returns `true` if it does, `false` otherwise.\n```c++\nexplicit constexpr operator bool() const noexcept;\n```\nChecks whether `poly` owns an object, i.e. whether `get() != nullptr`. Returns `true` if it does, `false` otherwise.\n##### Modifiers\n```c++\ntemplate \u003cclass Derived\u003e                         | (1)\nvoid reset(Derived* ptr);                        |\n-------------------------------------------------------\nvoid reset(std::nullptr_t = nullptr) noexcept;   | (2)\n```\n1. Destructs the managed pointer using selected policy's `destroy()` function, and replaces it with `ptr`. For `ptr`, same constraints as in constructor (6) apply.\n2. Destructs the managed pointer using selected policy's `destroy()` function, and replaces it with `nullptr`.\n```c++\nBase* release() noexcept;\n```\nReleases the ownership of the managed object if any. `get()` returns `nullptr` after the call. Returns pointer to the managed object as `Base*` or `nullptr` if there was no managed object, i.e. the value which would be returned by `get()` before the call.\n##### Member access\n```c++\nBase\u0026 operator*() const;             | (1)\n-------------------------------------------\nBase* operator-\u003e() const noexcept;   | (2)\nBase* get() const noexcept;          |\n```\n1. Returns the object owned by `poly`, equivalent to `*get()`. The object must not be empty, otherwise this operation results in undefined behavior.\n2. Returns the managed pointer, or `nullptr` if no object is owned.\n```c++\ntemplate \u003cclass T\u003e\nT* as() const noexcept;\n```\nReturns a pointer to the object owned by `poly` in the exact type of that object. Returns `nullptr` if the stored object is not of type `T` or if `poly` is empty.\n\n*Note: This function is not the same as dynamic_cast. First, types must match exactly, i.e. no up- or side-casting is allowed. Second, perfomance of this function is much better than of dynamic_cast, as no type tree traversal is performed.*\n### `class factory`\n```c++\ntemplate \u003cclass Base, class CopyDeletePolicy = deep\u003cBase\u003e\u003e\nclass factory;\n```\n`factory` is a class that registers and creates `poly` objects using strings as identifiers.  \nBy default, when a type is registered, a string identifier is generated using `std::type_info::name`, but this behavior can be overridden by defining a macro `POLY_CUSTOM_TYPE_NAME(type)` that accepts a type token and retuns a value of type `const cher*` or `std::string`. This macro must be defined before including `poly.hpp`.\nExample of a custom name generator using [prindex](https://github.com/andreasxp/prindex) library:\n```c++\n#include \"prindex.hpp\"\n// Using __VA_ARGS__ is recommended to elide the commas that can appear in templated types.\n#define POLY_CUSTOM_TYPE_NAME(...) prid\u003c__VA_ARGS__\u003e().name()\n#include \"poly.hpp\"\n```\nUsage example:\n```c++\npl::factory\u003cBase\u003e f;\nf.insert\u003cMid1\u003e();\nf.insert\u003cMid2\u003e();\nf.insert\u003cDer\u003e();\n\nauto p = f.make(\"struct Der\"); //MSVC\n//auto p = f.make(\"Der\"); //GCC or Clang (or prindex)\n\nauto ls = f.list();\nfor (auto\u0026 i : ls) std::cout \u003c\u003c i \u003c\u003c std::endl;\n```\nPossible output:\n```\nstruct Der\nstruct Mid1\nstruct Mid2\n```\n#### Member functions\n```c++\ntemplate \u003cclass Derived\u003e void insert();\n```\nInserts the type `Derived` into the factory. This function must be called at least once before `poly\u003cDerived\u003e` can be made with this instance of the factory.\n```c++\nstd::vector\u003cstd::string\u003e list() const;\n```\nReturns a list of all types, registered in the factory, as a `std::vector` of `std::string`s that are used to `make` objects.\n```c++\npoly\u003cBase, CopyDeletePolicy\u003e make(const std::string\u0026 name) const;\n```\nMakes a `poly\u003cBase, CopyDeletePolicy\u003e`, holding a type, represented by the string `name`. The required type must be registered using `insert` before a `poly` of that type can be made. If no such type is registered, a runtime exception is thrown.\n### `class compound`\n```c++\ntemplate \u003cclass Cloner, class Deleter\u003e\nclass compound;\n```\n`compound` is a helper class that can be used to build policies for `poly`. When instantiated with a cloner and a deleter, `compound` becomes a valid CopyDeletePolicy.\nBoth `Cloner` and `Deleter` are classes that\n1. Provide either or both:\n    * a constructor from `const T*`, where `T` is the type on which the class operates;\n    * a default constructor.  \n    \n    If both constructors are provided, the class will be instantiated with `const T*`.\n2. An `operator()`:  \n    * For `Cloner`, `operator()` accepts `const T*` and returns a pointer to a copied object;  \n    * For `Deleter`, `operator()` accepts `T*`, destroys the object, and returns nothing.\n\nAll pre-defined policies were made using `compound`.\n### `unique`\n```c++\ntemplate \u003cclass Base\u003e\nusing unique = compound\u003cno_copy,\n\ttypename std::conditional\u003cstd::has_virtual_destructor\u003cBase\u003e::value,\n\tstd::default_delete\u003cBase\u003e,\n\tpmr_delete\u003cBase\u003e\u003e::type\u003e;\n```\n`unique` is a policy for `poly` that disallows copying. When instantiated with this policy, `poly` is not CopyConstructible nor CopyAssignable.\nIf the base class does not have a virtual destructor, `unique` will raise a compiler error on `operator()` call to prevent memory leaks.\n### `deep`\n```c++\ntemplate \u003cclass Base\u003e\nusing deep = compound\u003cdeep_copy\u003cBase\u003e,\n\ttypename std::conditional\u003cstd::has_virtual_destructor\u003cBase\u003e::value,\n\tstd::default_delete\u003cBase\u003e,\n\tpmr_delete\u003cBase\u003e\u003e::type\u003e;\n```\n`deep` is a policy for `poly` that allows copying. When instantiated with this policy, `poly` is CopyConstructible and CopyAssignable.\nIf the base class does not have a virtual destructor, `deep` will raise a compiler error on `operator()` call to prevent memory leaks.\n## License\nThis project is licenced under the MIT licence. It is free for personal and commercial use.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbindreams%2Fpoly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbindreams%2Fpoly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbindreams%2Fpoly/lists"}