C++ class type array - c++

I am loading different types of derived classes from a file, which are of the form:
4-byte class ID header
n-byte serialized data
Each of the classes are inherited from a same base class, but I have some trouble creating them elegantly. This is my current code (here Foo and Bar are inherited from the same type):
// read class ID
uint32_t id = ReadHeader(myFile);
// create correct class
switch (id)
{
case ID_CLASS_FOO: myClass = new Foo(myFile); break;
case ID_CLASS_BAR: myClass = new Bar(myFile); break;
/* ... */
}
But I find this rather ugly, tedious and prone to error, since for every extra class I add, I need one extra define/enum member, and one additional line in the switch.
What I am looking for is something where I would declare a compile-time "type array" like such:
ClassTypes = {Foo, Bar, ...};
And then, when reading the file, just go:
myClass = new ClassTypes[id](myFile);
Is there some way to do this in C++?

You could create a factory class.
Factory definition:
typedef ClassType* (*ClassCreation(void))
class ClassFactory
{
private:
map<ClassId, ClassCreation> creators;
public:
ClassFactory()
{
creators[ID_CLASS_FOO] = &Foo::create;
creators[ID_CLASS_BAR] = &Bar::create;
}
ClassType* getClassType(ClassId id)
{
return (creators[id])()
}
};
class ClassType
{
//etc
};
class Foo : public ClassType
{
public:
static ClassType* create()
{
return new Foo;
}
};
class Bar : public ClassType
{
public:
static ClassType* create()
{
return new Bar;
}
};
Factory use:
ClassFactory factory;
uint32_t id = ReadHeader(myFile);
ClassType* myClass = factory.getClassType(id);

What about a static function in your base class that looks something like:
ClassTypes create(FileType myFile)
{
// read class ID
uint32_t id = ReadHeader(myFile);
// create correct class
switch (id)
{
case ID_CLASS_FOO: myClass = new Foo(myFile); break;
case ID_CLASS_BAR: myClass = new Bar(myFile); break;
/* ... */
}
return myClass;
}
At least that way, instead of
myClass = new ClassTypes[id](myFile);
you could do
myClass = ClassTypes::create(myFile);

Assuming your IDs go 0, 1, 2, 3, ..., What you could do is to create a std::map that maps each of the message ID to a function pointer to named constructor that creates the right kind of object for that ID.
class BaseClass {
private:
typedef (BaseClass*) (*NamedConstructor) (SomeType &);
// The map.
static std::map<int, NamedConstructor> id_to_constructor;
public:
// All the possible message types.
enum MessageType {
FooMsg = some_value,
BarMsg = some_other_value,
... // potentially a whole lot more
};
// Add a named constructor to the map.
static void add_handler (MessageType id, NamedConstructor cotr) {
// Error handling such as duplicates left as an exercise to the user.
id_to_constructor[id] = cotr;
}
// Function that applies the map.
static void handle_message (int id, SomeType & my_file) {
// Error handling such as a missing entry left as an exercise to the user.
NamedConstructor cotr = id_to_constructor[id];
cotr (my_file);
}
...
};
class Foo : public BaseClass {
public:
static BaseClass* create_foo (SomeType & my_file) {
return new Foo (my_file); // Or use a smart pointer.
}
// Member data and member functions elided.
...
};
class Bar : public BaseClass {
public:
static BaseClass* create_bar (SomeType & my_file) {
return new Bar (my_file); // Or use a smart pointer.
}
// Member data and member functions elided.
...
};
You'll need some mechanism to register the named constructors Foo::create_foo(), Bar::create_bar(), etc. using the BaseClass method add_handler. If you have 500 message types, that's 500 lines of code, but it will be straight line (no if, no switch) code.
The alternative is a switch statement with 500 cases. Yech.
So why a map rather than an vector? If you know the IDs will go 0, 1, 2, ..., a vector is fine. What if you have gaps? What if by design you have big huge gaps? For example, the message IDs might be Hamming-encoded so as to reduce errors.

Related

