2

I am working on city generation for a pcg game of mine. I have a for loop which makes 3 cities in random locations, I assign parentIteration to get that "id" for the city and do the same in the for loop where I make a building

for (int i = 0; i < 3; i++)
{
    parentIteration = i;
    std::srand(i);
    _rootNode = GameManager::getSingletonPtr()->getSceneManager()->getRootSceneNode();

    _cityNode = _rootNode->createChildSceneNode("cityNode " + parentIteration);
    generateCity(std::rand() % 10000 + 10, std::rand() % 10000 + 10, std::rand() % 11 +1);
}

building

for (int i = 0; i < _numberOfBuildings; i++)
    {
        childIteration = i;
        printf(" parent  %d and child %d \n", parentIteration, childIteration);
        Ogre::SceneNode* buildingNode  = _cityNode->createChildSceneNode("citybuildingNode"+childIteration+parentIteration );
}

However when I try to launch the game it will crash on creating the second city. Saying it already has a name similar to what it is trying to write. Yet my printf clearly show that the numbers at that point are all unique. Anyone know how to resolve this issue? (added picture for proof of output)

enter image description here

Scott Stensland
  • 26,870
  • 12
  • 93
  • 104
KevinTheGreat
  • 634
  • 5
  • 22
  • are you sure that `"citybuildingNode"+childIteration+parentIteration` does what you think it does? Because I think you meant to do a string concatenation and not pointer arithmethic. `std::string("citybuildingNode")+childIteration+parentIteration` seems more appropriate – PeterT Apr 06 '17 at 18:37
  • `cityNode = _rootNode->createChildSceneNode("cityNode " + parentIteration);` -- I do not know `Ogre`, but that parameter you're passing -- what is the actual type that is expected? You are adding a string literal and an `int`. That isn't going to work as expected, unless there is some magic going on that I'm not aware of. – PaulMcKenzie Apr 06 '17 at 18:37
  • [Possible duplicate](http://stackoverflow.com/questions/191757/how-to-concatenate-a-stdstring-and-an-int). This is probably what your problem boils down to. C++ does not work with string literals as does other languages. You can't just concatenate a number onto a literal and get a string that is what you expect. – PaulMcKenzie Apr 06 '17 at 18:44

1 Answers1

2

The "itybuildingNode" in the error message suggests that

"citybuildingNode"+childIteration+parentIteration

is not working quite the way you wanted.

This is because of a couple things working against you:

  1. "citybuildingNode" is a String Literal, and not a string object. It is litteraly just a bunch of characters in a row terminated by a null character and represented as a const char *, a pointer to that array of characters. It is low-level voodoo, the sort of stuff you might make a string class around. For more information see String Literals

  2. Because it's not a string object, you can't pull any of the usual string object tricks like concatenating with a + and comparing with ==. But because it is a pointer, the compiler interprets + as an attempt to perform pointer arithmetic and reference another location in the array. It compiles, but note how it turned "citybuildingNode" into "itybuildingNode". Oops.

What this looks like is something like:

const char* temp = "citybuildingNode"
_cityNode->createChildSceneNode(temp + childIteration + parentIteration);

which resolves to

const char* temp = "citybuildingNode"
_cityNode->createChildSceneNode(&temp[childIteration + parentIteration]);
  1. Even if it was a string object, the C++ standard string object, std::string does not allow you to add numbers to strings. It only adds strings together to build a bigger string. To add a number to a std::string, you have to turn the number into a std::string. std::to_string can help you here, but there is a cleaner-looking way to do this with std::stringstream

Eg:

std::stringstream nodename("citybuildingNode"); 
// builds a string stream around the string literal
nodename << childIteration << parentIteration;
// writes the numbers into the stream the same way `cin << number;` would
// turning the number into a string for you
Ogre::SceneNode* buildingNode  = _cityNode->createChildSceneNode(nodename.str());
// gets the assembled string from the stringstream 
// null-terminated string like ogre expects

This gets you started in the right direction, but still allows for collision between child 1 and parent 10 ("citybuildingNode110") and child 11 and parent 0 (also "citybuildingNode110") and similar. So you really want something more like

nodename << childIteration << '_' << parentIteration;

to force a separator between the two numbers.

Documentation for std::stringstream.

There is also another possible nasty. The string we just supplied to ogre will only exist for as long as std::stringstream nodename exists and it will die at the end of the loop that generates it. I do not see anything in a quick perusal of the documentation that says ogre makes its own copy of this string. So play around a bit to make sure that you don't have to store this name somewhere to prevent it from falling out of scope, being destroyed, and leaving ogre with a dangling reference.

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • I would like to thanks you for the clear explanation on my mistake as well as why it was that I made the mistake and how to resolve it! – KevinTheGreat Apr 07 '17 at 08:32