C++ change parent class based on option - c++

There is a Student class inherited from Person.
And there is Student class inherited from University.
I want to change the parent class Person, University based on the option without rewriting Student such as Student1 and Student2 (because student class is very complicated).
Here is the example code.
class Person {
void f() {printf("I'm person")}
};
class University {
void f() {printf("I'm university")}
};
class Student1 : public Person {
void g() {f()}
};
class Student2 : public University {
void g() {f()} // I don't wan't to rewrite this!
};
if (option.person) {
Student1 student;
}
else {
Student2 student;
}

Since we can't know what option.person is at compile-time, we need to find a way to work around that at runtime.
One option for doing so is std::variant, which can store any number of different types; but does so at the cost of always having the same size as the largest templated type.
As an example, if I did this:
std::variant<char, int> myVariant = '!';
Even though myVariant holds a char (1 byte), it uses 4 bytes of RAM because an int is 4 bytes.
Using Variants
Rather than inheriting from different objects that do not share a common base at compile-time, we can maintain the 'base' type as a variable within Student instead.
#include <iostream>
#include <variant>
#include <concepts>
class Person {
public:
void f()
{
std::cout << "I'm a person!\n";
}
};
class University {
public:
void f()
{
std::cout << "I'm a university!\n";
}
};
class Student {
public:
using variant_t = std::variant<Person, University>;
variant_t base;
// Here we accept an rvalue of any type, then we move it to the 'base' variable.
// if the type is not a Person or University, a compiler error is thrown.
Student(auto&& owner) : base{ std::move(owner) } {}
void g()
{
// METHOD 1: Using std::holds_alternative & std::get
// This has the advantage of being the simplest & easiest to understand.
if (std::holds_alternative<Person>(base))
std::get<Person>(base).f();
else if (std::holds_alternative<University>(base))
std::get<University>(base).f();
// METHOD 2: Using std::get_if
// This has the advantage of being the shortest.
if (auto* person = std::get_if<Person>(&base))
person->f();
else if (auto* university = std::get_if<University>(&base))
university->f();
// METHOD 3: Using std::visit
// This has the advantage of throwing a meaningful compiler error if-
// -we modify `variant_t` and end up passing an unhandled type.
std::visit([](auto&& owner) {
using T = std::decay_t<decltype(owner)>;
if constexpr (std::same_as<T, Person>)
owner.f(); //< this calls `Person::f()`
else if constexpr (std::same_as<T, University>)
owner.f(); //< this calls `University::f()`
else static_assert(false, "Not all potential variant types are handled!");
}, base);
}
};
In this example, I showed 3 different methods of accessing the underlying value of base.
As a result, the output is:
Further reading:
std::variant
std::get
std::get_if
std::visit

Related

How to get a reference/pointer to a class (not an object)?

I have a std::map where key is string and I want the value to be, not an object, but a reference/pointer to a class which I can instantiate.
std::map<::std::string, ?class_reference?> handlers_;
Once the specific entry is chosen, I want to create instance of the class and execute a member function.
As others have already mentioned, if you want to create relevant objects via a string (such as class name), you'll need to use factory pattern which can create related objects (same base class). Here's a simple example easy to understand (you just store lambda which returns objects in the map):
#include <map>
#include <string>
#include <functional>
#include <iostream>
class Base {};
class A : public Base{
public:
A() { std::cout << "A ctr" << std::endl; }
};
class B : public Base {
public:
B() { std::cout << "B ctr" << std::endl; }
};
int main() {
std::map<std::string, std::function<Base*()> > m;
m["a"] = []() { return new A(); };
m["b"] = []() { return new B(); };
m["a"]();
m["b"]();
}
How to get a reference/pointer to a class (not an object)?
Short answer, you can't. Unfortunately, C++ doesn't work that way.
However, to solve your specific problem, you have the option of a factory pattern, which would look something like this:
template <class T>
class factory {
public:
virtual std::unique_ptr<T> createObject() = 0;
};
class base { // some base class common to all the types you are intending to create
};
class foo : public base { // some class you want to create
};
class fooCreator : public fatory<base> {
public:
std::unique_ptr<T> createObject() {
return someUniquePointerToANewObject;
}
}
int main() {
std::map<std::string, std::unique_ptr<factory<base>>> myMap; // because we are creating some bases
myMap["some key"] = new myFooCreator;
// now let's create some new `base`
if(myMap.find("some matching key") != myMap.end()) {
std::unique_ptr<base> myBaseObject = myMap["some matching key"]->createObject();
// use created object
}
return 0;
}
Of course, there are a lot of things that could go wrong here, like if for some reason you push a nullptr to myMap. But at least you have an idea of what it looks like now.

c++ Mapping class to number

