With the use of constant expressions, parameter packs, lambdas, move semantics, standard containers, and std::any I can easily enough create the following design pattern:
#include <utility>
#include <vector>
#include <any>
constexpr auto assign_properties = [](auto&&... t) {
return std::vector<std::any>{ std::move(t)... };
};
That would create an arbitrary list of properties or attributes without the need of defining a struct
or class
object.
Simply enough it can be used in the following manner:
{
auto props = assign_properties(1, 3.1f, 6.4, 'a');
}
Now, to access the type-safe data within this container one would have to know each element's type in advance and would have to extract them in the following manner:
{
auto a = std::any_cast<int>( props[0] );
auto b = std::any_cast<float>( props[1] );
auto c = std::any_cast<double>( props[2] );
auto d = std::any_cast<char>( props[3] );
}
Okay, this is simple and fair enough...
Let's say I want to incorporate some kind of mechanism such as this within a class hierarchy that will preserve polymorphic behaviors...
Within the class hierarchy, I may want to have an abstract base type that all types are derived from. As for each derived or subclass, they may contain a set of properties or attributes that describes either its behavior or state, however, each subclass may have a different amount of attributes with various different types...
For example:
class Animal {
protected:
Animal(); // Abstract
std::vector<std::any> attributes_;
};
class Cat : public Animal {
public:
// ...
};
class Dog : public Animal {
public:
// ...
};
class Eagle : public Animal {
public:
// ...
};
Now, I'm not showing any polymorphic behavior, virtual, or purely virtual methods here since they are not necessary towards the intent of my question which I will get to briefly... somewhere else within some other code these objects are being created and then populated...
struct Fur;
struct Fangs;
struct Claws;
struct Feathers;
struct Talons;
struct Scales;
{
Cat c1;
c1.populate_attributes(assign_properties( "Fluffy", 9, Fur, Fangs, Claws));
Dog d1;
d1.populate_attributes(assign_properties( "Ruff", 3, Fur, Fangs, Claws ));
Eagle e1;
e1.populate_attriutes(assign_properties( Fangs, Claws, Feathers, Talons );
}
Simple enough... Now, as I stated above, accessing the values within these vectors from std::any
requires you to know the type
ahead of time in order to use std::any_cast
to extract its value in a type-safe
manner.
Let's say that I plan on using this while parsing a file to create and populate my class data types with their properties. Then somewhere else in some other function or code block, these elements need to be accessed and I don't know what their types are... std::any
within this context does not provide any facilities nor helps me with this process.
{
std::vector<Animals*> animals { Constructed & Populated; }
}
// some other code block
{
auto a, b, c, d, ...;
a = animals[0];
b = animals[1];
c = animals[2];
d = animals[3];
// Above we got each individual animal from our external source vector...
auto e,f,g,h, ...;
e = a[0]; // doesn't give the type and its value, e will be `std::any`.
e = std::any_cast<auto>( e[0] ); // will fail to compile since `<T>` must
// be known at compile time.
e = std::any_cast<?>( e[0] ); // ... and this is where I'm stuck
// I need some form of extraction here!
// and I would like for it to be automatic and generic.
}
If the user doesn't know the types
ahead of time and needs to access these values within a type-safe
context, is there an easy way to automate this retrieval or extraction process with the current implementation that is described above? If not, what can be done to modify the above code to make it work as intended, or is there any other mechanism or construct that could be used in place of this to implement a similar design pattern while preserving or providing the behavior that I have expressed?
question from:
https://stackoverflow.com/questions/65940503/generically-accessing-data-from-a-varying-list-of-attributes-within-sub-classes