https://github.com/wookeybiscotti/poly_value
C++ library for storing polymorphic objects on the stack
https://github.com/wookeybiscotti/poly_value
cpp20 optimization polymorphic polymorphism stack variant virtual
Last synced: 4 months ago
JSON representation
C++ library for storing polymorphic objects on the stack
- Host: GitHub
- URL: https://github.com/wookeybiscotti/poly_value
- Owner: WookeyBiscotti
- License: mit
- Created: 2025-06-05T08:15:43.000Z (8 months ago)
- Default Branch: master
- Last Pushed: 2025-06-12T20:34:36.000Z (8 months ago)
- Last Synced: 2025-06-12T21:28:20.764Z (8 months ago)
- Topics: cpp20, optimization, polymorphic, polymorphism, stack, variant, virtual
- Language: C++
- Homepage:
- Size: 358 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# poly_value
  
[](https://opensource.org/licenses/MIT)
Polymorphic C++ Classes on the Stack
- [Quick start](#quick-start)
- [Problem](#problem)
- [A solution option](#a-solution-option)
- [std::variant](#stdvariant)
- [Pros](#pros)
- [Cons](#cons)
- [Solution](#solution)
- [API](#api)
- [Definition](#definition)
- [Copy/Move](#copymove)
## Quick start
```c++
struct A {
virtual ~A() = default;
virtual void print() = 0;
};
struct B: A {
void print() override {
std::cout << "B::print " << value << std::endl;
}
int value = 42;
};
struct C: A {
void print() override {
std::cout << "C::print " << value << std::endl;
}
float value = 3.1415f;
};
using poly_value = pv::poly_value;
poly_value pv1 = B{};
poly_value pv2 = C{};
pv1->print(); // `B::print 42`
pv2->print(); // `C::print 3.1415`
pv1 = pv2;
pv1->print(); // `C::print 3.1415`
pv2 = B{};
pv1 = std::move(pv2); // pv2 value was moved
pv1->print(); // `B::print 42`
std::cout << pv2.has_value() << std::endl; // 0
```
## Problem
You 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.
```c++
class A {...};
class B: public A {...};
class C: public A {...};
class D: public A {...};
std::array, 4> values;
// No heap memory allocation
values[0].emplace(...);
values[1].emplace(...);
values[2].emplace(...);
values[3].emplace(...);
std::array> values;
// Heap memory allocations
values[0] = std::make_unique(...);
values[1] = std::make_unique(...);
values[2] = std::make_unique(...);
values[3] = std::make_unique(...);
```

## A solution option
### std::variant
#### Pros
- A variant object can store different classes
- No heap allocation
#### Cons
- To access a class method, you need to write a visitor for each class.
- In order for a variant to store a class, it must be declared as a parameter of the variant template
## Solution
Create a suitable storage for objects inherited from the base class, knowing the base class, you can call its methods from the created object.
## API
### Definition
To create a `poly_value` you must know:
- base class
- estimated storage size for the base class descendants
```c++
class Base {...};
using poly_value = pv::poly_value;
class Derived: public Base {...};
poly_value value;
value = Derived();
```
At the time of defining a poly_value you do not need to know about the descendants of the base class.
### Copy/Move
Since values are stored on the stack, we may want to assign one value to another.
```c++
class Base {...};
class Derived: public Base {...};
using poly_value = pv::poly_value;
poly_value v1(Base{});
poly_value v2(Derived{});
v1 = v2; // At this point, v1 will call the Base destructor, then the Derived copy constructor.
v2 = std::move(v1); // At this point, v2 will call the Derived destructor, then the Derived move constructor.
```
For all this to work, it is necessary for classes to implement:
- a copy constructor
or
- copy assignment operator and a default constructor
In the case of move semantics, it is necessary for classes to implement:
- a move constructor
or
- move assignment operator and a default constructor.
You can control the presence of a copy or move operator using additional flags.
```c++
pv::poly_value value;
pv::poly_value value;
pv::poly_value value;
```