I recently started with c++ development. I've come to a problem of which I am not able to solve, given that I am unaware if the following is possible.
I want to create a mapping between a number and class, which are derived from an abstract class.
Essentially what I would like to be able to do is create a factory method that can create a new instance of a class based on a given number associated with that class.
I know that I could do the following...
Vehicle *Vehicle::from_type(byte type)
{
switch(type)
{
case 0x00: return new Bicyle();
case 0x01: return new Car();
...
case 0x10: return new Truck();
}
return null;
}
..., but I'd rather not as I want to keep it DRY.
It there a way where one can do something along the lines of this:
// I know this is incorrect syntax
const map<byte, class extends Vehicle> VEHICLE_MAPPING = {{0x00, Bicyle}, {0x01, Car}, ..., {0x10, Truck}};
Vehicle *Vehicle::from_type(byte type)
{
return new VEHICLE_MAPPING[type]();
}
I can see how your approach could work with usage of std::map<uint8_t, std::unique_ptr<Vehicle>>, but there is a problem - you wouldn't be able to initialise that map with initializer_list, since it copies the elements and, as we all know, std::unique_ptr cannot be copied. You would have to create an init() function to initialise the map that would use similar logic to your Vehicle *Vehicle::from_type(byte type), which would simply be pointless given you already have your function.
Furthermore, I disagree that your first solution violates DRY. It is actually correct in a sense that you won't be forced to use switch or ifs elsewhere in the code. I'd definitely stick with it.
The final note - you could use std::map<uint8_t, std::shared_ptr<Vehicle>> instead of std::map<uint8_t, std::unique_ptr<Vehicle>> and initialise it with initializer_list, since std::shared_ptr can be copied, but I wouldn't advise that since it wrongly indicates the usage of shared_ptr. If you somehow feel forced to do so, here is an example:
class Base{ public: virtual ~Base() = default; };
class Derived1 : public Base{};
class Derived2 : public Base{};
class derived_factory{
private:
derived_factory();
static inline std::map<uint8_t, std::shared_ptr<Base>> base_map = {
{0x00, std::make_shared<Derived1>()},
{0x01, std::make_shared<Derived2>()}
};
public:
static std::unique_ptr<Base> from_type(uint8_t type)
{
return std::make_unique<Base>(*base_map[type]);
}
};
int main()
{
auto ptr = derived_factory::from_type(0x00);
// ptr is of a type std::unique_ptr<Base> and points to Derived1 object
}
Additional note that should be a final discouragement of using this solution is that it's quite slow. It constructs the objects in a map and does nothing with them except for keeping them as 'templated' copy examples.
If they're all derived from a base class, you can use the factory pattern, e.g., from Loki's implementation (see Modern C++ Design for the details, though that book is pre-C++11).
The following creates some concrete vehicles and puts them in a vector and then calls the drive() method on each of them:
#include <iostream>
#include <memory>
#include <vector>
#include "factory.h"
struct Vehicle
{
virtual ~Vehicle() = default;
virtual void drive() = 0;
};
struct Car : Vehicle
{
static constexpr auto ID = 1;
void drive() override { std::cout << "Car\n"; }
};
struct Truck : Vehicle
{
static constexpr auto ID = 2;
void drive() override { std::cout << "Truck\n"; }
};
// Create the factory object
auto g_factory = MyUtil::Factory<std::unique_ptr<Vehicle>, int>{};
void RegisterTypesWithFactory()
{
// We pass in creator functions for each type. Note that these
// could be lambdas or some other freestanding function and they
// could accept parameters.
g_factory.Register( Car::ID, &std::make_unique<Car> );
g_factory.Register( Truck::ID, &std::make_unique<Truck> );
}
int main()
{
// Configure the factory
// Note: Registration can be done any time, e.g., later based on input
// from a file. I do them all at once here for convenience of illustration.
RegisterTypesWithFactory();
// Create some objects with the factory
auto vehicles = std::vector<std::unique_ptr<Vehicle>>{};
vehicles.emplace_back( g_factory.Create( Car::ID ) );
vehicles.emplace_back( g_factory.Create( Truck::ID ) );
// Do something with the objects
for( const auto& v : vehicles )
{
v->drive();
}
}
Which prints:
Car
Truck
See it run live on Wandbox.

Is an object valid in serialized form? c++/inheritance/serialization

