type-casting pointers

Started by
1 comment, last by snk_kid 18 years, 8 months ago
I was looking through some of the articles here on gamedev.net and I stumbled upon this code:

namespace nTreeData
{
   const int kMaxLeaves = 10;
   const int kMaxDepth = 5;
}

class Leaf
{
public:

   Leaf() : value_(0) {}
   explicit Leaf(const int &value) : value_(value) {}

   const int &value() const { return value_; }

   bool operator==(const Leaf &rhs) const
      { return this->value() == rhs.value(); }
   bool operator<(const Leaf &rhs) const
      { return this->value() < rhs.value(); }

private:
   int value_;
};

#include <map>
#include <iostream>

typedef std::map<Leaf, int> LeafMapConcrete;
typedef std::map<Leaf, int>* LeafMapPointer;
typedef std::map<Leaf, LeafMapPointer > LeafMap;

void fun()
{
   using namespace nTreeData;
   LeafMap leafTree;

   ////////////////////////////////////////////////////
   // create a simple leaf tree
   ////////////////////////////////////////////////////
   for (int i = 0; i < kMaxLeaves; ++i)
   {
      // insert a starter leaf
      LeafMapPointer p = new LeafMapConcrete;
      leafTree.insert(LeafMap::value_type(Leaf(i), p));
      LeafMap::iterator iter = leafTree.find(Leaf(i));

      // continue inserting children inside of children
      for (int depth = 0; depth < kMaxDepth; ++depth)
      {
         LeafMapPointer inner = new LeafMapConcrete;
         LeafMap* outer = (LeafMap*)(iter->second);
         outer->insert(LeafMap::value_type(Leaf(depth), inner));

         iter = outer->find(Leaf(depth));
      }
   }

   ////////////////////////////////////////////////////
   // deallocate the leaf tree
   ////////////////////////////////////////////////////
   for (LeafMap::iterator destroy = leafTree.begin();
        destroy != leafTree.end();
        ++destroy)
   {
      LeafMap::const_iterator inner = destroy;
      LeafMap* iterMap = (LeafMap*)(destroy->second);
      LeafMap* lastMap;

      for (inner = iterMap->begin(); inner != iterMap->end();
           inner = iterMap->begin())
      {
         lastMap = iterMap;
         // move the iterMap forward
         iterMap = (LeafMap*)inner->second;
         delete lastMap;
      }
   }   
}


My question is what exactly is happening when one type casts LeafMapPointer to pointer to LeafMap? Insert is called on the converted pointer and it seems that one can insert a Leaf and a pointer even though the class that LeafMapPointer points to (LeafMapConcrete) takes a Leaf and an int? I have also seen this code:

int x;
char* p = &x;
char[0] = 0xff;
char[1] = 0x01;
char[2] = 0xa3;
char[3] = 0xbb;


I believe what is happening here is that the int is being assingned byte by byte. Any help is appreciated. Thanks. xidis
Advertisement
Pointers store memory addresses. On most computers with most compilers, the pointer representation is just an number that is the memory address. 0 for the null pointer, and some positive number for other addresses. When you have a compiler where the size of an int is the same as the size of a pointer, you can cast an int to a pointer and vice versa without losing any information. This is generally bad mojo and you better know what you're doing before you rely on code that does this, but it can be useful at times.

Though judging from that code snippet this isn't one of the times it's really necessary. I don't know why the author used such an ugly hack.

The second one has the int being assigned byte by byte. However, this may not do what you expect on your hardware. Integers representations on computer hardware come in two flavors: little endian or big endian. Wintel computers are little endian and PowerPC Macs are big endian. Endianess determines which byte is least significant and which byte is most significant. Little endian computers put the least significant byte first. This makes casts between integer types easier. Big endian computers put the most significant byte first. This makes more logical sense if you look at the binary layout. So the byte by byte assignment may be for making an endian independent storage of the int. Or it could be for some other reason. Without seeing more context on the code it's hard to tell; but endianess is the most likely reason.
The first snippet is abit of an atrocity for various reasons i'll explain shortly.

The bit your confused with the one with the cast, that cast is using a C-style cast which you in general should completely avoid in C++ use C++ cast operators, in this case the cast is no doubt equivalent to reinterpret_cast an unsafe, non-portable cast, a blind cast.

The reason why that may just about work for you is a pointer is a variable that holds the address of other variables in memory, a memory address is just an integral value (i don't mean type) so techincally you can store that value in an int assuming an int is large enough to hold all valid addresses of a pointer to a particular type. So basically he/she is making the assumption that a std::map<Leaf, int> is equivalent to std::map<Leaf, LeafMapPointer> because he/she views an LeafMapPointer as just an int.

Something not related to your problem and not a syntactic problem but one of efficiency, every time he/she inserts a new element he/she then uses std::map::find to get an iterator to where the new element was inserted but that is naive and unnecessarilly redundant as that version of std::map::insert returns an std::pair<std::map::iterator, bool> hence it already gives an iterator to the newly inserted element (if an insertion was made) without a need to search for it.

The second snippet is just not portable [grin].

The first snippet is really bad, its not even a cute trick i suggest looking else where.

[Edited by - snk_kid on August 20, 2005 5:06:15 PM]

This topic is closed to new replies.

Advertisement