How would you design a function that attaches children of a Component class to an Object class if the components can hold different data?

In my program, I have an Object class to which we can attach components that all derive from a base Component class. Since a component can have data that are initialized through it's constructor, when we call Object::addComponent() we need to pass the data for this particular component
#include <vector>
class Component;
class Object
{
public:
Object() {}
/* The challenge comes from implementing this function, the signature will change later in this post
and ideally it would return the added component */
void addComponent();
private:
std::vector<Component*> m_components;
};
class Component
{
public:
Component(Object* owner) : m_owner(owner) {}
// Note the pure virtual destructor, meaning Component is meant to be derived
virtual ~Component() = 0;
private:
Object* m_owner;
};
Here are two Component derived classes for our example
// This component holds an int
class ComponentDerivedA : public Component
{
public:
ComponentDerivedA(Object* owner, int data) : Component(owner), m_data(data) {}
virtual ~ComponentDerivedA() override {}
private:
int m_data;
};
// This component holds a string
class ComponentDerivedB : public Component
{
public:
ComponentDerivedB(Object* owner, char* message) : Component(owner), m_message(message) {}
virtual ~ComponentDerivedB() override {}
private:
char* message;
};
The only solution I came for to implement the addComponent() function the way I want to is to create an enum and a struct:
enum class ComponentType { A, B };
struct ComponentInfo
{
ComponentType type;
int data;
char* message
};
So we change the signature for Object::addComponent() to the following
void* Object::addComponent(const ComponentInfo& info);
And here is a possible implementation
void* Object::addComponent(const ComponentInfo& info)
{
switch(info.type)
{
case A:
{
// We ignore cleaning up for this example but it would go in the destructor
ComponentDerivedA* a = new ComponentDerivedA(this, info.data);
m_components.push_back(a);
return a;
}
break;
case B:
{
// We ignore cleaning up for this example but it would go in the destructor
ComponentDerivedB* b = new ComponentDerivedB(this, info.message);
m_components.push_back(b);
return b;
}
break;
}
}
And here is how we would use it
int main()
{
Object obj;
ComponentInfo info{0};
info.type = ComponentType::A;
info.data = 5;
obj.addComponent(info);
/*
If I wanted to use the component right after I would have to do:
reinterpret_cast<ComponentDerivedA>(obj.addComponent(info)).doSomething();
*/
return 0;
}
This solution works okay but anytime you want to call addComponent() you have to declare a struct before, the only benefit is if you add multiple components you can reuse the same struct and just change the data between each call, also is if you add a new component type you have to expand the enum and the function, if you have many component the switch can become very large but the code inside it remains pretty repetitive and simple. And the final flaw is that it's up to the caller to cast the return value as the component type.
Here is a possible solution.
Note the use of std::unique_ptr instead of regular c-pointers.
#include <memory>
#include <vector>
struct Component{};
class Object {
public:
Object() {}
template <typename TDerivedComponent, typename... TArgs>
TDerivedComponent * addComponent(TArgs&&... args){
auto ptr = std::make_unique<TDerivedComponent>
(std::forward<TArgs>(args)...);
TDerivedComponent * result = ptr.get();
m_components.push_back(std::move(ptr));
return result;
}
private:
std::vector<std::unique_ptr<Component> > m_components;
};
struct ComponentA : public Component {
ComponentA(int x, int y){}
};
struct ComponentB : public Component {};
struct Invalid{};
int main(){
Object obj;
ComponentA * a = obj.addComponent<ComponentA>(1, 2);
ComponentB * b = obj.addComponent<ComponentB>();
// obj.addComponent<Invalid>(); -> error!
}
You actually can store a derived object into a baseclass object pointer which u already have declared as in the vector.
To make your derived objects also call the correct methods you have to declare the method as virtual inside the base class.
your addComponent() function can then take the pointer of the baseClass
addComponent(Component * c)
with that signature it can also take derived object pointers.
You should read about static vs dynamic binding in C++.

Dynamic lists and polymorphism