I'm asking this because even tho it seems to work, I feel like it shouldn't. The goal is to have a collection of objects kept alive, and general access to them. This is what I have at the moment:
Take a base pointer access:
struct base { virtual void tick() = 0; }; //ptr access
With different types that inherit from it:
struct :public base
{
void tick() { cout << "im type 1" << endl; }
}type1;
struct :public base
{
void tick() { cout << "im type 2" << endl; }
}type2;
Then a container class that should be able to store any amount of these serialized:
class control
{
struct xtype //struct for organizing objects
{
vector<char>charbuf; //serialized object
}xtype_template;
vector<xtype>xtype_vec;
public:
template<typename T> base* tell_to(T &input) //take object, return (base*)
{
xtype_template.charbuf.resize(sizeof(input));
memcpy(xtype_template.charbuf.data(), (char*)&input, sizeof(input));
xtype_vec.push_back(xtype_template); //push back with template after filling
return (base*)xtype_vec[xtype_vec.size() - 1].charbuf.data(); //pointer to data
}
}xcontainer; //container object
Then call:
auto ptr = controller.tell_to(type1); //becomes base*
auto ptr2 = controller.tell_to(type2);
And you can access either static-sized serialized object, as well as its states, by doing:
ptr->tick(); //which will output "im type 1" to console
ptr2->tick() //"im type 2"
But is this legal? Do these serialized versions have an actual type? Is accessing a serialized object directly with a base pointer illegal or wrong?
Closest probable answer: as the return on is_trivially_copyable show false, objects might not be safe to manage after getting base inheritance.
Follow up: This approach seems to work, and fiddling with is_trivially_copyable, seems to suggest that making an object inherit methods from, makes it unsafe. Basic methods do not make it unsafe however, and that makes me wonder if the safety only applies to exporting between systems, saving to file, or transferring over the network. Maybe the check just assumes the virtual reference makes them unsafe?
Follow up 2: If the characters remain in the same spot in memory, does it matter how they are accessed? Id wager that the only real problem with this approach, is if the objects stored were to have elements that would change their size after being stored.
What you are doing is illegal. You can only memcpy an object as an array of chars when the object is TriviallyCopyable. And your object is not, since it has virtual functions.
Instead of doing this, you should simply store a (unique) pointer to newly allocated object, and avoid any casts to enforce hierarchy. Like this:
class xtype
{
std::unique_ptr<base> ptr;
public:
template<typename T> base* serial_acc(T &input) //take object, return (base*)
{
static_assert(std::is_base_of<base, T>::value, "Please use proper type");
ptr = std::make_unique<base>(input);
return ptr;
}
} xcontainer;
Working example given by user Andy Prowl
#include <vector>
#include <memory>
#include <iostream>
using namespace std;
struct base
{
virtual void tick() = 0;
};
struct type1 : base
{
virtual void tick() override { cout << "im type 1"<<endl; }
};
struct type2 : base
{
virtual void tick() override { cout << "im type 2" << endl; }
};
struct controller
{
vector<unique_ptr<base>> objects;
void cycle_tick(){ for (auto const& ptr : objects)ptr->tick();}
void add_object(unique_ptr<base> obj){ objects.emplace_back(move(obj));}
};
int main()
{
auto t1 = unique_ptr<type1>(new type1);
auto t2 = unique_ptr<type2>(new type2);
controller ctrl_object;
c.add_object(move(t1));
c.add_object(move(t2));
ctrl_object.cycle();
}

How to store templated objects of different type in container?

