Efficient way of matching entities to systems in an entity component system - c++

I'm working on a data-oriented entity component system where component types and system signatures are known at compile-time.
An entity is an aggregate of components. Components can be added/removed from entities at run-time.
A component is a small logic-less class.
A signature is a compile-time list of component types. An entity is said to match a signature if it contains all component types required by the signature.
A short code sample will show you how the user syntax looks and what the intended usage is:
// User-defined component types.
struct Comp0 : ecs::Component { /*...*/ };
struct Comp1 : ecs::Component { /*...*/ };
struct Comp2 : ecs::Component { /*...*/ };
struct Comp3 : ecs::Component { /*...*/ };
// User-defined system signatures.
using Sig0 = ecs::Requires<Comp0>;
using Sig1 = ecs::Requires<Comp1, Comp3>;
using Sig2 = ecs::Requires<Comp1, Comp2, Comp3>;
// Store all components in a compile-time type list.
using MyComps = ecs::ComponentList
<
Comp0, Comp1, Comp2, Comp3
>;
// Store all signatures in a compile-time type list.
using MySigs = ecs::SignatureList
<
Sig0, Sig1, Sig2
>;
// Final type of the entity manager.
using MyManager = ecs::Manager<MyComps, MySigs>;
void example()
{
MyManager m;
// Create an entity and add components to it at runtime.
auto e0 = m.createEntity();
m.add<Comp0>(e0);
m.add<Comp1>(e0);
m.add<Comp3>(e0);
// Matches.
assert(m.matches<Sig0>(e0));
// Matches.
assert(m.matches<Sig1>(e0));
// Doesn't match. (`Comp2` missing)
assert(!m.matches<Sig2>(e0));
// Do something with all entities matching `Sig0`.
m.forEntitiesMatching<Sig0>([](/*...*/){/*...*/});
}
I'm currently checking if entities match signatures using std::bitset operations. The performance, however, quickly degrades as soon as the number of signatures and the number of entities increase.
Pseudocode:
// m.forEntitiesMatching<Sig0>
// ...gets transformed into...
for(auto& e : entities)
if((e.bitset & getBitset<Sig0>()) == getBitset<Sig0>())
callUserFunction(e);
This works, but if the user calls forEntitiesMatching with the same signature multiple times, all entities will have to be matched again.
There may also be a better way of pre-caching entities in cache-friendly containers.
I've tried using some sort of cache that creates a compile-time map (implemented as std::tuple<std::vector<EntityIndex>, std::vector<EntityIndex>, ...>), where the keys are the signature types (every signature type has a unique incremental index thanks to SignatureList), and the values are vectors of entity indices.
I filled the cache tuple with something like:
// Compile-time list iterations a-la `boost::hana`.
forEveryType<SignatureList>([](auto t)
{
using Type = decltype(t)::Type;
for(auto entityIndex : entities)
if(matchesSignature<Type>(e))
std::get<idx<Type>()>(cache).emplace_back(e);
});
And cleared it after every manager update cycle.
Unfortunately it performed more slowly than then "raw" loop shown above in all of my tests. It also would have a bigger issue: what if a call to forEntitiesMatching actually removes or adds a component to an entity? The cache would have to be invalidated and recalculated for subsequent forEntitiesMatching calls.
Is there any faster way of matching entities to signatures?
A lot of things are known at compile-time (list of component types, list of signature types, ...) - is there any auxiliary data structure that could be generated at compile-time which would help with "bitset-like" matching?

For matching, are you checking for each component type one bit at a time? You should be able to plow through the entities by checking if all components for a signature are available in one instruction against a bit mask.
For example, we might use:
const uint64_t signature = 0xD; // 0b1101
... to check for the presence of components 0, 1, and 3.
for(const auto& ent: entities)
{
if (ent.components & signature)
// the entity has components 0, 1, and 3.
}
That should be fast as hell if your entities are stored contiguously in an array. It doesn't matter, performance-wise, whether you are checking for 3 component types or 50 component types at a time. I don't use this type of rep for my ECS, but definitely this shouldn't take long at all even if you have a million entities. That should complete in the blink of an eye.
It's pretty much the fastest practical way achievable to see what entities provide a given set of components while storing the minimal amount of state -- only reason I don't use this rep is because my ECS revolves around a plugin architecture where people register new component types and systems at runtime through plugins and scripts so I can't effectively anticipate an upper bound on how many component types there will be. If I had a compile-time system like yours that is designed to anticipate all this stuff in advance, then definitely I think this is the way to go. Just don't check one bit at a time.
You should be able to easily process a million components several hundred times a second using the above solution. There are people achieving similar rates applying CPU image filters processing that many pixels multiplied by that many times per second, and those filters have far more work to do than a single bitwise and and a single branch per iteration.
I also wouldn't even bother caching this super cheap sequential loop unless you have some systems that only want to process, say, a dozen out of a million entities. But at that point you can potentially cache those rare case scenarios where one system barely processes any entities local to the system instead of trying to cache things centrally. Just make sure systems that are interested can find out when entities are added to the system or removed from the system so that they can invalidate their local cache.
Also you don't necessarily need fancy metaprogramming for these signatures. At the end of the day you aren't really saving anything using template metaprogramming since it can't avoid looping through entities checking for something since the list of entities is only known at runtime. There's not really theoretically anything worth optimizing away at compile-time here. You can just do like:
static const uint64_t motion_id = 1 << 0;
static const uint64_t sprite_id = 1 << 1;
static const uint64_t sound_id = 1 << 2;
static const uint64_t particle_id = 1 << 3;
...
// Signature to check for entities with motion, sprite, and
// particle components.
static const uint64_t sig = motion_id | sprite_id | particle_id;
If you use entity-associated bits to indicate what components an entity has, I recommend setting an upper bound on the total number of component types your system can handle (ex: 64 is probably plenty, 128 is a boatload) so that you can just check for components against bitmasks like these in one go.
[...] what if a call to forEntitiesMatching actually removes or adds a
component to an entity?
If, say, you have a system which is adding/removing components every frame, then I wouldn't even bother to cache in the first place. The above version should be able to loop through entities super fast.
The worst-case scenario of looping through all entities sequentially is if you have a system that, say, is only going to process 3% of those entities. If your engine design has systems like that, then it's a bit awkward, but you might just notify them when components are added/removed that they are specifically interested in at which point they can invalidate their entity cache and then recache the next time the system kicks in. Hopefully you don't have a system adding/removing components every single frame of the types that are that 3% minority of components. If you do have that worst-case scenario though, probably best not to bother with caching at all. There's no use for a cache which is just going to be discarded every frame and trying to update it in a fancy way is probably not going to net you much.
Other systems which, say, process 50% or more of the entities probably shouldn't even bother caching, since the level of indirection is probably not worth it over just plowing through all the entities sequentially and doing a dirt cheap bitwise and over each one.

Depending on the ratio of add/remove of components against signature matching, you can try to build a kind of prefix tree storing references to entities.
The tree itself is static, only leaves which contains entities are runtime built containers.
This way when adding (or removing) a components, you just need to move the reference of the entity to the correct leaf.
When searching for entities matching a signature, you just have to take all the union of the leaves subsuming the signature and iterate over them. And since the tree is (almost) static, you don't even need to search for these leaves.
Another nice point: you bitset can be used to represent the path in the tree so moving an entity is pretty easy.
If the number of components induces an unrealistic number leaves, and not all combination of components are used, you can replace the tree with a hashtable where the bitset is the key and the value is the set of entity references.
That's more algorithmic ideas than concrete stuff, but it seems more reasonable than iterating over the set of entities.

Having a sparse integer set per signature type is the theoretically best solution (in terms of time complexity).
The sparse integer set data structure allows efficient contiguous O(N) iteration over the stored integers, O(1) insertion/removal of integers, and O(1) querying for a specific integer.
The per-signature sparse integer set would store all entity IDs associated with that specific signature.
Example: Diana, an open-source C and C++ ECS library, makes use of sparse integer set to keep track of entities in systems. Every system has its own sparse integer set instance.

Have you considered the following solution?
Each signature will have a container of entities that match that signature.
When a component is added or removed you need to update the relevant signature container.
Now the function can simply go to the signature entity container and execute the function for each entity.

