Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: problem with attach and detach child in SceneNode [SOLVED]  (Read 5001 times)

0 Members and 1 Guest are viewing this topic.

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
problem with attach and detach child in SceneNode [SOLVED]
« on: February 16, 2016, 01:22:43 am »
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;
}
« Last Edit: February 17, 2016, 06:20:56 am by MORTAL »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: vector not incrementable problem with attach and detach child in SceneNode
« Reply #1 on: February 16, 2016, 10:53:32 am »
You call that minimal? I know that many parts are taken from the SFML book, but you can also reduce them where possible. Since your problem is related to iterator invalidation, everything that doesn't have to do with can be removed. That includes all the rendering and SFML specific code, as well as unrelated functionality like setting colors.

See also: http://en.sfml-dev.org/forums/index.php?topic=5559.msg36368#msg36368

Furthermore, why doesn't the VS debugger help? What have you tried?
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: vector not incrementable problem with attach and detach child in SceneNode
« Reply #2 on: February 16, 2016, 05:05:31 pm »
You call that minimal? I know that many parts are taken from the SFML book, but you can also reduce them where possible. Since your problem is related to iterator invalidation, everything that doesn't have to do with can be removed. That includes all the rendering and SFML specific code, as well as unrelated functionality like setting colors.

See also: http://en.sfml-dev.org/forums/index.php?topic=5559.msg36368#msg36368

i have read that thread and i tried so hard as i could to make short and workable code, but as you see i'm dealing with complex data structure and software pattern and both should be working to gather. sorry for inconvenient approach that i attended to illustrate the problem, i don't know how to minimize their structure without break its functionality. and for the drawing i include it to give some sense about what the desired output will be look like if this bug is fixed.

Furthermore, why doesn't the VS debugger help? What have you tried?
i was expecting from debugger to be more descriptive about the assertion messages, or at least it may point it to where the potential cause of this problem originated. for example, in this code the debugger pointed to onCommand() which is simple iterated the vector no deletion nor insertion that may invalidate the iterators. debugger is simple misleading, it cause more troubles for me than help me to solve it.

for what i have tried, i spent most of my day working on this issue, in my opinion, i believe the problem either with

  • a bug with VS14
  • the both data structure have limitation if they were working to gather, like rule of thumb, don't attach nor detach with command pattern in Scene-graph data type.

i post my question, seeking for help and hopefully that my conclusions is plane wrong. and the issue is caused by a bug in the code. or where is a way to work around this issue.

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: vector not incrementable problem with attach and detach child in SceneNode
« Reply #3 on: February 16, 2016, 11:08:59 pm »
EDIT:
as Nexus suggested, i have modify code now it can be run in console window.
the value of player2 is changing to 20 instead of 2 as initial value. but the bug is still there.

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: vector not incrementable problem with attach and detach child in SceneNode
« Reply #4 on: February 16, 2016, 11:33:43 pm »
aha, i knew it, it is VS@@T bug again, i run the code in C++shell it runs fine.

here link to run the code, if some one interesting in this bug.
http://cpp.sh/6jxm
« Last Edit: February 16, 2016, 11:41:02 pm by MORTAL »

ratzlaff

  • Newbie
  • *
  • Posts: 33
    • View Profile
Re: problem with attach and detach child in SceneNode [SOLVED]
« Reply #5 on: February 17, 2016, 01:36:48 am »
I very much doubt there is a bug in VS14. Have you tried rewriting the loop using begin() and end() ?

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: problem with attach and detach child in SceneNode [SOLVED]
« Reply #6 on: February 17, 2016, 02:28:46 am »
I very much doubt there is a bug in VS14. Have you tried rewriting the loop using begin() and end() ?


yes, i did but no avail.
in release mode where all debug iterators level is zero, the code runs in VS14. but as i said the VS14 should be more descriptive in errors, for example if i detached and attached node in the node that will be transferred. the VS14 gives me "the vector iterator is not incrementable" but if i did same operations in the node that will be transferred to, the VS14 gives me "the vector iterator is not compatible" and for both error messages are not true because there is no deletion or insertion in onCommand() unless the vector in detach() function doesn't correct its iterator when the actual deletion happened and VS14 trigger the assertion for looping the vector. even if this is the case, we should not suffer the iterator correction. this most be done internally in vector implementation in first place.

« Last Edit: February 17, 2016, 02:30:34 am by MORTAL »

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: problem with attach and detach child in SceneNode [SOLVED]
« Reply #7 on: February 17, 2016, 06:04:48 am »
here simple solution for this bug to make it runs in VS14 with debug mode.

void onCommand(const Command& command)
{
        // Command current node, if category matches
        if (command.category & getCategory())
                command.action(*this);

        // Command children
        const static auto categoryObject = std::bind(&SceneNode::getCategory, std::placeholders::_1);
        auto lambda = std::bind(std::equal_to<unsigned int>(), categoryObject, command.category);
        auto found = std::find_if(mChildren.begin(), mChildren.end(), lambda);

        if (found != mChildren.end())
        {
                (*found)->onCommand(command);
        }
        else
        {
                for (const auto& child : mChildren) // now VS14 is happy
                        child->onCommand(command);
        }
}

this solution is not the best but it does do the job.
« Last Edit: March 21, 2016, 03:28:28 am by MORTAL »

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: vector not incrementable problem with attach and detach child in SceneNode
« Reply #8 on: February 17, 2016, 08:29:43 pm »
aha, i knew it, it is VS@@T bug again, i run the code in C++shell it runs fine.
If you actually believe it's a bug in the compiler, then write a minimal example that demonstrates the misbehavior in an obvious way. In case you can successfully show this, it would be nice to report it so that the next compiler version can be improved.

Another compiler working fine isn't even remotely a proof that VS is broken, it may just treat UB that you create differently...
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: problem with attach and detach child in SceneNode [SOLVED]
« Reply #9 on: February 17, 2016, 10:56:39 pm »
i did report to visual studio team about a bug with std::bind when i start leaning from SFML book that time i had VS10. the fun part is visual studio failed generation after generation with same code. so sadly

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: problem with attach and detach child in SceneNode [SOLVED]
« Reply #10 on: February 17, 2016, 11:37:40 pm »
You mean the one I mentioned here? You reported it?

I cannot confirm your bad experience. I reported several compiler bugs at Microsoft Connect, and usually they were fixed in a timely manner. Once even in a month and once in 2 weeks...

Anyway, that's a bad excuse for not searching the problem in your own code ;)
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: problem with attach and detach child in SceneNode [SOLVED]
« Reply #11 on: February 18, 2016, 01:47:22 am »
yeah, thats it... unfortunately they didn't fixed it in VS10 they put all their effort on next release compiler.

and yeah i'm now focus on my game. hopefully i could finish this month.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: problem with attach and detach child in SceneNode [SOLVED]
« Reply #12 on: February 18, 2016, 08:14:03 pm »
Yes, but 2010 lies 6 years in the past... VS 2013 and 2015 have really been a huge step forward regarding more recent C++ versions. You should give them a try.

In any case, good luck with your game!
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

 

anything