I have a map of type < lookup_ID, vector< parentclass*>> each location in the map holds a vector of type child class. The idea behind this system is the ability to add a child into its associated map using an add(parentclass*) function and it would be able to find its associated vector of child type. I tried using templates and casting to get the vector to recognized the type of child input into the add function with no luck. I don't want to have to declare an add function for each child of parent, and no matter how I did it I had to declare a function for each type. I could take them out of the map but then again I'm left with the issue of calling each child for any function I want to implement. Is their no way to match types of polymorphic structures into dynamically allocated lists?`
class Piece
{
public:
pieceType ID;
//...
}
class Infantry :
public Piece
{
public:
//...
};
class Artillery :
public Piece
{
public:
//...
};
//...
//In some other classes somewhere
std::map<pieceType, std::vector<Piece*>*> units;
units.emplace(Infantry_, new std::vector<Infantry*>);
units.emplace(Artillery_, new std::vector<Artillery*>);
//...
template<typename T>
std::vector<T*> operator+(std::vector<T*> a, Piece * b) {
a.push_back(static_cast<T*>(b));
return a;
}
add(Piece * piece){
units.at(piece->ID) = units.at(piece->ID) + piece;
}
Also I am aware that this code has some errors, it was more for an example of what i'm trying to say.
You have to use virtual funciton to get the ID for each child class
class Piece
{
public:
virtual PieceType ID() const = 0;
}
class Artillery
{
public:
virtual PieceType ID() const override { /* return your ID for Artillery */ }
}
class Infantery
{
public:
virtual PieceType ID() const override { /* return your ID for Infantery */ }
}
There's no relation between std::vector<Piece*> and either of std::vector<Infantry*> or std::vector<Artillery*>, so your map can only contain std::vector<Piece*>s.
This is for good reason. Imagine you have a std::vector<Infantry*>, and put a pointer to it into your std::map<pieceType, std::vector<Piece*>*>. You could then insert an Artillery * into that through the map.
Rather than exposing the std::vector<Piece*> directly, you could expose a (read only) view of it that casts to the particular subtype.
Using the ranges library
auto asInfantry = ranges::view::transform([](Piece * p){ return static_cast<Infantry *>(p); });
auto asArtillery = ranges::view::transform([](Piece * p){ return static_cast<Artillery *>(p); });
class PieceMap
{
std::map<pieceType, std::vector<Piece*>> units;
public:
auto Infantry() { return units.at(Infantry_) | asInfantry; }
auto Artillery() { return units.at(Artillery_) | asArtillery; }
};

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.

Creating C++ classes using an array of class definitions

I am writing a program that reads documents from a data base and turns the contents into C++ class instantiations. In each document there is a "type" field that I am mapping to a particular C++ class. Basically I read the document, construct an object of the corresponding class, and fill out the fields of the class object. I have maybe 10-15 different types of classes because the types of data in each document may be different, although some of the types are common. The common types of data are reflected in base/derived class definitions. Right now I have a switch statement with case statements corresponding to the different types of objects and in each case statement I create the right kind of object and then fill it in. It is a little messy though.
I was wondering if there is a means to place the various class definitions in an array so that I can mostly get rid of the switch statement. My ideal code would look like:
auto * obj = new arrayOfClassDefinitions[typeofClassIndex]
where arrayOfClassDefinitions contains the names of a set of classes. The obj pointer would then point to a new instantiation of the desired class.
I recall I could do things like this in Python but I am not sure about C++.
any ideas??
If you can organize all your classes into one hierarchical tree, you could use Abstract factory pattern to get similar result. This is example:
#include <map>
#include <memory>
class IBaseClass {
public:
virtual ~IBaseClass(){}
};
class A : public IBaseClass {
public:
// ....
};
class B : public IBaseClass {
public:
// ....
};
class C : public IBaseClass {
public:
// ....
};
class IFactory {
public:
virtual ~IFactory(){}
virtual IBaseClass* create() = 0;
};
template <class T>
class SimpleFactory :
public IFactory {
public:
IBaseClass* create() override {
return new T();
}
};
template <typename T>
std::shared_ptr<IFactory> make_factory() {
return std::make_shared<SimpleFactory<T> >();
}
int main(int, char**)
{
std::map<int, std::shared_ptr<IFactory>> factories = {
{1, make_factory<A>()},
{2, make_factory<B>()},
{3, make_factory<C>()}
};
// ....
int type_index = 1;
auto obj = factories[type_index]->create();
// ...
// don't forget to delete
delete obj;
return 0;
}

Setting value from Derivered class, while accesing same value from base class

I am getting an issue for retrieving BaseClass correct enum value.
class BaseClass
{
public:
enum EntityId {
EN_NONE = 0,
EN_PLAYER = 1,
EN_PLATFORM,
EN_GROUND,
EN_OBSTACLE,
EN_OTHER
};
void setEntityId(EntityId id) { _Entityid = id; }
EntityId getEntityId() { return _Entityid; }
protected:
EntityId _Entityid;
};
and
class DeriveredClassA : public SomeClass, public BaseClass {....};
class DeriveredClassB : public SomeClass, public BaseClass {....};
The initialization goes like this
DeriveredClassA->setEntityId(BaseClass::EntityId::EN_PLAYER);
DeriveredClassB->setEntityId(BaseClass::EntityId::EN_OBSTACLE);
Which is placed into a different vector list correspoinding to that enum.
However, I am forced to use void* to do static_casts cats...
Like this:
BaseClass* EA = static_cast<BaseClass*>(bodyUserDataA); //bodyUserDataA and bodyUserDataB are both void*
BaseClass* EB = static_cast<BaseClass*>(bodyUserDataB);
And I am trying to retrieve using EA->getEntityId() and EB->getEntityId() so I could check which one is EN_PLAYER, which one is EN_GROUND and etc. So then I could up-class from base into derivered class and do other stuff with it.
Tried using with virtual, however somehow I am receiving 2 copies of _EntityID, which can be either the same or DIFFERENT between my Derivered and BaseClass of that one object.
Moreover, I can't cast right away into DeriveredClass, since the code checking would be huge, due to many different types of DeriveredClass'es (DeriveredClassA, DeriveredClassB, DeriveredClassC, DeriveredClassD) with their corresponding vector list.
My question is that How I need setup correctly both Base and Derivered class, so that I could access _EntityID from Baseclass which is the same of that DeriveredClass? My main problem might is that I used incorectly virtual functions, so I left on default to understand my issue.
P.S. This is mainly my c++ issue, other tags are added due to I am using game engine and physics engine for this case.
I believe that you want your code to look more like this:
class Entity
{
public:
enum Type {
EN_NONE = 0,
EN_PLAYER = 1,
EN_PLATFORM,
EN_GROUND,
EN_OBSTACLE,
EN_OTHER
};
Type getType() { return _type; }
protected:
Entity(Type type): _type(type) {}
private:
const Type _type;
};
Then your derived classes and usage of this base would be more like:
class PlayerEntity: public Entity, public SomeClass
{
public:
PlayerEntity(std::string name): Entity(EN_PLAYER), _name(name) {}
std::string getName() const { return _name; }
private:
std::string _name;
};
class PlatformEntity: public Entity, public SomeClass
{
public:
PlatformEntity(): Entity(EN_PLATFORM) {}
};
Initialization is then done like:
int main()
{
PlatformEntity platform;
std::vector<PlatformEntity> platforms(platform);
std::vector<PlayerEntity> players;
players.emplace_back("Bob");
players.emplace_back("Alice");
players.emplace_back("Ook");
}
Access from user-data could then look like this:
// bodyUserDataA and bodyUserDataB are both void*
Entity* const EA = static_cast<Entity*>(bodyUserDataA);
Entity* const EB = static_cast<Entity*>(bodyUserDataB);
switch (EA->getType())
{
case Entity::EN_PLAYER:
{
PlayerEntity* player = static_cast<PlayerEntity*>(EA);
std::cout << "Found player: " << player->getName();
break;
}
case Entity::EN_OTHER:
...
default:
break;
}