Another option which is a bit influenced from #Marwan Burelle idea.
Each component will hold a sorted container of entities which have that component.
When looking for entities that match a signature you need to iterate over the component container of entities.
Adding or removing is O(nlogn) since it needs to be sorted. but you only need to add/remove it to/from a single container which will also contain fewer items.
Iterating over the items is a bit heavier since it is a factor of the amount of components and the number of entities in each component.
You still have an element of multiplying but the number of elements is again smaller.
I wrote down a simplified version as a POC.
Edit: My previous version had some bugs, now hopefully they are fixed.
// Example program
#include <iostream>
#include <string>
#include <set>
#include <map>
#include <vector>
#include <functional>
#include <memory>
#include <chrono>
struct ComponentBase
{
};
struct Entity
{
Entity(std::string&& name, uint id)
: _id(id),
_name(name)
{
}
uint _id;
std::string _name;
std::map<uint, std::shared_ptr<ComponentBase>> _components;
};
template <uint ID>
struct Component : public ComponentBase
{
static const uint _id;
static std::map<uint, Entity*> _entities;
};
template <uint ID>
std::map<uint, Entity*> Component<ID>::_entities;
template <uint ID>
const uint Component<ID>::_id = ID;
using Comp0 = Component<0>;
using Comp1 = Component<1>;
using Comp2 = Component<2>;
using Comp3 = Component<3>;
template <typename ...TComponents>
struct Enumerator
{
};
template <typename TComponent>
struct Enumerator<TComponent>
{
std::map<uint, Entity*>::iterator it;
Enumerator()
{
it = TComponent::_entities.begin();
}
bool AllMoveTo(Entity& entity)
{
while (HasNext() && Current()->_id < entity._id)
{
MoveNext();
}
if (!Current())
return false;
return Current()->_id == entity._id;
}
bool HasNext() const
{
auto it_next = it;
++it_next;
bool has_next = it_next != TComponent::_entities.end();
return has_next;
}
void MoveNext()
{
++it;
}
Entity* Current() const
{
return it != TComponent::_entities.end() ? it->second : nullptr;
}
};
template <typename TComponent, typename ...TComponents>
struct Enumerator<TComponent, TComponents...>
{
std::map<uint, Entity*>::iterator it;
Enumerator<TComponents...> rest;
Enumerator()
{
it = TComponent::_entities.begin();
}
bool AllMoveTo(Entity& entity)
{
if (!rest.AllMoveTo(entity))
return false;
while (HasNext() && Current()->_id < entity._id)
{
MoveNext();
}
if (!Current())
return false;
return Current()->_id == entity._id;
}
bool HasNext() const
{
auto it_next = it;
++it_next;
bool has_next = it_next != TComponent::_entities.end();
return has_next;
}
void MoveNext()
{
++it;
}
Entity* Current() const
{
return it != TComponent::_entities.end() ? it->second : nullptr;
}
};
template <typename ...TComponents>
struct Requires
{
};
template <typename TComponent>
struct Requires<TComponent>
{
static void run_on_matching_entries(const std::function<void(Entity&)>& fun)
{
for (Enumerator<TComponent> enumerator; enumerator.Current(); enumerator.MoveNext())
{
if (!enumerator.AllMoveTo(*enumerator.Current()))
continue;
fun(*enumerator.Current());
}
}
};
template <typename TComponent, typename ...TComponents>
struct Requires<TComponent, TComponents...>
{
static void run_on_matching_entries(const std::function<void(Entity&)>& fun)
{
for (Enumerator<TComponent, TComponents...> enumerator; enumerator.Current(); enumerator.MoveNext())
{
if (!enumerator.AllMoveTo(*enumerator.Current()))
continue;
fun(*enumerator.Current());
}
}
};
using Sig0 = Requires<Comp0>;
using Sig1 = Requires<Comp1, Comp3>;
using Sig2 = Requires<Comp1, Comp2, Comp3>;
struct Manager
{
uint _next_entity_id;
Manager()
{
_next_entity_id = 0;
}
Entity createEntity()
{
uint id = _next_entity_id++;
return Entity("entity " + std::to_string(id), id);
};
template <typename Component>
void add(Entity& e)
{
e._components[Component::_id] = std::make_shared<Component>();
Component::_entities.emplace(e._id, &e);
}
template <typename Component>
void remove(Entity& e)
{
e._components.erase(Component::_id);
Component::_entities.erase(e._id);
}
template <typename Signature>
void for_entities_with_signature(const std::function<void(Entity&)>& fun)
{
Signature::run_on_matching_entries(fun);
}
};
int main()
{
Manager m;
uint item_count = 100000;
std::vector<Entity> entities;
for (size_t item = 0; item < item_count; ++item)
{
entities.push_back(m.createEntity());
}
for (size_t item = 0; item < item_count; ++item)
{
//if (rand() % 2 == 0)
m.add<Comp0>(entities[item]);
//if (rand() % 2 == 0)
m.add<Comp1>(entities[item]);
//if (rand() % 2 == 0)
m.add<Comp2>(entities[item]);
//if (rand() % 2 == 0)
m.add<Comp3>(entities[item]);
}
size_t sig0_count = 0;
size_t sig1_count = 0;
size_t sig2_count = 0;
auto start = std::chrono::system_clock::now();
m.for_entities_with_signature<Sig0>([&](Entity& e) { ++sig0_count; });
m.for_entities_with_signature<Sig1>([&](Entity& e) { ++sig1_count; });
m.for_entities_with_signature<Sig2>([&](Entity& e) { ++sig2_count; });
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);
std::cout << "first run took " << duration.count() << " milliseconds: " << sig0_count << " " << sig1_count << " " << sig2_count << std::endl;
for (size_t item = 0; item < item_count; ++item)
{
if (rand() % 2 == 0)
m.remove<Comp0>(entities[item]);
if (rand() % 2 == 0)
m.remove<Comp1>(entities[item]);
if (rand() % 2 == 0)
m.remove<Comp2>(entities[item]);
if (rand() % 2 == 0)
m.remove<Comp3>(entities[item]);
}
sig0_count = sig1_count = sig2_count = 0;
start = std::chrono::system_clock::now();
m.for_entities_with_signature<Sig0>([&](Entity& e) { ++sig0_count; });
m.for_entities_with_signature<Sig1>([&](Entity& e) { ++sig1_count; });
m.for_entities_with_signature<Sig2>([&](Entity& e) { ++sig2_count; });
duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);
std::cout << "second run took " << duration.count() << " milliseconds: " << sig0_count << " " << sig1_count << " " << sig2_count << std::endl;
}

Regarding the pseudo code:
for(auto& e : entities)
for(const auto& s : signatures)
if((e.bitset & s.bitset) == s.bitset)
callUserFunction(e);
I am unsure why you need the inner loop.
If you have the requested signature in the function then you can get the bitset of that signature and there is no need to iterate over all of the signatures.
template <typename T>
void forEntitiesMatching(const std::function<void(Entity& e)>& fun)
{
for(auto& e : entities)
if((e.bitset & T::get_bitset()) == T::get_bitset())
fun(e);
}

Related

Mechanism for Save/Load+Undo/Redo with minimum boilerplate