Assuming I have a vector (or list or whatever container might be more suitable here) that I would like to store multiple objects (or pointers) of a templated type in:
std::vector<MyClass<double>> v;
// std::vector<MyClass<double> *> v;
Unfortunately, I want to store different templated objects in this container (and I need to access them ideally at constant time).
My first intuition was to create some sort of WrapperClass around MyClass that would internally manage any MyClass as a member variable, but it's not clear to me how I could pass along the appropriate type through to MyClass:
#include <iostream>
#include <string>
#include <stdlib.h>
#include <vector>
using namespace std;
template<typename T>
class MyClass
{
public:
MyClass() {}
~MyClass() {}
};
// templating this of course works, but it doesn't solve my problem
template<typename T>
class WrapperClass
{
public:
WrapperClass()
{
m_object = MyClass<T>();
}
~WrapperClass() { }
private:
MyClass<T> m_object;
};
int main()
{
WrapperClass<bool> tmp = WrapperClass<bool>();
std::vector<WrapperClass<bool> *> v;
return 0;
}
So is there (A) a different container than vector that I could be using for this problem or (B) a way to select the type of MyClass in WrapperClass inside the constructor? I was thinking of something along the lines of:
class WrapperClass2
{
public:
WrapperClass2(unsigned int typeId)
{
switch (typeId)
{
case 0: m_object = new MyClass<bool>();
case 1: m_object = new MyClass<int>();
case 2: m_object = new MyClass<float>();
default: m_object = new MyClass<double>();
}
}
~WrapperClass2()
{
delete m_object;
}
private:
MyClass * m_object;
};
Another idea may be to have some parent AbstractType that I would be using in the vector, but I'm not sure how that would help with the templated type problem.
Different instantiations of a class template are completely unrelated types, so you cannot have a container that directly stores them.
You have a few options:
Keep a collection of pointers to some base class that your class template inherits from:
class Base
{
virtual ~Base {}
virtual void someMethod() const = 0;
};
template <typename T>
class MyClass : public Base
{
void someMethod() const
{
// stuff
}
};
int main()
{
std::vector<std::unique_ptr<Base>> objs;
objs.push_back(std::make_unique<MyClass<int>>());
objs.push_back(std::make_unique<MyClass<std::string>>());
for (auto& i : objs) {
i->someMethod();
}
}
This is a fairly simple approach, but it incurs a bit of runtime overhead with dynamic allocation and RTTI. Note also that someMethod can't return T, since it's a method on a parent class that doesn't know what T is.
Use some sort of type-erased wrapper like boost::any (or the forthcoming std::any in C++17).
#include <any>
#include <string>
#include <vector>
template <typename T>
class MyClass {
public:
T someMethod() const {
// stuff
return {};
}
};
void someFunctionThatTakesInt(int i) {}
void someFunctionThatTakesString(std::string s) {}
int main() {
std::vector<std::any> objs;
objs.push_back(MyClass<int>());
objs.push_back(MyClass<std::string>());
for (const auto& i : objs) {
if (i.type() == typeid(MyClass<int>)) {
auto& mc = std::any_cast<const MyClass<int>&>(i);
someFunctionThatTakesInt(mc.someMethod());
} else if (i.type() == typeid(MyClass<std::string>)) {
auto& mc = std::any_cast<const MyClass<std::string>&>(i);
someFunctionThatTakesString(mc.someMethod());
}
}
}
This approach means that you can have someMethod return T, but makes it much harder to handle retrieving objects from the vector because you have to figure out what type they are before you can do anything with them (you're essentially rolling your own RTTI).
Don't.
Rethink why you need this in the first place. Maybe another approach could work better. Maybe something with callbacks or visitors. I don't know your objective here, so I can't really say what's appropriate.
Can you do a base class and have all other classes inherit from the base class.
And you can make a list that holds a list of base class elements.
Now this is more of a pseudo example, but I hope this way would solve your problem.
Example:
class Base:
{
}
class whatever:Base
{
}
class whatever2:Base
int main()
{
list<whatever> object1;
list<whatever2> object2;
list<list<Base>> mainObj;
mainObj.push_back(object1);
mainObj.push_back(object2);
}
Now if the problem is to just have different datatypes than abstract datatypes in some container. Can't you have a Singly Link List, and have your Node generic.
Example:
template<typenameT>
struct Node
{
T data;
Node* next;
}
class LinkList
{
//Your code:
}

How to access derived objects in base class container without direct inheritance

I know I can cast pointer to base class in a container to pointer to derived class with the help of static_cast<derived_class*>(base_class_ptr).
However what can I do if they are not directly related, but only have a common child.
Look at the following example:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class Item
{
public:
Item(std::string name): _name(name) { /* */ };
private:
std::string _name;
};
class Readable
{
public:
Readable(std::string content): _content(content) { /* */ };
virtual auto content(void) const -> std::string
{
return this->_content;
}
private:
std::string _content;
};
class Book: public Item, public Readable
{
public:
Book(std::string name, std::string content): Item(name), Readable(content) { /* */ };
};
class Person
{
public:
auto read(const Readable& readable) const noexcept
{
std::cout << readable.content() << '\n';
}
};
int main(void)
{
auto i0 = std::make_unique<Item>("Pot");
auto i1 = std::make_unique<Item>("Shoe");
auto b0 = std::make_unique<Book>("Death of a Salesman", "Blablablabla...");
std::vector<std::unique_ptr<Item>> container;
container.emplace_back(std::move(i0));
container.emplace_back(std::move(i1));
container.emplace_back(std::move(b0));
Person jonnie{};
jonnie.read(static_cast<Readable*>(container[2].get())) // Error!
}
I want to read a Book from the Items container, and that should be ok, because it is inherits from Readable, but I can't, because the compiler complains:
static_cast from 'pointer' (aka 'Item *') to 'Readable *', which are not related by inheritance, is not allowed
What can I do in such a case? Is there a clean solution?
You are looking to cast objects around that are required to be polymorphic; they require a virtual method. The easiest (and I think in this case the best) is to make the destructor virtual for all the base classes, e.g.;
class Item
{
public:
virtual ~Item() { /* */ };
// .. the rest of the class
};
This allows dynamic_cast<> to work at runtime (it works with polymorphic types), it will check for and cast the object appropriately. One caveat here (given the sample use in the OP) is that you will almost certainly have to check the return value against nullptr to check the cast succeeded.
If you want to read a Book, you should cast it to a Book (which is derived from Item).
The Book can then be used as a Readable, beacuse it is. But the Item is not.