{"id":15047422,"url":"https://github.com/ldionne/dyno","last_synced_at":"2025-04-13T04:59:08.232Z","repository":{"id":41113836,"uuid":"76579661","full_name":"ldionne/dyno","owner":"ldionne","description":"Runtime polymorphism done right","archived":false,"fork":false,"pushed_at":"2021-05-27T20:03:40.000Z","size":463,"stargazers_count":998,"open_issues_count":27,"forks_count":44,"subscribers_count":56,"default_branch":"master","last_synced_at":"2025-04-13T04:59:03.909Z","etag":null,"topics":["cpp","cpp11","cpp14","cpp17","hana","polymorphism","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":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ldionne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-12-15T17:07:36.000Z","updated_at":"2025-04-08T13:04:31.000Z","dependencies_parsed_at":"2022-07-13T10:50:30.570Z","dependency_job_id":null,"html_url":"https://github.com/ldionne/dyno","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldionne%2Fdyno","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldionne%2Fdyno/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldionne%2Fdyno/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldionne%2Fdyno/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ldionne","download_url":"https://codeload.github.com/ldionne/dyno/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665759,"owners_count":21142123,"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","cpp11","cpp14","cpp17","hana","polymorphism","type-erasure"],"created_at":"2024-09-24T20:58:06.728Z","updated_at":"2025-04-13T04:59:08.201Z","avatar_url":"https://github.com/ldionne.png","language":"C++","funding_links":[],"categories":["Miscellaneous"],"sub_categories":[],"readme":"# Dyno: Runtime polymorphism done right\n\u003ca target=\"_blank\" href=\"https://travis-ci.org/ldionne/dyno\"\u003e![Travis status][badge.Travis]\u003c/a\u003e\n\n## DISCLAIMER\nAt this point, this library is experimental and it is a pure curiosity.\nNo stability of interface or quality of implementation is guaranteed.\nUse at your own risks.\n\n## Overview\n__Dyno__ solves the problem of runtime polymorphism better than vanilla C++\ndoes. It provides a way to define interfaces that can be fulfilled\nnon-intrusively, and it provides a fully customizable way of storing\npolymorphic objects and dispatching to virtual methods. It does not\nrequire inheritance, heap allocation or leaving the comfortable world\nof value semantics, and it can do so while outperforming vanilla C++.\n\n__Dyno__ is pure-library implementation of what's also known as [Rust trait\nobjects][], [Go interfaces][], [Haskell type classes][], and [virtual concepts][].\nUnder the hood, it uses a C++ technique known as [type erasure][], which is\nthe idea behind [`std::any`][], [`std::function`][] and many other useful types.\n\n\u003c!-- Important: keep this in sync with example/overview.cpp --\u003e\n```c++\n#include \u003cdyno.hpp\u003e\n#include \u003ciostream\u003e\nusing namespace dyno::literals;\n\n// Define the interface of something that can be drawn\nstruct Drawable : decltype(dyno::requires_(\n  \"draw\"_s = dyno::method\u003cvoid (std::ostream\u0026) const\u003e\n)) { };\n\n// Define how concrete types can fulfill that interface\ntemplate \u003ctypename T\u003e\nauto const dyno::default_concept_map\u003cDrawable, T\u003e = dyno::make_concept_map(\n  \"draw\"_s = [](T const\u0026 self, std::ostream\u0026 out) { self.draw(out); }\n);\n\n// Define an object that can hold anything that can be drawn.\nstruct drawable {\n  template \u003ctypename T\u003e\n  drawable(T x) : poly_{x} { }\n\n  void draw(std::ostream\u0026 out) const\n  { poly_.virtual_(\"draw\"_s)(out); }\n\nprivate:\n  dyno::poly\u003cDrawable\u003e poly_;\n};\n\nstruct Square {\n  void draw(std::ostream\u0026 out) const { out \u003c\u003c \"Square\"; }\n};\n\nstruct Circle {\n  void draw(std::ostream\u0026 out) const { out \u003c\u003c \"Circle\"; }\n};\n\nvoid f(drawable const\u0026 d) {\n  d.draw(std::cout);\n}\n\nint main() {\n  f(Square{}); // prints Square\n  f(Circle{}); // prints Circle\n}\n```\n\nAlternatively, if you find this to be too much boilerplate and you can stand\nusing a macro, the following is equivalent:\n\n\u003c!-- Important: keep this in sync with example/overview.macro.cpp --\u003e\n```c++\n#include \u003cdyno.hpp\u003e\n#include \u003ciostream\u003e\n\n// Define the interface of something that can be drawn\nDYNO_INTERFACE(Drawable,\n  (draw, void (std::ostream\u0026) const)\n);\n\nstruct Square {\n  void draw(std::ostream\u0026 out) const { out \u003c\u003c \"Square\"; }\n};\n\nstruct Circle {\n  void draw(std::ostream\u0026 out) const { out \u003c\u003c \"Circle\"; }\n};\n\nvoid f(Drawable const\u0026 d) {\n  d.draw(std::cout);\n}\n\nint main() {\n  f(Square{}); // prints Square\n  f(Circle{}); // prints Circle\n}\n```\n\n\n## Compiler requirements\nThis is a C++17 library. No efforts will be made to support older compilers\n(sorry). The library is known to work with the following compilers:\n\n| Compiler    | Version  |\n| ----------- |:--------:|\n| GCC         | \u003e= 7     |\n| Clang       | \u003e= 4.0   |\n| Apple Clang | \u003e= 9.1   |\n\n\n## Dependencies\nThe library depends on [Boost.Hana][] and [Boost.CallableTraits][]. The unit\ntests depend on [libawful][] and the benchmarks depend on [Google Benchmark][],\n[Boost.TypeErasure][] and [Mpark.Variant][], but you don't need them to use\nthe library. For local development, the `dependencies/install.sh` script can\nbe used to install all the dependencies automatically.\n\n\n## Building the library\n__Dyno__ is a header-only library, so there's nothing to build per-se. Just\nadd the `include/` directory to your compiler's header search path (and make\nsure the dependencies are satisfied), and you're good to go. However, there\nare unit tests, examples and benchmarks that can be built:\n\n```sh\n(cd dependencies \u0026\u0026 ./install.sh) # Install dependencies; will print a path to add to CMAKE_PREFIX_PATH\nmkdir build\n(cd build \u0026\u0026 cmake .. -DCMAKE_PREFIX_PATH=\"${PWD}/../dependencies/install\") # Setup the build directory\n\ncmake --build build --target examples   # Build and run the examples\ncmake --build build --target tests      # Build and run the unit tests\ncmake --build build --target check      # Does both examples and tests\ncmake --build build --target benchmarks # Build and run the benchmarks\n```\n\n\n## Introduction\nIn programming, the need for manipulating objects with a common interface but\nwith a different dynamic type arises very frequently. C++ solves this with\ninheritance:\n\n```c++\nstruct Drawable {\n  virtual void draw(std::ostream\u0026 out) const = 0;\n};\n\nstruct Square : Drawable {\n  virtual void draw(std::ostream\u0026 out) const override final { ... }\n};\n\nstruct Circle : Drawable {\n  virtual void draw(std::ostream\u0026 out) const override final { ... }\n};\n\nvoid f(Drawable const* drawable) {\n  drawable-\u003edraw(std::cout);\n}\n```\n\nHowever, this approach has several drawbacks. It is\n\n1. __Intrusive__\u003cbr\u003e\n   In order for `Square` and `Circle` to fulfill the `Drawable` interface, they\n   both need to inherit from the `Drawable` base class. This requires having\n   the license to modify those classes, which makes inheritance very inextensible.\n   For example, how would you make a `std::vector\u003cint\u003e` fulfill the `Drawable`\n   interface? You simply can't.\n\n2. __Incompatible with value semantics__\u003cbr\u003e\n   Inheritance requires you to pass polymorphic pointers or references to objects\n   instead of the objects themselves, which plays very badly with the rest of\n   the language and the standard library. For example, how would you copy a\n   vector of `Drawable`s? You'd need to provide a virtual `clone()` method, but\n   now you've just messed up your interface.\n\n3. __Tightly coupled with dynamic storage__\u003cbr\u003e\n   Because of the lack of value semantics, we usually end up allocating these\n   polymorphic objects on the heap. This is both horribly inefficient and\n   semantically wrong, since chances are we did not need the dynamic storage\n   duration at all, and an object with automatic storage duration (e.g. on\n   the stack) would have been enough.\n\n4. __Prevents inlining__\u003cbr\u003e\n   95% of the time, we end up calling a virtual method through a polymorphic\n   pointer or reference. That requires three indirections: one for loading the\n   pointer to the vtable inside the object, one for loading the right entry in\n   the vtable, and one for the indirect call to the function pointer. All this\n   jumping around makes it difficult for the compiler to make good inlining\n   decisions. However, it turns out that all of these indirections except the\n   indirect call can be avoided.\n\nUnfortunately, this is the choice that C++ has made for us, and these are the\nrules that we are bound to when we need dynamic polymorphism. Or is it really?\n\n### So, what is this library?\n__Dyno__ solves the problem of runtime polymorphism in C++ without any of the\ndrawbacks listed above, and many more goodies. It is:\n\n1. __Non-intrusive__\u003cbr\u003e\n   An interface can be fulfilled by a type without requiring any modification\n   to that type. Heck, a type can even fulfill the same interface in different\n   ways! With __Dyno__, you can kiss ridiculous class hierarchies goodbye.\n\n2. __100% based on value semantics__\u003cbr\u003e\n   Polymorphic objects can be passed as-is, with their natural value semantics.\n   You need to copy your polymorphic objects? Sure, just make sure they have\n   a copy constructor. You want to make sure they don't get copied? Sure, mark\n   it as deleted. With __Dyno__, silly `clone()` methods and the proliferation\n   of pointers in APIs are things of the past.\n\n3. __Not coupled with any specific storage strategy__\u003cbr\u003e\n   The way a polymorphic object is stored is really an implementation detail,\n   and it should not interfere with the way you use that object. __Dyno__ gives\n   you complete control over the way your objects are stored. You have a lot of\n   small polymorphic objects? Sure, let's store them in a local buffer and\n   avoid any allocation. Or maybe it makes sense for you to store things on\n   the heap? Sure, go ahead.\n\n4. __Flexible dispatch mechanism to achieve best possible performance__\u003cbr\u003e\n   Storing a pointer to a vtable is just one of many different implementation\n   strategies for performing dynamic dispatch. __Dyno__ gives you complete\n   control over how dynamic dispatch happens, and can in fact beat vtables\n   in some cases. If you have a function that's called in a hot loop, you can\n   for example store it directly in the object and skip the vtable indirection.\n   You can also use application-specific knowledge the compiler could never\n   have to optimize some dynamic calls \u0026mdash; library-level devirtualization.\n\n\n## Using the library\nFirst, you start by defining a generic interface and giving it a name.\n__Dyno__ provides a simple domain specific language to do that. For example,\nlet's define an interface `Drawable` that describes types that can be drawn:\n\n```c++\n#include \u003cdyno.hpp\u003e\nusing namespace dyno::literals;\n\nstruct Drawable : decltype(dyno::requires_(\n  \"draw\"_s = dyno::method\u003cvoid (std::ostream\u0026) const\u003e\n)) { };\n```\n\nThis defines `Drawable` as representing an interface for anything that has a\nmethod called `draw` taking a reference to a `std::ostream`. __Dyno__ calls\nthese interfaces _dynamic concepts_, since they describe sets of requirements\nto be fulfilled by a type (like C++ concepts). However, unlike C++ concepts,\nthese _dynamic concepts_ are used to generate runtime interfaces, hence the\nname _dynamic_. The above definition is basically equivalent to the following:\n\n```c++\nstruct Drawable {\n  virtual void draw(std::ostream\u0026) const = 0;\n};\n```\n\nOnce the interface is defined, the next step is to actually create a type that\nsatisfies this interface. With inheritance, you would write something like this:\n\n```c++\nstruct Square : Drawable {\n  virtual void draw(std::ostream\u0026 out) const override final {\n    out \u003c\u003c \"square\" \u003c\u003c std::endl;\n  }\n};\n```\n\nWith __Dyno__, the polymorphism is non-intrusive and it is instead provided\nvia what is called a _concept map_ (after [C++0x Concept Maps][]):\n\n```c++\nstruct Square { /* ... */ };\n\ntemplate \u003c\u003e\nauto const dyno::concept_map\u003cDrawable, Square\u003e = dyno::make_concept_map(\n  \"draw\"_s = [](Square const\u0026 square, std::ostream\u0026 out) {\n    out \u003c\u003c \"square\" \u003c\u003c std::endl;\n  }\n);\n```\n\n\u003e This construct is the specialization of a C++14 variable template named\n\u003e `concept_map` defined in the `dyno::` namespace. We then initialize that\n\u003e specialization with `dyno::make_concept_map(...)`.\n\nThe first parameter of the lambda is the implicit `*this` parameter that is\nimplied when we declared `draw` as a method above. It's also possible to\nerase non-member functions (see [the relevant section](#erasing-non-member-functions)).\n\nThis _concept map_ defines how the type `Square` satisfies the `Drawable`\nconcept. In a sense, it _maps_ the type `Square` to its implementation of\nthe concept, which motivates the appellation. When a type satisfies the\nrequirements of a concept, we say that the type _models_ (or is a model of)\nthat concept. Now that `Square` is a model of the `Drawable` concept, we'd\nlike to use a `Square` polymorphically as a `Drawable`. With traditional\ninheritance, we would use a pointer to a base class like this:\n\n```c++\nvoid f(Drawable const* d) {\n  d-\u003edraw(std::cout);\n}\n\nf(new Square{});\n```\n\nWith __Dyno__, polymorphism and value semantics are compatible, and the way\npolymorphic types are passed around can be highly customized. To do this,\nwe'll need to define a type that can hold anything that's `Drawable`. It is\nthat type, instead of a `Drawable*`, that we'll be passing around to and from\npolymorphic functions. To help define this wrapper, __Dyno__ provides the\n`dyno::poly` container, which can hold an arbitrary object satisfying a given\nconcept. As you will see, `dyno::poly` has a dual role: it stores the polymorphic\nobject and takes care of the dynamic dispatching of methods. All you need to do\nis write a thin wrapper over `dyno::poly` to give it exactly the desired interface:\n\n```c++\nstruct drawable {\n  template \u003ctypename T\u003e\n  drawable(T x) : poly_{x} { }\n\n  void draw(std::ostream\u0026 out) const\n  { poly_.virtual_(\"draw\"_s)(out); }\n\nprivate:\n  dyno::poly\u003cDrawable\u003e poly_;\n};\n```\n\n\u003e Note: You could technically use `dyno::poly` directly in your interfaces.\n\u003e However, it is much more convenient to use a wrapper with real methods\n\u003e than `dyno::poly`, and so writing a wrapper is recommended.\n\nLet's break this down. First, we define a member `poly_` that is a polymorphic\ncontainer for anything that models the `Drawable` concept:\n\n```c++\ndyno::poly\u003cDrawable\u003e poly_;\n```\n\nThen, we define a constructor that allows constructing this container from an\narbitrary type `T`:\n\n```c++\ntemplate \u003ctypename T\u003e\ndrawable(T x) : poly_{x} { }\n```\n\nThe unsaid assumption here is that `T` actually models the `Drawable` concept.\nIndeed, when you create a `dyno::poly` from an object of type `T`, __Dyno__\nwill go and look at the concept map defined for `Drawable` and `T`, if any. If\nthere's no such concept map, the library will report that we're trying to create\na `dyno::poly` from a type that does not support it, and your program won't compile.\n\nFinally, the strangest and most important part of the definition above is that\nof the `draw` method:\n\n```c++\nvoid draw(std::ostream\u0026 out) const\n{ poly_.virtual_(\"draw\"_s)(out); }\n```\n\nWhat happens here is that when `.draw` is called on our `drawable` object,\nwe'll actually perform a dynamic dispatch to the implementation of the `\"draw\"`\nfunction for the object currently stored in the `dyno::poly`, and call that.\nNow, to create a function that accepts anything that's `Drawable`, no need\nto worry about pointers and ownership in your interface anymore:\n\n```c++\nvoid f(drawable d) {\n  d.draw(std::cout);\n}\n\nf(Square{});\n```\n\nBy the way, if you're thinking that this is all stupid and you should have been\nusing a template, you're right. However, consider the following, where you really\ndo need _runtime_ polymorphism:\n\n```c++\ndrawable get_drawable() {\n  if (some_user_input())\n    return Square{};\n  else\n    return Circle{};\n}\n\nf(get_drawable());\n```\n\nStrictly speaking, you don't need to wrap `dyno::poly`, but doing so puts a nice\nbarrier between __Dyno__ and the rest of your code, which never has to worry\nabout how your polymorphic layer is implemented. Also, we largely ignored how\n`dyno::poly` was implemented in the above definition. However, `dyno::poly` is\na very powerful policy-based container for polymorphic objects that can be\ncustomized to one's needs for performance. Creating a `drawable` wrapper makes\nit easy to tweak the implementation strategy used by `dyno::poly` for performance\nwithout impacting the rest of your code.\n\n\n### Customizing the polymorphic storage\nThe first aspect that can be customized in a `dyno::poly` is the way the object\nis stored inside the container. By default, we simply store a pointer to the\nactual object, like one would do with inheritance-based polymorphism. However,\nthis is often not the most efficient implementation, and that's why `dyno::poly`\nallows customizing it. To do so, simply pass a storage policy to `dyno::poly`.\nFor example, let's define our `drawable` wrapper so that it tries to store\nobjects up to `16` bytes in a local buffer, but then falls back to the heap\nif the object is larger:\n\n```c++\nstruct drawable {\n  template \u003ctypename T\u003e\n  drawable(T x) : poly_{x} { }\n\n  void draw(std::ostream\u0026 out) const\n  { poly_.virtual_(\"draw\"_s)(out); }\n\nprivate:\n  dyno::poly\u003cDrawable, dyno::sbo_storage\u003c16\u003e\u003e poly_;\n  //                   ^^^^^^^^^^^^^^^^^^^^^ storage policy\n};\n```\n\nNotice that nothing except the policy changed in our definition. That is one\nvery important tenet of __Dyno__; these policies are implementation\ndetails, and they should not change the way you write your code. With the\nabove definition, you can now create `drawable`s just like you did before,\nand no allocation will happen when the object you're creating the `drawable`\nfrom fits in `16` bytes. When it does not fit, however, `dyno::poly` will allocate\na large enough buffer on the heap.\n\nLet's say you actually never want to do an allocation. No problem, just change\nthe policy to `dyno::local_storage\u003c16\u003e`. If you try to construct a `drawable`\nfrom an object that's too large to fit in the local storage, your program\nwon't compile. Not only are we saving an allocation, but we're also saving a\npointer indirection every time we access the polymorphic object if we compare\nto the traditional inheritance-based approach. By tweaking these (important)\nimplementation details for you specific use case, you can make your program\nmuch more efficient than with classic inheritance.\n\nOther storage policies are also provided, like `dyno::remote_storage` and\n`dyno::non_owning_storage`. `dyno::remote_storage` is the default one, which\nalways stores a pointer to a heap-allocated object. `dyno::non_owning_storage`\nstores a pointer to an object that already exists, without worrying about\nthe lifetime of that object. It allows implementing non-owning polymorphic\nviews over objects, which is very useful.\n\nCustom storage policies can also be created quite easily. See `\u003cdyno/storage.hpp\u003e`\nfor details.\n\n\n### Customizing the dynamic dispatch\nWhen we introduced `dyno::poly`, we mentioned that it had two roles; the first\nis to store the polymorphic object, and the second one is to perform dynamic\ndispatch. Just like the storage can be customized, the way dynamic dispatching\nis performed can also be customized using policies. For example, let's define\nour `drawable` wrapper so that instead of storing a pointer to the vtable, it\ninstead stores the vtable in the `drawable` object itself. This way, we'll\navoid one indirection each time we access a virtual function:\n\n```c++\nstruct drawable {\n  template \u003ctypename T\u003e\n  drawable(T x) : poly_{x} { }\n\n  void draw(std::ostream\u0026 out) const\n  { poly_.virtual_(\"draw\"_s)(out); }\n\nprivate:\n  using Storage = dyno::sbo_storage\u003c16\u003e;                      // storage policy\n  using VTable = dyno::vtable\u003cdyno::local\u003cdyno::everything\u003e\u003e; // vtable policy\n  dyno::poly\u003cDrawable, Storage, VTable\u003e poly_;\n};\n```\n\nNotice that nothing besides the vtable policy needs to change in the definition\nof our `drawable` type. Furthermore, if we wanted, we could change the storage\npolicy independently from the vtable policy. With the above, even though we are\nsaving all indirections, we are paying for it by making our `drawable` object\nlarger (since it needs to hold the vtable locally). This could be prohibitive\nif we had many functions in the vtable. Instead, it would make more sense to\nstore most of the vtable remotely, but only inline those few functions that we\ncall heavily. __Dyno__ makes it very easy to do so by using __Selectors__, which\ncan be used to customize what functions a policy applies to:\n\n```c++\nstruct drawable {\n  template \u003ctypename T\u003e\n  drawable(T x) : poly_{x} { }\n\n  void draw(std::ostream\u0026 out) const\n  { poly_.virtual_(\"draw\"_s)(out); }\n\nprivate:\n  using Storage = dyno::sbo_storage\u003c16\u003e;\n  using VTable = dyno::vtable\u003c\n    dyno::local\u003cdyno::only\u003cdecltype(\"draw\"_s)\u003e\u003e,\n    dyno::remote\u003cdyno::everything_else\u003e\n  \u003e;\n  dyno::poly\u003cDrawable, Storage, VTable\u003e poly_;\n};\n```\n\nGiven this definition, the vtable is actually split in two. The first part is\nlocal to the `drawable` object and contains only the `draw` method. The second\npart is a pointer to a vtable in static storage that holds the remaining methods\n(the destructor, for example).\n\n__Dyno__ provides two vtable policies, `dyno::local\u003c\u003e` and `dyno::remote\u003c\u003e`.\nBoth of these policies must be customized using a __Selector__. The selectors\nsupported by the library are `dyno::only\u003cfunctions...\u003e`, `dyno::except\u003c...\u003e`,\nand `dyno::everything_else` (which can also be spelled `dyno::everything`).\n\n\n### Defaulted concept maps\nWhen defining a concept, it is often the case that one can provide a default\ndefinition for at least some functions associated to the concept. For example,\nby default, it would probably make sense to use a member function named `draw`\n(if any) to implement the abstract `\"draw\"` method of the `Drawable` concept.\nFor this, one can use `dyno::default_concept_map`:\n\n```c++\ntemplate \u003ctypename T\u003e\nauto const dyno::default_concept_map\u003cDrawable, T\u003e = dyno::make_concept_map(\n  \"draw\"_s = [](auto const\u0026 self, std::ostream\u0026 out) { self.draw(out); }\n);\n```\n\nNow, whenever we try to look at how some type `T` fulfills the `Drawable`\nconcept, we'll fall back to the default concept map if no concept map was\ndefined. For example, we can create a new type `Circle`:\n\n```c++\nstruct Circle {\n  void draw(std::ostream\u0026 out) const {\n    out \u003c\u003c \"circle\" \u003c\u003c std::endl;\n  }\n};\n\nf(Circle{}); // prints \"circle\"\n```\n\n`Circle` is automatically a model of `Drawable`, even though we did not\nexplicitly define a concept map for `Circle`. On the other hand, if we\nwere to define such a concept map, it would have precedence over the\ndefault one:\n\n```c++\ntemplate \u003c\u003e\nauto dyno::concept_map\u003cDrawable, Circle\u003e = dyno::make_concept_map(\n  \"draw\"_s = [](Circle const\u0026 circle, std::ostream\u0026 out) {\n    out \u003c\u003c \"triangle\" \u003c\u003c std::endl;\n  }\n);\n\nf(Circle{}); // prints \"triangle\"\n```\n\n\n### Parametric concept maps\nIt is sometimes useful to define a concept map for a complete family of types\nall at once. For example, we might want to make `std::vector\u003cT\u003e` a model of\n`Drawable`, but only when `T` can be printed to a stream. This is easily\nachieved by using this (not so) secret trick:\n\n```c++\ntemplate \u003ctypename T\u003e\nauto const dyno::concept_map\u003cDrawable, std::vector\u003cT\u003e, std::void_t\u003cdecltype(\n  std::cout \u003c\u003c std::declval\u003cT\u003e()\n)\u003e\u003e = dyno::make_concept_map(\n  \"draw\"_s = [](std::vector\u003cT\u003e const\u0026 v, std::ostream\u0026 out) {\n    for (auto const\u0026 x : v)\n      out \u003c\u003c x \u003c\u003c ' ';\n  }\n);\n\nf(std::vector\u003cint\u003e{1, 2, 3}) // prints \"1 2 3 \"\n```\n\n\u003e Notice how we do not have to modify `std::vector` at all. How could we do\n\u003e this with classic polymorphism? Answer: no can do.\n\n\n### Erasing non-member functions\n__Dyno__ allows erasing non-member functions and functions that are dispatched\non an arbitrary argument (but only one argument) too. To do this, simply define\nthe concept using `dyno::function` instead of `dyno::method`, and use the\n`dyno::T` placeholder to denote the argument being erased:\n\n```c++\n// Define the interface of something that can be drawn\nstruct Drawable : decltype(dyno::requires_(\n  \"draw\"_s = dyno::function\u003cvoid (dyno::T const\u0026, std::ostream\u0026)\u003e\n)) { };\n```\n\nThe `dyno::T const\u0026` parameter used above represents the type of the object\non which the function is being called. However, it does not have to be the\nfirst parameter:\n\n```c++\nstruct Drawable : decltype(dyno::requires_(\n  \"draw\"_s = dyno::function\u003cvoid (std::ostream\u0026, dyno::T const\u0026)\u003e\n)) { };\n```\n\nThe fulfillment of the concept does not change whether the concept uses a\nmethod or a function, but make sure that the parameters of your function\nimplementation match that of the function declared in the concept:\n\n```c++\n// Define how concrete types can fulfill that interface\ntemplate \u003ctypename T\u003e\nauto const dyno::default_concept_map\u003cDrawable, T\u003e = dyno::make_concept_map(\n  \"draw\"_s = [](std::ostream\u0026 out, T const\u0026 self) { self.draw(out); }\n  //            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ matches the concept definition\n);\n```\n\nFinally, when calling a `function` on a `dyno::poly`, you'll have to pass in\nall the parameters explicitly, since __Dyno__ can't guess which one you want\nto dispatch on. The parameter that was declared with a `dyno::T` placeholder\nin the concept should be passed the `dyno::poly` itself:\n\n```c++\n// Define an object that can hold anything that can be drawn.\nstruct drawable {\n  template \u003ctypename T\u003e\n  drawable(T x) : poly_{x} { }\n\n  void draw(std::ostream\u0026 out) const\n  { poly_.virtual_(\"draw\"_s)(out, poly_); }\n  //                              ^^^^^ passing the poly explicitly\n\nprivate:\n  dyno::poly\u003cDrawable\u003e poly_;\n};\n```\n\n\n\u003c!-- Links --\u003e\n[`std::any`]: http://en.cppreference.com/w/cpp/utility/any\n[`std::function`]: http://en.cppreference.com/w/cpp/utility/functional/function\n[badge.Travis]: https://travis-ci.org/ldionne/dyno.svg?branch=master\n[Boost.CallableTraits]: https://github.com/badair/callable_traits\n[Boost.Hana]: https://github.com/boostorg/hana\n[Boost.TypeErasure]: http://www.boost.org/doc/libs/release/doc/html/boost_typeerasure.html\n[C++0x Concept Maps]: https://isocpp.org/wiki/faq/cpp0x-concepts-history#cpp0x-concept-maps\n[Go interfaces]: https://gobyexample.com/interfaces\n[Google Benchmark]: https://github.com/google/benchmark\n[Haskell type classes]: http://learnyouahaskell.com/types-and-typeclasses\n[libawful]: https://github.com/ldionne/libawful\n[Mpark.Variant]: https://github.com/mpark/variant\n[Rust trait objects]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html\n[type erasure]: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Erasure\n[virtual concepts]: https://github.com/andyprowl/virtual-concepts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fldionne%2Fdyno","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fldionne%2Fdyno","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fldionne%2Fdyno/lists"}