{"id":18034458,"url":"https://github.com/cbeck88/strict-variant","last_synced_at":"2025-03-27T06:31:33.524Z","repository":{"id":123400784,"uuid":"64229598","full_name":"cbeck88/strict-variant","owner":"cbeck88","description":"A realtime/embedded-friendly C++11 variant type which is never empty and prevents undesirable implicit conversions","archived":false,"fork":false,"pushed_at":"2018-08-08T16:30:56.000Z","size":1585,"stargazers_count":98,"open_issues_count":1,"forks_count":12,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-20T04:12:51.604Z","etag":null,"topics":["container","cpp11","polymorphism","type-safety","union","variant"],"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/cbeck88.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}},"created_at":"2016-07-26T14:49:47.000Z","updated_at":"2025-02-23T14:54:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"20cb760d-82eb-4654-9c31-c8cf7765996e","html_url":"https://github.com/cbeck88/strict-variant","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbeck88%2Fstrict-variant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbeck88%2Fstrict-variant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbeck88%2Fstrict-variant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbeck88%2Fstrict-variant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cbeck88","download_url":"https://codeload.github.com/cbeck88/strict-variant/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245797566,"owners_count":20673886,"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":["container","cpp11","polymorphism","type-safety","union","variant"],"created_at":"2024-10-30T11:12:06.839Z","updated_at":"2025-03-27T06:31:33.519Z","avatar_url":"https://github.com/cbeck88.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# strict variant\n\n[![Build Status](https://travis-ci.org/cbeck88/strict-variant.svg?branch=master)](http://travis-ci.org/cbeck88/strict-variant)\n[![Appveyor status](https://ci.appveyor.com/api/projects/status/github/cbeck88/strict-variant?branch=master\u0026svg=true)](https://ci.appveyor.com/project/cbeck88/strict-variant)\n[![Boost licensed](https://img.shields.io/badge/license-Boost-blue.svg)](./LICENSE)\n\nDo you use `boost::variant` or one of the many open-source C++11 implementations of a \"tagged union\" or variant type\nin your C++ projects?\n\n`boost::variant` is a great library. I created `strict_variant` in order to address a few things about `boost::variant` that I didn't like.\n\nThe tl;dr version is that unlike `boost::variant` or `std::variant`, `strict_variant` will never throw an exception or make a dynamic allocation\nin the effort of supporting types that have throwing moves. The default version will simply fail a static assert if this would happen. The\n`strict_variant::easy_variant` will make allocations in this situation, so you can opt-in to that if you want, and these two versions of variant\n\"play nicely\" together. This kind of thing is often a major concern in projects with realtime requirements, or in embedded devices, which may not\nallow, or simply may not have these C++ features. If you are making a library that might be used in \"conventional\" projects that want the ease-of-use\nthat comes from `boost::variant`, but might also be used in projects with restrictive requirements, and you want to use a `variant` type as part of the\nAPI, `strict_variant` might offer a way to keep everyone happy.\n\nBesides this, there are some issues in the interface of variant that were addressed that make it more pleasant to use day-to-day IMHO.\n(These were actually the original motivation of the project.)\n\n- I didn't like that code like this may compile without any warning or error messages:\n\n  ```c++\n  boost::variant\u003cstd::string, int\u003e v;  \n\n  v = true;  \n  ```\n  \n  I'd usually rather that my `variant` is more restrictive about what implicit conversions can happen.\n\n- I *wanted* that things like this should compile and do what makes sense, even if overload resolution would\n  be ambiguous.\n\n  ```c++\n  variant\u003cbool, long, double, std::string\u003e v;  \n\n  v = true;  // selects bool\n  v = 10;    // selects long \n  v = 20.5f; // selects double\n  v = \"foo\"; // selects string\n  ```\n\n  I also wanted that such behavior (what gets selected in such cases) is portable.\n\n  (For code examples like this, where `boost::variant` has unfortunate behavior, see \"Abstract and Motivation\" in the documentation.)\n\n  In `strict_variant` we modify overload resolution in these situations by removing some candidates.\n\n  For instance:\n     * We eliminate many classes of problematic conversions, including narrowing and pointer conversions.\n     * We prohibit standard conversions between `bool`, integral, floating point, pointer, character, and some other classes.  \n     * We impose \"rank-based priority\". If two integer promotions are permitted\n       but one of them is larger than another, the larger one gets discarded,  \n       e.g. if `int -\u003e long` and `int -\u003e long long` are candidates,\n       the `long long` is eliminated.\n       \n   See documentation for details.\n  \n- I didn't like that `boost::variant` will silently make backup copies of my objects. For instance, consider this simple program,\n  in which `A` and `B` have been defined to log all ctor and dtor calls.\n  \n  ```c++\n  int main() {\n    using var_t = boost::variant\u003cA, B\u003e;\n  \n    var_t v{A()};\n    std::cout \u003c\u003c \"1\" \u003c\u003c std::endl;\n    v = B();\n    std::cout \u003c\u003c \"2\" \u003c\u003c std::endl;\n    v = A();\n    std::cout \u003c\u003c \"3\" \u003c\u003c std::endl;\n  }\n  ```\n  \n  The `boost::variant` produces the following output:\n  \n  ```c++\n  A()\n  A(A\u0026\u0026)\n  ~A()\n  1\n  B()\n  B(B\u0026\u0026)\n  A(const A \u0026)\n  ~A()\n  B(const B \u0026)\n  ~A()\n  ~B()\n  ~B()\n  2\n  A()\n  A(A\u0026\u0026)\n  B(const B \u0026)\n  ~B()\n  A(const A \u0026)\n  ~B()\n  ~A()\n  ~A()\n  3\n  ~A()\n  ```\n  \n  This may be pretty surprising to some programmers.\n   \n  By contrast, if you use the C++17 `std::variant`, or one of the variants with\n  \"sometimes-empty\" semantics, you get something like this (this output from `std::experimental::variant`)\n  \n  ```c++\n  A()\n  A(A\u0026\u0026)\n  ~A()\n  1\n  B()\n  ~A()\n  B(B\u0026\u0026)\n  ~B()\n  2\n  A()\n  ~B()\n  A(A\u0026\u0026)\n  ~A()\n  3\n  ~A()\n  ```\n  \n  This is much closer to what the naive programmer expects who doesn't know about internal\n  details of `boost::variant` -- the only copies of his objects that exist are what he can see\n  in his source code.\n  \n  This kind of thing usually doesn't matter, but sometimes if for instance you are\n  debugging a nasty memory corruption problem (perhaps there's bad code in one of the objects contained\n  in the variant), then these extra objects, moves, and copies, may make things incidentally more complicated.\n  \n  Here's what you get with `strict_variant`:\n  \n  ```c++\n  A()\n  A(A\u0026\u0026)\n  ~A()\n  1\n  B()\n  B(B\u0026\u0026)\n  ~A()\n  ~B()\n  2\n  A()\n  A(A\u0026\u0026)\n  ~B()\n  ~A()\n  3\n  ~A()\n  ```\n  \n  Yet, `strict_variant` does not have an empty state, and is fully exception-safe!\n\n  (These examples from `gcc 5.4`, see code in [`example`](./example) folder.)\n\n  To summarize the differences:\n\n  - `std::variant` is rarely-empty, always stack-based. In fact, it's empty exactly\n    when an exception is thrown. Later, it throws different exceptions if you try to visit\n    when it is empty.\n  - `boost::variant` is never-empty, usually stack-based. It has to make a dynamic allocation\n    and a backup copy whenever an exception *could* be thrown, but that gets freed right after\n    if an exception is not actually thrown.\n  - `strict_variant` is never-empty, and stack-based exactly when the current value-type is\n    nothrow moveable. It never makes a backup move or copy, and never throws an exception.\n   \n  Each approach has its merits. I chose the `strict_variant` approach because I find it\n  simpler and it avoids what I consider to be drawbacks of `boost::variant` and `std::variant`. \n  And, if you manage\n  to make all your types no-throw move constructible, which I often find I can, then `strict_variant`\n  gives you optimal performance, the same as `std::variant`, without an empty state.\n\n\nFor an in-depth discussion of the design, check out the documentation.\n\nFor a gentle intro to variants, and an overview of strict-variant, see **slides**\nfrom a talk I gave about this: \\[[pptx](https://cbeck88.github.io/strict-variant/strict_variant.pptx)\\]\\[[pdf](https://cbeck88.github.io/strict-variant/strict_variant_static.pdf)\\]\n\n\nDocumentation\n=============\n\nOn [github pages](https://cbeck88.github.io/strict-variant/index.html).\n\nCompiler Compatibility\n======================\n\n`strict_variant` targets the C++11 standard.\n\nIt is known to work with `gcc \u003e= 4.8` and `clang \u003e= 3.5`, and is tested against `MSVC 2015`.\n\n`strict_variant` can be used as-is in projects which require `-fno-exceptions` and `-fno-rtti`.\n\nLicensing and Distribution\n==========================\n\n**strict variant** is available under the boost software license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbeck88%2Fstrict-variant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcbeck88%2Fstrict-variant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbeck88%2Fstrict-variant/lists"}