2

Inside this following thread routine :

void*   Nibbler::moveRoutine(void* attr)
{
  [...]
      Nibbler*  game = static_cast<Nibbler*>(attr);

      while (game->_continue == true)
       {
         std::cout << game->_snake->_body.front()->getX() << std::endl; // display 0
         std::cout << game->getDirection() << std::endl; // display 0
         game->moveSnake();
         std::cout << game->_snake->_body.front()->getX() << std::endl; // display 0
         std::cout << game->getDirection() << std::endl; // display 42
       }
    }
  [...]
}

I am calling the member function moveSnake(), which is supposed to modify the positions of the cells forming my snake's body.

void    Nibbler::moveSnake()
{
  [...]
  std::cout << this->_snake->_body.front()->getX() << std::endl; // display 0
  this->_snake->_body.front()->setX(3);
  this->_direction = 42;
  std::cout << this->_snake->_body.front()->getX() << std::endl; // display 3
  [...]
}

Although my two coordinates are effectively modified inside my moveSnake() function, they are not anymore when I go back to my routine, where they keep their initial value. I don't understand why this is happening, since if I try to modify any other value of my class inside my moveSnake() function, the instance is modified and it will keep this value back in the routine.

The Nibbler class :

class Nibbler
{
public :
  [...]
  void          moveSnake();
  static void*  moveRoutine(void*);

private :
  [...]
  int           _direction
  Snake*        _snake;
  IGraphLib*    _lib;
  pthread_t     _moveThread;
...
};

The snake :

class   Snake
{
public :

  [...]
  std::vector<Cell*>    _body;
};

And finally the cell :

class   Cell
{
public :

  void  setX(const int&);
  void  setY(const int&);

  int   getX() const;
  int   getY() const;

  Cell(const int&, const int&);
  ~Cell();

private :

  int   _x;
  int   _y;
};

The cell.cpp code :

void        Cell::setX(const int& x)
{
  this->_x = x;
}

void        Cell::setY(const int& y)
{
  this->_y = y;
}

int     Cell::getX() const
{
  return this->_x;
}

int     Cell::getY() const
{
  return this->_y;
}

Cell::Cell(const int& x, const int& y)
{
  this->_x = x;
  this->_y = y;
}

Cell::~Cell()
{}
Kernael
  • 3,270
  • 4
  • 22
  • 42
  • 1
    It seems to me that you're in a multithreaded environment (or I don't see the point of using a static method for `moveRoutine`). Maybe another thread re-initialize the data in the same time. And to create a thread in C++, better use `std::thread` and eventually `std::bind` if needed, this will preserve the class structure and prevent the use of `void*` – Jaffa Apr 02 '14 at 12:51
  • The only thread I am using here is the one that calls moveRoutine. Moreover, if I set any other attribute inside moveSnake(), it will work, so I was guessing that the problem came from the fact that I was setting the Cell inside my vector this way, but I can't see why. – Kernael Apr 02 '14 at 12:55
  • Can you show the code for set() and get()? I do not see any reason why your code shouldn't work. @Geoffrey: How could another thread interfere there? Unless there is a reset() method or similar in Snake. The shown test code for setting and getting is in a single thread, obviously, so there shouldn't be an issue with missing write-throughs. – Peter - Reinstate Monica Apr 02 '14 at 12:57
  • I edited my question with the getters and the setters, and I added an example of what is actually working with the _direction attribute. – Kernael Apr 02 '14 at 13:01
  • Discarding type information, as you're doing with use of **`void*`**, is asking for trouble. Then you got trouble. Surprise? – Cheers and hth. - Alf Apr 02 '14 at 13:15
  • What does `[...]` do at the end of `Nibbler::moveSnake` ? Does it modify the `_body` vector ? – Sander De Dycker Apr 02 '14 at 13:30
  • Do you modify your snake's _body anywhere else? I think the problem is related to the std::vector you have for _body but there isn't enough code to tell. – jhaight Apr 02 '14 at 19:17

1 Answers1

2

On its face, your question ("why does this member not get modified when it should?") seems reasonable. The design intent of what has been shown is clear enough and I think it matches what you have described. However, other elements of your program have conspired to make it not so.

One thing that may plague you is Undefined Behavior. Believe it or not, even the most experienced C++ developers run afoul of UB occasionally. Also, stack and heap corruption are extremely easy ways to cause terribly difficult-to-isolate problems. You have several things to turn to in order to root it out:

Debuggers (START HERE!)

  • with a simple single-step debugger, you can walk through your code and check your assumptions at every turn. Set a breakpoint, execute until, check the state of memory/variables, bisect the problem space again, iterate.

Static analysis

  • Starting with compiler warnings and moving up to lint and sophisticated commercial tools, static analysis can help point out "code smell" that may not necessarily be UB, but could be dead code or other places where your code likely doesn't do what you think it does.
  • Have you ignored the errors returned by the library/OS you're making calls into? In your case, it seems as if you're manipulating the memory directly, but this is a frequent source of mismatch between expectations and reality.
  • Do you have a rubber duck handy?

Dynamic analysis

  • Tools like Electric Fence/Purify/Valgrind(memcheck, helgrind)/Address-Sanitizer, Thread-Sanitizer/mudflaps can help identify areas where you've written to memory outside of what's been allocated.

If you haven't used a debugger yet, that's your first step. If you've never used one before, now is the time when you must take a brief timeout and learn how. If you plan on making it beyond this level, you will be thankful that you did.

If you're developing on Windows, there's a good chance you're using Visual Studio. The debugger is likely well-integrated into your IDE. Fire it up!

If you are developing on linux/BSD/OSX, you either have access to gdb or XCode, both of which should be simple enough for this problem. Read a tutorial, watch a video, do whatever it takes and get that debugger rolling. You may quickly discover that your code has been modifying one instance of Snake and printing out the contents of another (or something similarly frustrating).

If you can't duplicate the problem condition when you use a debugger, CONGRATULATIONS! You have found a heisenbug. It likely indicates a race condition, and that information alone will help you hone in on the source of the problem.

Community
  • 1
  • 1
Brian Cain
  • 14,403
  • 3
  • 50
  • 88