{"id":16719,"date":"2022-11-09T05:00:00","date_gmt":"2022-11-09T04:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=16719"},"modified":"2023-02-16T15:09:46","modified_gmt":"2023-02-16T14:09:46","slug":"modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-c17","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/en\/modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-c17\/","title":{"rendered":"Modern approach in object-oriented design with std::variant and std::visit (C++17)"},"content":{"rendered":"\n<p>Inheritance is one of the core concepts of object-oriented programming (OOP) languages. It is a mechanism where you can derive a class from another class for a hierarchy of classes that share a set of attributes and methods.<\/p>\n\n\n\n<p>It is a mechanism that allows developers to create a hierarchy between classes using an \u201eis-a\u201d relationship. The class being inherited from is called a parent class (base class) and the class inheriting is called the child class (derived class).<\/p>\n\n\n\n<p>Inheritance allows programmers to create classes built upon existing classes and specify a new implementation while maintaining the same behaviours.<\/p>\n\n\n\n<p>This article will demonstrate an alternative approach which will allow the creation of many polymorphic implementations with a minimal amount of code resulting in the same behaviour as in a well-known inheritance mechanism.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Runtime polymorphism with virtual functions<\/strong><\/h2>\n\n\n\n<p>When speaking of polymorphism immediately the first things that come to our mind are virtual functions and v-tables (C++).<\/p>\n\n\n\n<p>You declare a virtual function in a base class and then you override it in derived classes.<\/p>\n\n\n\n<p>When you call such a function on a reference or a pointer to the base class, then the compiler will invoke the correct overload. In most cases, compilers implement this technique with virtual tables (v-tables). Each class that has a virtual method contains an extra table that points to the addresses of the member functions. Before each call to a virtual method, the compiler needs to look at v-table and resolve the address of a derived function at runtime (it is called late method binding).<\/p>\n\n\n\n<p>An example below demonstrates a Vehicle base class with some interface and derived class (Pickup, Truck) which derive from it in a standard way.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Ryc.-1.png\"><img decoding=\"async\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Ryc.-1.png\" alt=\"kod\" class=\"wp-image-16720\" width=\"634\" height=\"809\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Ryc.-1.png 686w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Ryc.-1-235x300.png 235w\" sizes=\"(max-width: 634px) 100vw, 634px\" \/><\/a><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-3.png\"><img decoding=\"async\" width=\"636\" height=\"236\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-3.png\" alt=\"kod\" class=\"wp-image-16722\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-3.png 636w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-3-300x111.png 300w\" sizes=\"(max-width: 636px) 100vw, 636px\" \/><\/a><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-2.png\"><img decoding=\"async\" width=\"387\" height=\"356\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-2.png\" alt=\"schema\" class=\"wp-image-16724\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-2.png 387w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-2-300x276.png 300w\" sizes=\"(max-width: 387px) 100vw, 387px\" \/><\/a><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Pros and Cons of run-time polymorphism with virtual functions<\/strong><\/h2>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>it is very convenient to extend with new class,<\/li><li>object orientation allows deep hierarchies,<\/li><li>storing heterogeneous types in a single container is easy, just store pointers to the Base class or to the abstract class (interface).<\/li><\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>virtual methods must be resolved before the call (performance overhead),<\/li><li>since you need a pointer to call a method, usually it means dynamic allocation (more performance overhead),<\/li><li>you need to modify the code of all classes in the inheritance structure.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Std::variant polymorphism<\/strong><\/h2>\n\n\n\n<p>The class template <strong>std::variant<\/strong>&nbsp;<strong>represents a type-safe union<\/strong>. An instance of <strong>std::variant<\/strong> at any given time either holds a value of one of its alternative types or in the case of error \u2013 no value.<\/p>\n\n\n\n<p>With this approach, there is no <strong>Vehicle<\/strong> class needed anymore as in the previous example. We can create more unrelated types here. The vehicles variable defines an object that can be a <strong>Pickup<\/strong> or <strong>Truck<\/strong>. In order to call a function we need a callable object and std::visit.<\/p>\n\n\n\n<p>A struct <strong>callPrintName<\/strong> implements overloads for the call operator. Then <strong>std::visit<\/strong> takes the variant object and calls the correct overload. std :: visit is a powerful tool that allows you to call functions on the current type stored inside std :: variant. It can find the appropriate function overloads, and what&#8217;s more, it works for multiple variants at once.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-4.png\"><img decoding=\"async\" width=\"527\" height=\"769\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-4.png\" alt=\"code\" class=\"wp-image-16726\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-4.png 527w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-4-206x300.png 206w\" sizes=\"(max-width: 527px) 100vw, 527px\" \/><\/a><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-5.png\"><img decoding=\"async\" width=\"290\" height=\"144\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-5.png\" alt=\"schema\" class=\"wp-image-16728\"\/><\/a><\/figure><\/div>\n\n\n\n<p>This can be simplified and the callable object <strong>callPrintName<\/strong> can be replaced with generic lambdas if our variant subtypes share common interface.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-6.png\"><img decoding=\"async\" width=\"544\" height=\"169\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-6.png\" alt=\"code\" class=\"wp-image-16731\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-6.png 544w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-6-300x93.png 300w\" sizes=\"(max-width: 544px) 100vw, 544px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>What if we need some arguments? The main issues is that <strong>std::visit<\/strong> does not have a way to pass arguments into the callable object. It only takes a function object and a list of <strong>std::variant<\/strong> objects.<\/p>\n\n\n\n<p>One option is to create a custom functor:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-7.png\"><img decoding=\"async\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-7.png\" alt=\"code\" class=\"wp-image-16733\" width=\"646\" height=\"501\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-7.png 826w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-7-300x233.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-7-768x596.png 768w\" sizes=\"(max-width: 646px) 100vw, 646px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>The other options to solve it by capturing the argument and forwarding it to a member function<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-8.png\"><img decoding=\"async\" width=\"604\" height=\"184\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-8.png\" alt=\"code\" class=\"wp-image-16735\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-8.png 604w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-8-300x91.png 300w\" sizes=\"(max-width: 604px) 100vw, 604px\" \/><\/a><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The Overload pattern<\/strong><\/h2>\n\n\n\n<p>The overload pattern is useful for wrapping multiple separate lambdas into an overload set. The code is shorter and there is no need to declare a structure that holds <strong>operator()<\/strong> overloads. In C++17 it required 2 lines of code to be implemented:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-9.png\"><img decoding=\"async\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-9.png\" alt=\"\" class=\"wp-image-16737\" width=\"756\" height=\"45\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-9.png 872w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-9-300x18.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-9-768x46.png 768w\" sizes=\"(max-width: 756px) 100vw, 756px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>The first line is the overload pattern, the second is the deduction guide for it. The struct <strong>Overloaded<\/strong><\/p>\n\n\n\n<p>can have arbitrary many base classes. It derives from each class and brings the call operator(<strong>Ts::operator..<\/strong>) of each base class into the scope. The base classes need an overloaded call operator (<strong>Ts::operator())<\/strong>. Lambdas provide this call operator. <\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-10.png\"><img decoding=\"async\" width=\"591\" height=\"237\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-10.png\" alt=\"code\" class=\"wp-image-16739\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-10.png 591w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Fig.-10-300x120.png 300w\" sizes=\"(max-width: 591px) 100vw, 591px\" \/><\/a><\/figure><\/div>\n\n\n\n<p>The overload pattern demonstrated several C++ techniques and allows to write shorter code.&nbsp; C++17 simplifies syntax and reduces boilerplate code limiting potential errors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Pros and Cons of std::variant polymorphism<\/strong><\/h2>\n\n\n\n<p><strong>Pros<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>value semantics,<\/li><li>no dynamic allocation,<\/li><li>no need for the base class,<\/li><li>class can be unrelated,<\/li><li>duck typing: gives extra flexibility, we can call functions from visitors with a different number of arguments, return types etc. On the contrary virtual functions need to have the same signature<\/li><\/ul>\n\n\n\n<p><strong>Cons<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>need to know all types up front at compile time,<\/li><li>hard to add new types (changing type of variant and all visitors),<\/li><li>might waste memory,<\/li><li>each operation requires writing separate visitors,<\/li><li>passing parameters to <strong>std::visit<\/strong> is not as easy as with regular functions.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Summary<\/strong><\/h2>\n\n\n\n<p>Modern C++(17) extends possibilities in terms of the runtime polymorphism approach. It leverages <strong>std::variant<\/strong> and <strong>std::visit<\/strong> to obtain that behaviour and might offer better performance and value semantics.<\/p>\n\n\n\n<p>I\u2019ve shown how you can use <strong>std::visit<\/strong> with multiple variants. Such a technique might lead to various \u201cpattern matching\u201d algorithms. You have a set of types, and you want to perform some algorithm based on the currently active types. It\u2019s like doing polymorphic operations, but differently \u2013 as <strong>std::visit<\/strong> doesn\u2019t use any v-tables.<\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<p>If you want more detailed information on <strong>std::visit<\/strong> and <strong>std::variant<\/strong> and its internals please visit the link:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/mpark.github.io\/programming\/2015\/07\/07\/variant-visitation\/\" rel=\"nofollow\" >Variant visitation<\/a><\/li><li><a href=\"https:\/\/cpp-polska.pl\/post\/czym-jest-stdvariantij\" rel=\"nofollow\" >Czym jest std::variant<\/a><\/li><\/ul>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;16719&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;7&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 7)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Modern approach in object-oriented design with std::variant and std::visit (C++17)&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 7)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Inheritance is one of the core concepts of object-oriented programming (OOP) languages. It is a mechanism where you can derive &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/en\/modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-c17\/\">Continued<\/a><\/p>\n","protected":false},"author":423,"featured_media":19834,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1320],"tags":[1342,1395,1430],"class_list":["post-16719","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hard-development","tag-embedded-en","tag-c17-en","tag-stdvariant-en"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/11\/Modern-approach-in-object-oriented-design-with-stdvariant-and-stdvisit-C17.jpg","category_names":["Hard development"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/16719"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/users\/423"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/comments?post=16719"}],"version-history":[{"count":4,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/16719\/revisions"}],"predecessor-version":[{"id":26768,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/16719\/revisions\/26768"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media\/19834"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media?parent=16719"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/categories?post=16719"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/tags?post=16719"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}