I want to make an app where a user can edit a diagram (for example), which would provide the standard mechanisms of: Save, Load, Undo, and Redo.
A simple way to do it is to have classes for the diagram and for the various shapes in it, which implement serialization via save and load methods, and where all methods to edit them return UndoableActions that can be added to an UndoManager which calls their perform method and adds them to an undo stack.
The problem with the simple way described above is that it requires a lot of error-prone boilerplate work.
I know that the serialization (save/load) part of the work can be solved by using something like Google's Protocol Buffers or Apache Thrift, which generates the boiler-plate serialization code for you, but it doesn't solve the undo+redo problem. I know that for Objective C and Swift, Apple provides Core Data which does solve serialization + undo, but I'm not familiar with anything similar for C++.
Is there a good way non-error-prone to solve save+load+undo+redo with little boilerplate?
The problem with the simple way described above is that it requires a lot of error-prone boilerplate work.
I am not convinced that this is the case. Your approach sounds reasonable and using Modern C++ features and abstractions you can implement a safe and elegant interface for it.
For starters, you could use std::variant as a sum type for "undoable actions" - this will give you a type-safe tagged union for every action. (Consider using boost::variant or other implementations that can be easily found on Google if you do not have access to C++17). Example:
namespace action
{
// User dragged the shape to a separate position.
struct move_shape
{
shape_id _id;
offset _offset;
};
// User changed the color of a shape.
struct change_shape_color
{
shape_id _id;
color _previous;
color _new;
};
// ...more actions...
}
using undoable_action = std::variant<
action::move_shape,
action::change_shape_color,
// ...
>;
Now that you have a sum type for all your possible "undoable actions", you can define undo behavior by using pattern matching. I wrote two articles on variant "pattern matching" by overloading lambdas that you could find interesting:
"visiting variants using lambdas - part 1"
"visiting variants using lambdas - part 2"
Here's an example of how your undo function could look like:
void undo()
{
auto action = undo_stack.pop_and_get();
match(action, [&shapes](const move_shape& y)
{
// Revert shape movement.
shapes[y._id].move(-y._offset);
},
[&shapes](const change_shape_color& y)
{
// Revert shape color change.
shapes[y._id].set_color(y._previous);
},
[](auto)
{
// Produce a compile-time error.
struct undo_not_implemented;
undo_not_implemented{};
});
}
If every branch of match gets large, it could be moved to its own function for readability. Trying to instantiate undo_not_implemented or using a dependent static_assert is also a good idea: a compile-time error will be produced if you forget to implement behavior for a specific "undoable action".
That's pretty much it! If you want to save the undo_stack so that the history of actions is preserved in saved documents, you can implement a auto serialize(const undoable_action&) that, again, uses pattern matching to serialize the various actions. You could then implement a deserialize function that repopulates the undo_stack on file load.
If you find implementing serialization/deserialization for every action too tedious, consider using BOOST_HANA_DEFINE_STRUCT or similar solutions to automatically generate serialization/deserialization code.
Since you're concerned about battery and performance, I would also like to mention that using std::variant or similar tagged union constructs is on average faster and more lightweight compared to polymorphic hierarchies, as heap allocation is not required and as there is no run-time virtual dispatch.
About redo functionality: you could have a redo_stack and implement an auto invert(const undoable_action&) function that inverts the behavior of an action. Example:
void undo()
{
auto action = undo_stack.pop_and_get();
match(action, [&](const move_shape& y)
{
// Revert shape movement.
shapes[y._id].move(-y._offset);
redo_stack.push(invert(y));
},
// ...
auto invert(const undoable_action& x)
{
return match(x, [&](move_shape y)
{
y._offset *= -1;
return y;
},
// ...
If you follow this pattern, you can implement redo in terms of undo! Simply call undo by popping from the redo_stack instead of the undo_stack: since you "inverted" the actions it will perform the desired operation.
EDIT: here's a minimal wandbox example that implements a match function that takes in a variant and returns a variant.
The example uses boost::hana::overload to generate the visitor.
The visitor is wrapped in a lambda f that unifies the return type to the type of the variant: this is necessary as std::visit requires that the visitor always returns the same type.
If returning a type which is different from the variant is desirable, std::common_type_t could be used, otherwise the user could explicitly specify it as the first template parameter of match.
Two reasonable approaches to this problem are implemented in the frameworks Flip and ODB.
Code-generation / ODB
With ODB you need to add #pragma declarations to your code, and have it's tool generate methods that you use for save/load and for editing the model, like so:
#pragma db object
class person
{
public:
void setName (string);
string getName();
...
private:
friend class odb::access;
person () {}
#pragma db id
string email_;
string name_;
};
Where the accessors declared in the class are auto-generated by ODB so that all changes to the model can get captured and undo-transactions may be made for them.
Reflection with minimal boilerplate / Flip
Unlike ODB, Flip doesn't generate C++ code for you, but rather it requires your program to call Model::declare to re-declare your structures like so:
class Song : public flip::Object
{
public:
static void declare ();
flip::Float tempo;
flip::Array <Track> tracks;
};
void Song::declare ()
{
Model::declare <Song> ()
.name ("acme.product.Song")
.member <flip::Float, &Song::tempo> ("tempo");
.member <flip::Array <Track>, &Song::tracks> ("tracks");
}
int main()
{
Song::declare();
...
}
With the structured declared like so, flip::Object's constructor can initialize all the fields so that they can point to the undo stack, and all the edits on them are recorded. It also has a list of all the members so that flip::Object can implement the serialization for you.
The problem with the simple way described above is that it requires a lot of error-prone boilerplate work.
I would say that the actual problem is that your undo/redo logic is part of a component, that should ship only a bunch of data as a position, a content and so on.
A common OOP way to decouple the undo/redo logic from the data is the command design pattern.
The basic idea is that all the user interactions are converted to commands and those commands are executed on the diagram itself. They contain all the information required to perform an operation and to rollback it, as long as you maintain a sorted list of commands and undo/redo them in order (that is usually the user expectation).
Another common OOP pattern that can help you either to design a custom serialization utility or to use the most common ones is the visitor design pattern.
Here the basic idea is that your diagram should not care about the kind of components it contains. Whenever you want to serialize it, you provide a serializer and the components promote themselves to the right type when queried (see double dispatching for further details on this technique).
That being said, a minimal example is worth more than a thousand words:
#include <memory>
#include <stack>
#include <vector>
#include <utility>
#include <iostream>
#include <algorithm>
#include <string>
struct Serializer;
struct Part {
virtual void accept(Serializer &) = 0;
virtual void draw() = 0;
};
struct Node: Part {
void accept(Serializer &serializer) override;
void draw() override;
std::string label;
unsigned int x;
unsigned int y;
};
struct Link: Part {
void accept(Serializer &serializer) override;
void draw() override;
std::weak_ptr<Node> from;
std::weak_ptr<Node> to;
};
struct Serializer {
void visit(Node &node) {
std::cout << "serializing node " << node.label << " - x: " << node.x << ", y: " << node.y << std::endl;
}
void visit(Link &link) {
auto pfrom = link.from.lock();
auto pto = link.to.lock();
std::cout << "serializing link between " << (pfrom ? pfrom->label : "-none-") << " and " << (pto ? pto->label : "-none-") << std::endl;
}
};
void Node::accept(Serializer &serializer) {
serializer.visit(*this);
}
void Node::draw() {
std::cout << "drawing node " << label << " - x: " << x << ", y: " << y << std::endl;
}
void Link::accept(Serializer &serializer) {
serializer.visit(*this);
}
void Link::draw() {
auto pfrom = from.lock();
auto pto = to.lock();
std::cout << "drawing link between " << (pfrom ? pfrom->label : "-none-") << " and " << (pto ? pto->label : "-none-") << std::endl;
}
struct TreeDiagram;
struct Command {
virtual void execute(TreeDiagram &) = 0;
virtual void undo(TreeDiagram &) = 0;
};
struct TreeDiagram {
std::vector<std::shared_ptr<Part>> parts;
std::stack<std::unique_ptr<Command>> commands;
void execute(std::unique_ptr<Command> command) {
command->execute(*this);
commands.push(std::move(command));
}
void undo() {
if(!commands.empty()) {
commands.top()->undo(*this);
commands.pop();
}
}
void draw() {
std::cout << "draw..." << std::endl;
for(auto &part: parts) {
part->draw();
}
}
void serialize(Serializer &serializer) {
std::cout << "serialize..." << std::endl;
for(auto &part: parts) {
part->accept(serializer);
}
}
};
struct AddNode: Command {
AddNode(std::string label, unsigned int x, unsigned int y):
label{label}, x{x}, y{y}, node{std::make_shared<Node>()}
{
node->label = label;
node->x = x;
node->y = y;
}
void execute(TreeDiagram &diagram) override {
diagram.parts.push_back(node);
}
void undo(TreeDiagram &diagram) override {
auto &parts = diagram.parts;
parts.erase(std::remove(parts.begin(), parts.end(), node), parts.end());
}
std::string label;
unsigned int x;
unsigned int y;
std::shared_ptr<Node> node;
};
struct AddLink: Command {
AddLink(std::shared_ptr<Node> from, std::shared_ptr<Node> to):
link{std::make_shared<Link>()}
{
link->from = from;
link->to = to;
}
void execute(TreeDiagram &diagram) override {
diagram.parts.push_back(link);
}
void undo(TreeDiagram &diagram) override {
auto &parts = diagram.parts;
parts.erase(std::remove(parts.begin(), parts.end(), link), parts.end());
}
std::shared_ptr<Link> link;
};
struct MoveNode: Command {
MoveNode(unsigned int x, unsigned int y, std::shared_ptr<Node> node):
px{node->x}, py{node->y}, x{x}, y{y}, node{node}
{}
void execute(TreeDiagram &) override {
node->x = x;
node->y = y;
}
void undo(TreeDiagram &) override {
node->x = px;
node->y = py;
}
unsigned int px;
unsigned int py;
unsigned int x;
unsigned int y;
std::shared_ptr<Node> node;
};
int main() {
TreeDiagram diagram;
Serializer serializer;
auto addNode1 = std::make_unique<AddNode>("foo", 0, 0);
auto addNode2 = std::make_unique<AddNode>("bar", 100, 50);
auto moveNode2 = std::make_unique<MoveNode>(10, 10, addNode2->node);
auto addLink = std::make_unique<AddLink>(addNode1->node, addNode2->node);
diagram.serialize(serializer);
diagram.execute(std::move(addNode1));
diagram.execute(std::move(addNode2));
diagram.execute(std::move(addLink));
diagram.serialize(serializer);
diagram.execute(std::move(moveNode2));
diagram.draw();
diagram.undo();
diagram.undo();
diagram.serialize(serializer);
}
I've not implemented the redo action and the code is far from being a production-ready piece of software, but it acts quite well as a starting point from which to create something more complex.
As you can see, the goal is to create a tree diagram that contains both nodes an links. A component contains a bunch of data and knows how to draw itself. Moreover, as anticipated, a component accepts a serializer in case you want to write it down on a file or whatever.
All the logic is contained in the so called commands. In the example there are three commands: add node, add link and move node. Neither the diagram nor the components know anything about what's going on under the hood. All what the diagram knows is that it's executing a set of commands and those commands can be executed back a step at the time.
A more complex undo/redo system can contain a circular buffer of commands and a few indexes that indicate the one to substitute with the next one, the one valid when going forth and the one valid when going back.
It's quite easy to implement indeed.
This approach will help you decoupling the logic from the data and it's quite common when dealing with user interfaces.
To be honest, it's not something that came up suddenly to my mind. I found something similar while looking at how open-source software solved the issue and I've used it a few years ago in a software of mine. The resulting code is really easy to maintain.
Another approach you might want to consider is working with inmutable data structures and objects. Then, the undo/redo stack can be implemented as a stack of versions of the scene/diagram/document. Undo() replaces the current version with an older version from the stack, and so on. Because all data is inmutable, you can keep references instead of copies, so it is fast and (relatively) cheap.
Pros:
simple undo/redo
multithread-friendly
clean separation of "structure" and transient state (e.g. current selection)
may simplify serialization
caching/memoization/precomputation-friendly (e.g. bounding-box, gpu buffers)
Cons:
consumes a bit more memory
forces separation of "structure" and transient state
probably more difficult: for example, for a typical tree-like scenegraph, to change a node you would also need to change all the nodes along the path to the root; the old and new versions can share the rest of the nodes
Assuming that you're calling save() on a temporary file for each edit of the diagram (even if user doesn't explicitly call the save action) and that you undo only the latest action, you can do as follows:
LastDiagram load(const std::string &path)
{
/* Check for valid path (e.g. boost::filesystem here) */
if(!found)
{
throw std::runtime_exception{"No diagram found"};
}
//read LastDiagram
return LastDiagram;
}
LastDiagram undoLastAction()
{
return loadLastDiagram("/tmp/tmp_diagram_file");
}
and in your main app you handle the exception if thrown. In case you want to allow more undos, then you should think to a solution like sqlite or a tmp file with more entries.
If performance in time and space are issues due large diagrams, think to implement some strategy like keeping an incremental difference for each element of a diagram in a std::vector (limit it to 3/5 if objects are big) and call the renderer with the current statuses. I'm not an OpenGL expert, but I think it's the way it's done there. Actually you could 'steal' this strategy from game development best practices, or generally graphics related ones.
One of those strategies could be something like this:
A structure for efficient update, incremental redisplay and undo in graphical editors

What is the "correct OOP" way to deal with a storage pool of items of mixed types?

This was inspired by a comment to my other question here:
How do you "not repeat yourself" when giving a class an accessible "name" in C++?
nvoight: "RTTI is bad because it's a hint you are not doing good OOP. Doing your own homebrew RTTI does not make it better OOP, it just means you are reinventing the wheel on top of bad OOP."
So what is the "good OOP" solution here? The problem is this. The program is in C++, so there are also C++ specific details mentioned below. I have a "component" class (actually, a struct), which is subclassed into a number of different derived classes containing different kinds of component data. It's part of an "entity component system" design for a game. I'm wondering about the storage of the components. In particular, the current storage system has:
a "component manager" which stores an array, actually a hash map, of a single type of component. The hash map allows for lookup of a component by the entity ID of the entity it belongs to. This component manager is a template which inherits from a base, and the template parameter is the type of component to manage.
a full storage pack which is a collection of these component managers, implemented as an array of pointers to the component manager base class. This has methods to insert and extract an entity (on insertion, the components are taken out and put into the managers, on removal, they are extracted and collected into a new entity object), as well as ones to add new component managers, so if we want to add a new component type to the game, all we have to do is put another command to insert a component manager for it.
It's the full storage pack that prompted this. In particular, it offers no way of accessing a particular type of component. All the components are stored as base class pointers with no type information. What I thought of was using some kind of RTTI and storing the component managers in a map which maps type names and thus allows for lookup and then the proper downcasting of the base class pointer to the appropriate derived class (the user would call a template member on the entity storage pool to do this).
But if this RTTI means bad OOP, what would be the correct way to design this system so no RTTI is required?
Disclaimer/resources: my BCS thesis was about the design and implementation of a C++14 library for compile-time Entity-Component-System pattern generation. You can find the library here on GitHub.
This answer is meant to give you a broad overview of some techniques/ideas you can apply to implement the Entity-Component-System pattern depending on whether or not component/system types are known at compile-time.
If you want to see implementation details, I suggest you to check out my library (linked above) for an entirely compile-time based approach. diana is a very nice C library that can give you an idea of a run-time based approach.
You have several approaches, depending on the scope/scale of your project and on the nature of your entities/components/systems.
All component types and system types are known at compile-time.
This is the case analyzed in my BCS thesis - what you can do is use advanced metaprogramming techniques (e.g. using Boost.Hana) to put all component types and system types in compile-time lists and create data structures that link everything together at compile time. Pseudocode example:
namespace c
{
struct position { vec2f _v };
struct velocity { vec2f _v };
struct acceleration { vec2f _v };
struct render { sprite _s; };
}
constexpr auto component_types = type_list
{
component_type<c::position>,
component_type<c::velocity>,
component_type<c::acceleration>,
component_type<c::render>
};
After defining your components, you can define your systems and tell them "what components to use":
namespace s
{
struct movement
{
template <typename TData>
void process(TData& data, float ft)
{
data.for_entities([&](auto eid)
{
auto& p = data.get(eid, component_type<c::position>)._v;
auto& v = data.get(eid, component_type<c::velocity>)._v;
auto& a = data.get(eid, component_type<c::acceleration>)._v;
v += a * ft;
p += v * ft;
});
}
};
struct render
{
template <typename TData>
void process(TData& data)
{
data.for_entities([&](auto eid)
{
auto& p = data.get(eid, component_type<c::position>)._v;
auto& s = data.get(eid, component_type<c::render>)._s;
s.set_position(p);
some_context::draw(s);
});
}
};
}
constexpr auto system_types = type_list
{
system_type<s::movement,
uses
(
component_type<c::position>,
component_type<c::velocity>,
component_type<c::acceleration>
)>,
system_type<s::render,
uses
(
component_type<c::render>
)>
};
All that's left is using some sort of context object and lambda overloading to visit the systems and call their processing methods:
ctx.visit_systems(
[ft](auto& data, s::movement& s)
{
s.process(data, ft);
},
[](auto& data, s::render& s)
{
s.process(data);
});
You can use all the compile-time knowledge to generate appropriate data structures for components and systems inside the context object.
This is the approach I used in my thesis and library - I talked about it at C++Now 2016: "Implementation of a multithreaded compile-time ECS in C++14".
All component types and systems types are known at run-time.
This is a completely different situation - you need to use some sort of type-erasure technique to dynamically deal with components and systems. A suitable solution is using a scripting language such as LUA to deal with system logic and/or component structure (a more efficient simple component definition language can also be handwritten, so that it maps one-to-one to C++ types or to your engine's types).
You need some sort of context object where you can register component types and system types at run-time. I suggest either using unique incrementing IDs or some sort of UUIDs to identify component/system types. After mapping system logic and component structures to IDs, you can pass those around in your ECS implementation to retrieve data and process entities. You can store component data in generic resizable buffers (or associative maps, for big containers) that can be modified at run-time thanks to component structure knowledge - here's an example of what I mean:
auto c_position_id = ctx.register_component_type("./c_position.txt");
// ...
auto context::register_component_type(const std::string& path)
{
auto& storage = this->component_storage.create_buffer();
auto file_contents = get_contents_from_path(path);
for_parsed_lines_in(file_contents, [&](auto line)
{
if(line.type == "int")
{
storage.append_data_definition(sizeof(int));
}
else if(line.type == "float")
{
storage.append_data_definition(sizeof(float));
}
});
return next_unique_component_type_id++;
}
Some component types and system types are known at compile-time, others are known at run-time.
Use approach (1), and create some sort of "bridge" component and system types that implements any type-erasure technique in order to access component structure or system logic at run-time. An std::map<runtime_system_id, std::function<...>> can work for run-time system logic processing. An std::unique_ptr<runtime_component_data> or an std::aligned_storage_t<some_reasonable_size> can work for run-time component structure.
To answer your question:
But if this RTTI means bad OOP, what would be the correct way to design this system so no RTTI is required?
You need a way of mapping types to values that you can use at run-time: RTTI is an appropriate way of doing that.
If you do not want to use RTTI and you still want to use polymorphic inheritance to define your component types, you need to implement a way to retrieve some sort of run-time type ID from a derived component type. Here's a primitive way of doing that:
namespace impl
{
auto get_next_type_id()
{
static std::size_t next_type_id{0};
return next_type_id++;
}
template <typename T>
struct type_id_storage
{
static const std::size_t id;
};
template <typename T>
const std::size_t type_id_storage<T>::id{get_next_type_id()};
}
template <typename T>
auto get_type_id()
{
return impl::type_id_storage<T>::id;
}
Explanation: get_next_type_id is a non-static function (shared between translation units) that stores a static incremental counter of type IDs. To retrieve the unique type ID that matches a specific component type you can call:
auto position_id = get_type_id<position_component>();
The get_type_id "public" function will retrieve the unique ID from the corresponding instantiation of impl::type_id_storage, that calls get_next_type_id() on construction, which in turn returns its current next_type_id counter value and increments it for the next type.
Particular care for this kind of approach needs to be taken to make sure it behaves correctly over multiple translation units and to avoid race conditions (in case your ECS is multithreaded). (More info here.)
Now, to solve your issue:
It's the full storage pack that prompted this. In particular, it offers no way of accessing a particular type of component.
// Executes `f` on every component of type `T`.
template <typename T, typename TF>
void storage_pack::for_components(TF&& f)
{
auto& data = this->_component_map[get_type_id<T>()];
for(component_base* cb : data)
{
f(static_cast<T&>(*cb));
}
}
You can see this pattern in use in my old and abandoned SSVEntitySystem library. You can see an RTTI-based approach in my old and outdated “Implementation of a component-based entity system in modern C++” CppCon 2015 talk.
Despite the good and long answer by #VittorioRomeo, I'd like to show another possible approach to the problem.
Basic concepts involved here are type erasure and double dispatching.
The one below is a minimal, working example:
#include <map>
#include <vector>
#include <cstddef>
#include <iostream>
#include <memory>
struct base_component {
static std::size_t next() noexcept {
static std::size_t v = 0;
return v++;
}
};
template<typename D>
struct component: base_component {
static std::size_t type() noexcept {
static const std::size_t t = base_component::next();
return t;
}
};
struct component_x: component<component_x> { };
struct component_y: component<component_y> { };
struct systems {
void elaborate(std::size_t id, component_x &) { std::cout << id << ": x" << std::endl; }
void elaborate(std::size_t id, component_y &) { std::cout << id << ": y" << std::endl; }
};
template<typename C>
struct component_manager {
std::map<std::size_t, C> id_component;
};
struct pack {
struct base_handler {
virtual void accept(systems *) = 0;
};
template<typename C>
struct handler: base_handler {
void accept(systems *s) {
for(auto &&el: manager.id_component) s->elaborate(el.first, el.second);
}
component_manager<C> manager;
};
template<typename C>
void add(std::size_t id) {
if(handlers.find(C::type()) == handlers.cend()) {
handlers[C::type()] = std::make_unique<handler<C>>();
}
handler<C> &h = static_cast<handler<C>&>(*handlers[C::type()].get());
h.manager.id_component[id] = C{};
}
template<typename C>
void walk(systems *s) {
if(handlers.find(C::type()) != handlers.cend()) {
handlers[C::type()]->accept(s);
}
}
private:
std::map<std::size_t, std::unique_ptr<base_handler>> handlers;
};
int main() {
pack coll;
coll.add<component_x>(1);
coll.add<component_y>(1);
coll.add<component_x>(2);
systems sys;
coll.walk<component_x>(&sys);
coll.walk<component_y>(&sys);
}
I tried to be true to the few points mentioned by the OP, so as to provide a solution that fits the real problem.
Let me know with a comment if the example is clear enough for itself or if a few more details are required to fully explain how and why it works actually.
If I understand correctly, you want a collection, such as a map, where the values are of different type, and you want to know what type is each value (so you can downcast it).
Now, a "good OOP" is a design which you don't need to downcast. You just call the mothods (which are common to the base class and the deriveratives) and the derived class performs a different operation than its parent for the same method.
If this is not the case, for example, where you need to use some other data from the child and thus you want to downcast, it means, in most cases, you didn't work hard enough on the design. I don't say it's always possible, but you need to design it in such a way the polymorphism is your only tool. That's a "good OOP".
Anyway, if you really need to downcast, you don't have to use RTTI. You can use a common field (string) in the base class, that marks the class type.

Return type which depends on static parameter pack fields

I've recently been experimenting with parameter packs, since they seem to fill a pretty big need in my development. However, I've been running up against a problem for which parameter packs seem to be a feasible solution, but I can't figure out this particular issue. The issue at hand is how to take each type in the parameter pack, retrieve the n-th element from a static field in that type, and return that element (along with the others) in a tuple. In case that wording was a bit unclear, my current use-case is as follows:
/* WARNING: Possibly gratuitous detail */
The architecture of my program is that of an Entity-Component System. Essentially, there are multiple systems that define the logic of different areas of the program. These systems act on entities, which are composed of multiple components (which contain a specific piece of data, e.g. DescriptionComponent). Each system will verify that a given entity has all required components, then perform the system-specific logic.
To accomplish this, I have created a component base class from which specific components are derived. Each derived component contains a static vector which acts as a manager for all components of that type. Each component has an entityId field which references its owning entity, and the static vector is sorted on this id.
Currently, in a given system, I am able to find applicable entities by looking at the components which the system requires, then finding the sets of components which all contain the same entityId and performing the logic on those components. However, I am doing the logic to iterate through the static vectors of the components manually at this point, since each system will have different requirements in terms of component types.
A quick example, just in case the above wasn't clear:
Say DisplaySystem requires a PictureComponent and a PositionComponent. Now say the static fields managing these types are as follows:
PictureComponent::elements = [(picture: "somePic.jpg", id: 1), (picture: "otherPic.jpg", id: 2)]
PositionComponent::elements = [(pos: (1,2), id: 2), (pos: (4,5), id: 3)]
Currently, each component has its own iterator. Starting at zero, we bump up the component index with the lowest entityId (since the vectors are sorted on this key). In the above example, we'd try indices (0,0), see the id 1 is less than id 2, so bump the first index. Then we'd try (1,0), see that both components have entityId 2, and pass those to the system as a pair to perform logic on. Then we'd bump both indices, try (2,1), and be out of the bounds of the PictureComponent vector, and be done.
/* END Gratuitous detail */
The solution that I'd imagine for this problem would be a single templated parameter pack class which takes in the required component types and outputs tuples of components whose entityIds all match. The pseudo-code for this would be as follows
template <typename... RequiredComponentTypes>
class ComponentIterator {
/* standard input iterator functionality*/
...
array componentIndices // same size as our param pack, one index per type
tuple<RequiredComponentTypes...> getNextSetOfComponents() {
while every component's entity id is not equal:
find the currently indexed component with the lowest entity id;
increment the current index of that component type;
out of the while, once we have a set of components with the same entity id:
return a tuple of the indexed components for each type. **//this is the part I have no idea how to do**
}
}
As noted in the code, the process of returning a tuple whose values are retrieved from an index to the static field on the types is the part I'm unsure about. For any number of template parameters, it's trivial to do manually (if I have a set of named parameterized types, then making the tuple is as simple as calling make_tuple with the appropriate values.) However, when dealing with a parameter pack, I'm not sure how to get around the issue.
Any help you all could offer would be greatly appreciated, and if you need any clarification or more details, please don't hesitate to let me know. Thanks!
You may do something like:
template <typename... Ts>
class ComponentIterator {
static constexpr std::size_t size = sizeof...(Ts);
public:
ComponentIterator() : indices(), finished(false) { adjust(); }
std::tuple<Ts&...> get()
{
return get(std::index_sequence_for<Ts...>());
}
ComponentIterator& operator ++()
{
for (auto& e : indices) {
++e;
}
adjust();
return *this;
}
bool is_finished() const { return finished; }
private:
void adjust()
{
adjust(std::index_sequence_for<Ts...>());
}
template <std::size_t...Is>
std::tuple<Ts&...> get(std::index_sequence<Is...>)
{
return std::tie(Ts::elements[indices[Is]]...);
}
template <std::size_t...Is>
void adjust(std::index_sequence<Is...>) {
bool is_ended[size] = {indices[Is] == Ts::elements.size()...};
if (std::any_of(std::begin(is_ended), std::end(is_ended),
[](bool b) { return b; })) {
finished = true;
return;
}
int min_value = std::min({Ts::elements[indices[Is]].id...}) - 1;
for (;;)
{
++min_value;
bool increases[size] = {increase_until<Ts>(indices[Is], min_value)...};
if (std::any_of(std::begin(increases), std::end(increases),
[](bool b) { return !b; })) {
finished = true;
return;
}
const int ids[size] = {Ts::elements[indices[Is]].id...};
if (std::all_of(std::begin(ids), std::end(ids),
[min_value](int id) { return id == min_value;})) {
return;
}
}
}
template <typename T>
bool increase_until(std::size_t& index, int min_value)
{
for (; index != T::elements.size(); ++index) {
if (min_value <= T::elements[index].id) {
return true;
}
}
return false;
}
private:
std::array<std::size_t, size> indices;
bool finished;
};
Live example

Object oriented design for hotel reservation system

I am practicing object oriented design for an upcoming interview. My question is about the design for a hotel reservation system:
- The system should be able to return an open room of a specific type or return all the open rooms in the hotel.
- There are many types of rooms in hotel like regular, luxury, celebrity and so on.
So far I have come up with following classes:
Class Room{
//Information about room
virtual string getSpecifications(Room *room){};
}
Class regularRoom: public Room{
//get specifications for regular room
}
Class luxuryRoom: public Room{
//get specifications for regular room
}
//Similarly create as many specialized rooms as you want
Class hotel{
vector<Room *>openRooms; //These are all the open rooms (type casted to Room type pointer)
Public:
Room search(Room *aRoom){ //Search room of a specific type
for(int i=0;i<openRooms.size();i++){
if(typeid(*aRoom)==typeid(*openRooms[i])) return *openRooms[i];
}
}
vector<Room> allOpenRooms(){//Return all open rooms
...
}
}
I am confused about the implementation of hotel.search() method where I am checking the type (which I believe should be handled by polymorphism in some way). Is there a better way of designing this system so that the search and allOpenRooms methods can be implemented without explicitly checking the type of the objects?
Going through the sub-class objects asking what type they are isn't really a good illustration of o-o design. You really need something you want to do to all rooms without being aware of what type each one is. For example print out the daily room menu for the room (which might be different for different types).
Deliberately looking for the sub-class object's type, while not being wrong, is not great o-o style. If you just want to do that, as the other respondents have said, just have "rooms" with a set of properties.
You could always let a room carry it's real type, instead of comparing the object type:
enum RoomType
{
RegularRoom,
luxuryRoom
};
class Room{
public:
explicit Room(RoomType room_type) : room_type_(room_type) { }
virtual ~Room(){}
RoomType getRoomType() const { return room_type_; }
private:
RoomType room_type_; // carries room type
};
class regularRoom: public Room{
public:
regularRoom() : Room(RegularRoom){ }
};
Room search(Room *aRoom)
{
//Search room of a specific type
for(size_t i=0;i<openRooms.size();i++)
{
if (aRoom->getRoomType() == RegularRoom) // <<-- compare room type
{
// do something
}
}
};
Do the different types of rooms have different behavior? From
the description you give, this is not a case where inheritance
should be used. Each room simply has an attribute, type, which
is, in its simplest form, simply an enum.
The simplest way is to have a Room type enumeration as #billz suggest you. The problem with tis way is that you must not forget to add a value to the enumeration and use it once every time you add a new type of Room to the system. You have to be sure you use the enum values only once, one time per class.
But, on the other hand, inheritance bassed dessigns only have sense if the types of the hierarchy shares a common behaviour. In other words, you want to use them in the same way, regardless of its type. IMPO, an OO/inheritance dessign is not the better way to do this.
The freak and scalable way I do this type of things is through typelists.
Normally, you have different search criteria for every type in your system. And, in many cases, the results of this search are not the same for different types of your system (Is not the ssame to search a luxury room and to search a normal room, you could have different search criteria and/or want different search results data).
For this prupose, the system has three typelists: One containing the data types, one containing the search criteria types, and one containing the search results types:
using system_data_types = type_list<NormalRoom,LuxuryRoom>;
using search_criteria_types = type_list<NormalRoomsCriteria,LuxuryRoommsCriteria>;
using search_results_types = type_list<NormalRoomSearchResults,LuxuryRoomSearchResults>;
Note that type_lists are sorted in the same manner. This is important, as I show below.
So the implementation of the search engine is:
class SearchEngine
{
private:
std::vector<VectorWrapper*> _data_lists; //A vector containing each system data type in its own vector. (One vector for NormalRoom, one for LuxuryRoom, etc)
//This function returns the vector that stores the system data type passed.
template<typename T>
std::vector<T>& _get_vector() {...} //Implementation explained below.
public:
SearchEngine() {...}//Explanation below.
~SearchEngine() {...}//Explanation below.
//This function adds an instance of a system data type to the "database".
template<typename T>
void addData(const T& data) { _get_vector<T>().push_back( data ); }
//The magic starts here:
template<typename SEARCH_CRITERIA_TYPE>//This template parameter is deduced by the compiler through the function parameter, so you can ommit it.
typename search_results_types::type_at<search_criteria_types::index_of<SEARCH_CRITERIA_TYPE>> //Return value (The search result that corresponds to the passed criteria. THIS IS THE REASON BECAUSE THE TYPELISTS MUST BE SORTED IN THE SAME ORDER.
search( const SEARCH_CRITERIA_TYPE& criteria)
{
using system_data_type = system_data_types::type_at<search_criteria_types::index_of<SEARCH_CRITERIA_TYPE>>; //The type of the data to be searched.
std::vector<system_data_type>& data = _get_vector<system_data_type>(); //A reference to the vector where that type of data is stored.
//blah, blah, blah (Search along the vector using the criteria parameter....)
}
};
And the search engine can be used as follows:
int main()
{
SearchEngine engine;
engine.addData(LuxuryRoom());
engine.addData(NormalRoom());
auto luxury_search_results = engine.search(LuxuryRoomCriteria()); //Search LuxuryRooms with the specific criteria and returns a LuxuryRoomSearchResults instance with the results of the search.
auto normal_search_results = engine.search(NormalRoomCriteria()); //Search NormalRooms with the specific criteria and returns a NormalRoomSearchResults instance with the results of the search.
}
The engine is based on store one vector for every system data type. And the engine uses a vector that stores that vectors.
We cannot have a polymorphic reference/pointer to vectors of different types, so we use a wrapper of a std::vector:
struct VectorWrapper
{
virtual ~VectorWrapper() = 0;
};
template<typename T>
struct GenericVectorWrapper : public VectorWrapper
{
std::vector<T> vector;
~GenericVectorWrapper() {};
};
//This template class "builds" the search engine set (vector) of system data types vectors:
template<int type_index>
struct VectorBuilder
{
static void append_data_type_vector(std::vector<VectorWrapper*>& data)
{
data.push_back( new GenericVectorWrapper< system_data_types::type_at<type_index> >() ); //Pushes back a vector that stores the indexth type of system data.
VectorBuilder<type_index+1>::append_data_type_vector(data); //Recursive call
}
};
//Base case (End of the list of system data types)
template<>
struct VectorBuilder<system_data_types::size>
{
static void append_data_type_vector(std::vector<VectorWrapper*>& data) {}
};
So the implementation of SearchEngine::_get_vector<T> is as follows:
template<typename T>
std::vector<T>& get_vector()
{
GenericVectorWrapper<T>* data; //Pointer to the corresponing vector
data = dynamic_cast<GenericVectorWrapper<T>*>(_data_lists[system_data_types::index_of<T>]); //We try a cast from pointer of wrapper-base-class to the expected type of vector wrapper
if( data )//If cast success, return a reference to the std::vector<T>
return data->vector;
else
throw; //Cast only fails if T is not a system data type. Note that if T is not a system data type, the cast result in a buffer overflow (index_of<T> returns -1)
}
The constructor of SearchEngine only uses VectorBuilder to build the list of vectors:
SearchEngine()
{
VectorBuilder<0>::append_data_type_vector(_data_list);
}
And the destructor only iterates over the list deleting the vectors:
~SearchEngine()
{
for(unsigned int i = 0 ; i < system_data_types::size ; ++i)
delete _data_list[i];
}
The advantages of this dessign are:
The search engine uses exactly the same interface for different searches (Searches with different system data types as target). And the process of "linking" a data type to its corresponding search criteria and results is done at compile-time.
That interface is type safe: A call to SearchEngine::search() returns a type of results based only on the search criteria passed. Assignament results errors are detected at compile-time. For example: NormalRoomResults = engine.search(LuxuryRoomCriteria()) generates a compilation error (engine.search<LuxuryRoomCriteria> returns LuxuryRoomResults).
The search engine is fully scalable: To add a new datatype to the system, you must only go to add the types to the typelists. The implementation of the search engine not changes.
Room Class
class Room{
public:
enum Type {
Regular,
Luxury,
Celebrity
};
Room(Type rt):roomType(rt), isOpen(true) { }
Type getRoomType() { return roomType; }
bool getRoomStatus() { return isOpen; }
void setRoomStatus(bool isOpen) { this->isOpen = isOpen; }
private:
Type roomType;
bool isOpen;
};
Hotel Class
class Hotel{
std::map<Room::Type, std::vector<Room*>> openRooms;
//std::map<Room::Type, std::vector<Room*>> reservedRooms;
public:
void addRooms(Room &room) { openRooms[room.getRoomType()].push_back(&room); }
auto getOpenRooms() {
std::vector<Room*> allOpenRooms;
for(auto rt : openRooms)
for(auto r : rt.second)
allOpenRooms.push_back(r);
return allOpenRooms;
}
auto getOpenRoomsOfType(Room::Type rt) {
std::vector<Room*> OpenRooms;
for(auto r : openRooms[rt])
OpenRooms.push_back(r);
return OpenRooms;
}
int totalOpenRooms() {
int roomCount=0;
for(auto rt : openRooms)
roomCount += rt.second.size();
return roomCount;
}
};
Client UseCase:
Hotel Marigold;
Room RegularRoom1(Room::Regular);
Room RegularRoom2(Room::Regular);
Room LuxuryRoom(Room::Luxury);
Marigold.addRooms(RegularRoom1);
Marigold.addRooms(RegularRoom2);
Marigold.addRooms(LuxuryRoom);
auto allRooms = Marigold.getOpenRooms();
auto LRooms = Marigold.getOpenRoomsOfType(Room::Luxury);
auto RRooms = Marigold.getOpenRoomsOfType(Room::Regular);
auto CRooms = Marigold.getOpenRoomsOfType(Room::Celebrity);
cout << " TotalOpenRooms : " << allRooms.size()
<< "\n Luxury : " << LRooms.size()
<< "\n Regular : " << RRooms.size()
<< "\n Celebrity : " << CRooms.size()
<< endl;
TotalOpenRooms : 4
Luxury : 2
Regular : 2
Celebrity : 0
If you really want to check that a room is of the same type as some other room, then typeid() is as good as any other method - and it's certainly "better" (from a performance perspective, at least) to calling a virtual method.
The other option is to not have separate classes at all, and store the roomtype as a member variable (and that is certainly how I would design it, but that's not a very good design for learning object orientation and inheritance - you don't get to inherit when the base class fulfils all your needs).

Accomplish this task in C++; Migration from AS3.0

I've got way too much information to work with, so for now I'll consider this question answered until I can sort it all out and decide on the final implementation! Thanks a ton gf and Simon Buchan. I wish I could accept both of your answers, since they're both definite possibilities!
Additional / Revised Conceptual Information as suggested:
What I am aiming to do;
I am making a game. In this game every object used is an instance of the DOBJ class. The TUR class extends the DOBJ class. The SHO class extends the TUR class.
Each TUR class has an array of SHO's stored in it's SHOARR array. Each SHO instance needs to be given a set of instructions.
I know for a fact I could make 1000's of different SHO classes that have their instructions set during construction.
However, considering I will have so many different acting SHO instances, I was interested in another way to pass a set of instructions. Through the contruction of the SHO would be the ideal.
The instructions I am attempting to pass to each SHO are simple if statements;
if(frame > 64) { rotation += 4; };
if(state == 0 && frame < 32) { xs = 12; ys = 12; state = 1; };
Original question
Migration from ActionScript3.0 to C++ is proving to be a trial indeed. Thanks to those who have answered my questions thus far and also to those who opened stackoverflow in the first place. Onto the question... (TL;DR near the bottom to get straight to the question)
I'm attempting to apply the same logic that I could apply in AS3.0 to my project in C++ and it's just not going very well.
In AS3.0 I was used to slapping any and every datatype into an Array. It made things pretty simple. Now that I've run into C++ dev, I realized that I can't exactly do that anymore.
So now I'm stuck with this problem of rewriting a little AI system in a new language, where the driving point of the system isn't even compatible!
Here's an example of a piece of the code I was writing in AS3.0;
AI[NUM][1]( OBJ, AI[NUM][2], AI[NUM][3] );
AI being an array, NUM being an integer, OBJ being an instance of a class.
This line obviously called the function in the second element of the first array in the main array with the arguments being a class in which to perform the function on, whatever was in the third element of the first array of the main array, and likewise the fourth element.
In this case;
AI[NUM][1] would be a function
AI[NUM][2] would be a variable
AI[NUM][3] would be a number
Generally, my AI was run on calling a function to change or compare the variable with a number.
An example would be;
CompareST( someObject, "x", 500 );
and return true if someObject's x variable was smaller than (ST) 500.
The AI array itself was just filled with arrays of calls similar to this.
Quite new to C++ I had no idea how to go about this, so I did a bit of searching and reading of many different websites and came to the conclusion that I should look into function pointers.
However, after reading a bit into them, I've come to the conclusion that it won't help me realize my goal. While it did help me call functions like I wanted to call them, it doesn't help me stack different datatypes into one large array of arrays.
TL;DR
EDIT++:
What I need for each object is a set of instructions to be checked every frame. However, for each instance of the class, the instructions have to be different.
I plan on having a LOT of different instances, so making a class for each one is unreasonable.
Thus, I needed a way to pass a set of instructions to each one through it's constructor and read + execute them at any time their think() function is called.
My ultimate goal (aside from finding out about a better way to go about this) would be to be able to have an array of function calls, like;
A[n][0]( O, A[n][1], A[n][2] );
Where;
O is the instance the function is altering
A[n][0] is a function (Equality or Comparison)
A[n][1] is the variable, eg; "x", O["x"] (or a pointer to that variable in the case of C++)
A[n][2] is the value to alter the variable by, or compare it to.
And I'm not sure how I would rewrite this into C++, or alter it to work in another way.
Aftermath / Additional Information
What I'm actually aiming to do is be able to give an object a set of instructions at the time of it's creation, through the constructor. For example upon creation give an object instructions to wait 64 frames, and then rotate in the opposite direction, would have been something like this;
t.AI = [ [ 1, AIF.CompareET, "STATE", 0, AIF.CompareGT, "FRAME", 64, 0, AIF.EqualityAT, "baseRotation", 180, AIF.EqualityET, "STATE", 1 ] ];
In pseudocode;
(The 1 in the array denotes how to read the rest of the array, in this case everything before the odd 0 [ The one that comes after 64 ] is a comparison. If any of those fail, anything after the 0 will not be looked at )
Compare STATE is equal to (ET) 0, if true
Compare FRAME is greather than (GT) 64, if true
Add 180 to (AT) baseRotation, Set STATE equal to 1
Sorry that this turned out really long. I hope it's understandable, and I'm not asking something stupidly difficult to explain.
You can store functions using function pointers or functors. Variant types though are not natively supported by C++, you have to use custom solutions there.
One possibility would be to use Boost.Any (or better, Boost.Variant if you only use a fixed set of types):
typedef void (*Function)(Object*, const std::string&, boost::any&);
std::vector<Function> functions;
Given some function:
void f(Object* obj, const std::string& name, boost::any& value) {
// ...
}
you could store and call it similar to your example:
functions.push_back(&f);
functions[0](obj, "x", boost::any(500));
To utilize a declarative syntax, there are three options that come to my mind:
you use a similar approach and have central "interpreter" function, e.g. based on a switch (don't forget to switch to integers or pointers-to-members instead of strings if you need performance)
you invent your own language and generate C++ code from description files
you compose function objects in a declarative way
To do composition, you could use Boost.Bind or something like custom objects that represent operations:
struct Operation {
virtual ~Operation() {}
virtual bool operator()(Object&) = 0;
};
template<class T>
struct GreaterThen : Operation {
typedef T Object::*Member;
Member member;
const T value;
CompareGT(Member member, const T& value) : member(member), value(value) {}
bool operator()(Object& obj) { return (obj.*member > value); }
};
template<class T>
struct SetTo : Operation {
typedef T Object::*member;
Member member;
const T value;
SetTo(Member member, const T& value) : member(member), value(value) {}
bool operator()(Object& obj) { obj.*member = value; return true; }
};
Now we can build operation lists:
typedef std::vector<Operation*> OpList;
OpList operation;
operations.push_back(new GreaterThen<int>(&Object::Frame, 64));
operations.push_back(new SetTo<int>(&Object::State, 1));
We can use helper functions to avoid having to specify the template types:
template<class T>
Operation* opGreaterThen(T Object::*mem, const T& val) {
return new GreaterThen<T>(mem, val);
}
Assuming a similar helper for SetTo and using Boost.Assign the above becomes:
OpList operations = boost::assign::list_of
(opGreaterThen(&Object::Frame, 64))
(opSetTo (&Object::State, 1));
Executing the operations becomes the following then:
OpList::iterator it = operation.begin();
for( ; it != operations.end(); ++it) {
Operation& op = *it; // just for readability
if(!op(someObject)) break; // stop if operation returns false
}
Wow.
Reading through that slowly suggests what you're trying to end up with is an array of function calls and you can choose a different function with the same parameters (but different implementation) for different actions and choose the correct one for the correct case.
If that is the case, you're looking for function pointers. Try this tutorial.
You should be able to use a function pointer with an argument set and point it to the correct function based on your needs. You won't need an array of function pointers for this either - any function that matches the definition should do. From the tutorial, declare a function pointer like this:
int (TMyClass::*functptr)(classname, int, int) = NULL; // C++
Then assign it later:
this.functptr = &TMyClass::doitthisway;
While it is possible (although a pain) to have an array of arbitrary types, you pretty much never need it, since you have to know something about what is where to do anything interesting with it: for example, your 'TL;DR' example seems to look something like:
struct AIRule {
// Can only handle comparing ints, see later for more general solution.
typedef bool compare_type(AIObject*, AIObject::*int, int);
compare_type* compare;
AIObject* object;
AIObject::int* member;
int comparand;
};
So now you can do something like:
bool ai_equal(AIObject* object, AIObject::int* member, int comparand) {
return object->*member == comparand;
}
...
ai[n].compare = &ai_equal;
ai[n].object = some_object;
ai[n].member = &AIObject::some_member;
ai[n].comparand = 50;
...
if (ai[n].compare(ai[n].object, ai[n].member, ai[n].comparand)) {
...
}
This just moves the any type problem from the rules array to member though. C++ needs to know at least how many bytes a member is, and a string (for example) can be much bigger than an int. You can get around this by using pointers: which essentially is C++'s version of any, but you then need to delete it yourself (or you will leak memory!), at which point the interface method below becomes simpler.
If I was doing what you seem to want, I would use inheritance:
struct Sprite {
int frame;
double rotation;
Sprite() {
frame = 0;
rotation = 0.0;
}
virtual ~Sprite() {}
virtual void think() {
++frame;
}
virtual void draw() {
...
}
};
struct RotatingSprite : public Sprite {
int state;
MyShape() {
state = 0;
}
void think() {
Sprite::think();
if (state == 0 && frame > 64) {
state = 1;
rotation += 180.0;
}
}
};
Or a function pointer:
struct Sprite {
int frame;
double rotation;
void (*think)(Sprite*);
Sprite() {
frame = 0;
rotation = 0.0;
}
};
void rotate_think(Sprite* sprite) {
if (sprite->state == 0 && sprite->frame > 64) {
sprite->state = 1;
sprite->rotation += 180.0;
}
}
...
sprite->think = &rotate_think;
If you really need to do it dynamically I would recommend using the ++ part of C++. For the predicates (a predicate is just something that returns a boolean, like isLowerCase()) create an AIPredicate interface, and the actions an AIAction interface:
struct AIPredicate {
// "When you delete an AIPredicate, delete the full type, not just this interface."
virtual ~AIPredicate() {}
// "You can treat this as a function (operator()) but I'm not providing an implementation here ( = 0)"
virtual bool operator()(AIObject* object) = 0;
};
struct AIAction {
virtual ~AIAction() {}
virtual void operator()(AIObject* object) = 0;
};
struct AIRule {
// std::auto_ptr (or std::unique_ptr if you can use C++0x) will delete predicate for you.
// Add "#include <memory>" to your includes if it complains (most std headers will include it already)
std::auto_ptr<AIPredicate> predicate;
std::auto_ptr<AIAction> action;
};
Now you can make types like:
struct AIFrame : public AIPredicate {
// Implement the operator() member AICondition promises.
bool operator()(AIObject* object) {
return object->foo < 100;
}
};
...
// Use .reset() instead of = if you use std::unique_ptr.
ai[n].predicate = new AIFooIsLow();
If you want to have a very general predicate type, you can use the very powerful (and complicated) templates feature:
// The naming convention I'm using here is 'T'TitleCase for template parameters, TitleCase for types,
// lower_case for arguments and variables and '_'lower_case for members.
template<typename TMemberType, AIObject::TMemberType* TMember>
struct AIMemberEquals : public AIPredicate {
// Constructor: Initializes a new instance after it is created.
AIMemberEquals(TMemberType comparand) {
// Save comparand argument so we can use it in operator().
_comparand = comparand;
}
bool operator()(AIObject* object) {
return object->*TMember == comparand;
}
// Stores the value to compare.
TMemberType _comparand;
};
Unfortunately, creating templates looks a bit crazy:
ai[n].predicate = new AIMemberEquals<int, &AIObject::some_member>(100);
Read it as "create a new instance of (the type that AIMemberEquals applied to int and (the some_member member of AIObject) creates), with the argument 100".
When you have multiple predicates memory management becomes a bit more difficult without C++0x's unique_ptr or shared_ptr, types that will delete the object for you, since std::auto_ptr doesn't work in containers:
#include <vector>
struct AIData {
// vector is fairly close to AS3's Array type, it is a good default for
// arrays of changing or unknown size.
std::vector<AIPredicate*> predicates;
// Destructor: will be run before the memory for this object is freed.
~AIData() {
for (int i = 0; i != predicates.size(); ++i) {
delete predicates[i];
}
}
};
...
ai[n].predicates.push_back(new AIFooIsLow());
...
for (int i = 0; i != ai[n].predicates.size(); ++i) {
(*ai[n].predicates[i])(ai[n].object);
}
In C++0x:
struct AIData {
// unique_ptr will delete it for you, so no ~AIData() needed.
std::vector<unique_ptr<AIPredicate>> predicates;
};
Your final example could in C++ look something like:
std::auto_ptr<Shape> shape(new Shape());
...
std::auto_ptr<AIRule> rule(new AIRule());
rule->predicates.push(new AIMemberEquals<int, &Shape::state>(0));
rule->predicates.push(new AIMemberGreater<int, &Shape::frame>(64));
rule->actions.push(new AIAddMember<double, &Shape::rotation>(180.0));
rule->actions.push(new AISetMember<int, &Shape::state>(1));
shape->ai.push(rule); // .push(std::move(rule)); if you are using unique_ptr
Certainly not as pretty, but it works and is fairly flexible.