i have problem with command pattern in SFML book that works aside with scene-graph data structure when i detached child node and attached it once again. the VS14 doesn't help me much when i debug the problem. it is point me to the vector iterator is not incrementable in onCommend(), this function seems okay, it doesn't invalidate the iterator, it just a loop.
could someone please help me in this issue, thanks you.
i have made minimal example as i could to show the problem as shown below:
#include <stdexcept>
#include <iostream>
#include <utility>
#include <vector>
#include <memory>
#include <cassert>
#include <functional>
#include <queue>
#include <algorithm>
class SceneNode;
namespace Category
{
enum Type
{
None = 0,
Player1 = 1 << 0,
Player2 = 1 << 1,
};
}
struct Command
{
using Action = std::function<void(SceneNode&)>;
Command()
: action()
, category(Category::None)
{
}
Action action;
unsigned int category;
};
template
<
typename GameObject,
typename Function,
typename = std::enable_if_t<std::is_base_of<SceneNode, GameObject>::value>
>
auto derivedAction(Function fn)
{
return [=](SceneNode& node)
{
fn(static_cast<GameObject&>(node));
};
}
class CommandQueue
{
public:
void push(const Command& command)
{
mQueue.push(command);
}
Command pop()
{
auto command(mQueue.front());
mQueue.pop();
return command;
}
bool isEmpty() const
{
return mQueue.empty();
}
private:
std::queue<Command> mQueue;
};
class SceneNode
{
public:
using Ptr = std::unique_ptr<SceneNode>;
public:
SceneNode(Category::Type category = Category::None)
: mChildren()
, mParent(nullptr)
, mDefaultCategory(category)
{
}
void attachChild(Ptr child)
{
child->mParent = this;
mChildren.push_back(std::move(child));
}
Ptr detachChild(const SceneNode& node)
{
auto found = std::find_if(mChildren.begin(), mChildren.end(),
[&](Ptr& p)
{
return p.get() == &node;
});
assert(found != mChildren.end());
Ptr result = std::move(*found);
result->mParent = nullptr;
mChildren.erase(found);
return result;
}
void onCommand(const Command& command)
{
// Command current node, if category matches
if (command.category & getCategory())
command.action(*this);
// Command children
for (const auto& child : mChildren) // here VS14 complains
child->onCommand(command);
}
virtual unsigned int getCategory() const
{
return mDefaultCategory;
}
SceneNode* getParent() const
{
return mParent;
}
virtual void setNumber(int i) {};
private:
virtual void draw(std::ostream& stream) const
{
drawCurrent(stream);
drawChildren(stream);
}
friend std::ostream& operator<<(std::ostream& stream, const SceneNode& self)
{
self.draw(stream);
return stream;
}
virtual void drawCurrent(std::ostream& stream) const
{
// do nothing
}
void drawChildren(std::ostream& stream) const
{
for (const auto& child : mChildren)
child->draw(stream);
}
private:
std::vector<Ptr> mChildren;
SceneNode* mParent;
Category::Type mDefaultCategory;
};
class Player final : public SceneNode
{
public:
enum Type
{
None,
Player1,
Player2,
};
public:
Player(Type type, int n, Player* player = nullptr)
: mType(type)
, mNumber(n)
, mPlayer(player)
{
}
void setNumber(int i) override
{
mNumber = i;
}
void follow()
{
if (getCategory() & Category::Player2)
{
if (mPlayer)
{
auto found = getParent()->detachChild(*this);
found->setNumber(20);
mPlayer->attachChild(std::move(found));
}
}
}
unsigned int getCategory() const override
{
if (mType == Type::Player1)
return Category::Player1;
else
return Category::Player2;
}
private:
void drawCurrent(std::ostream& stream) const override
{
stream << "Parent: "<< getParent()->getCategory() << " has child: " << mNumber << '\n';
}
private:
Type mType;
int mNumber;
Player* mPlayer;
};
int main()
{
SceneNode sceneGraph;
CommandQueue commandQueue;
Player* player1;
Player* player2;
auto first(std::make_unique<Player>(Player::Player1, 1));
player1 = first.get();
sceneGraph.attachChild(std::move(first));
// Works
//auto second(std::make_unique<Player>(Player::Player2, 2, player1));
//player2 = second.get();
//player2->setNumber(20);
//player1->attachChild(std::move(second));
// Works
//auto second(std::make_unique<Player>(Player::Player2, 2, player1));
//player2 = second.get();
//sceneGraph.attachChild(std::move(second));
//auto found = player1->getParent()->detachChild(*player2);
//found->setNumber(20);
//player1->attachChild(std::move(found));
// Works
//auto second(std::make_unique<Player>(Player::Player2, 2, player1));
//player2 = second.get();
//sceneGraph.attachChild(std::move(second));
//player2->follow();
// Failed ????????
auto second(std::make_unique<Player>(Player::Player2, 2, player1));
player2 = second.get();
sceneGraph.attachChild(std::move(second));
Command c;
c.action = derivedAction<Player>(std::bind(&Player::follow, std::placeholders::_1));
c.category = Category::Player2;
commandQueue.push(c);
while (!commandQueue.isEmpty())
sceneGraph.onCommand(commandQueue.pop());
std::cout << sceneGraph;
}