1

I'm trying to work with the offsetof macro in the following way:

typedef unsigned char u8;
typedef unsigned short u16;

struct MapBlock
{
  u16 type : 10;
  u8 variant : 3;
  bool isTop : 1;
};

struct MapTile
{
  struct MapBlock top, middle;
  u16 x;
  u8 y;
};

MapTile *tfb(struct MapBlock *block)
{
  if (block->isTop)
    return (MapTile*)block;
  else
    return (MapTile*)((u8*)block - offsetof(struct MapTile, middle));
}

and it seems to work with this simple test case:

for (int i = 0; i < width; ++i)
  for (int j = 0; j < height; ++j)
  {
    map[i][j].x = i;
    map[i][j].y = j;
    map[i][j].top.isTop = true;
    map[i][j].middle.isTop = false;
  }

printf("%p == %p == %p\n",tfb(&map[40][50].top),tfb(&map[40][50].middle),&map[40][50]);

Now the real questions:

  • is it safe? (assuming to work with g++)
  • will it work with any standard optimization? (mainly -O2)
  • is there a better way to do what I'm doing? (I need to do it to avoid storing x and y for every MapBlock but be able to access them through block without knowing related MapTile)
  • can I avoid the cast to u8 when subtracting the offset? I guess no but I just wanted to be sure..

Thanks

Jack
  • 131,802
  • 30
  • 241
  • 343
  • It is most definitely unsafe to whomever tries to read that code. – Hans Passant Mar 03 '12 at 17:33
  • That's not what I'm asking, since I'm the only one working on the code and if I'm looking for a solution that can save some bytes it means that I really need it, don't you think? :) – Jack Mar 03 '12 at 17:37
  • 1
    Your `tfb` function clearly assumes that `top` will be the first thing in the structure. Why not go all the way with that, and assume that it's immediately followed by `middle`? Then you could write `(MapTile*)(block - 1)`, which is less future-proof but more clear. – ruakh Mar 03 '12 at 17:53
  • The `MapTile` struct won't change, so I can definitely do it.. I just thought that I would end up obtaining the same thing since `offsetof` is a macro – Jack Mar 03 '12 at 17:56
  • You could make top and middle a two-element array, then the subtract 1 trick would be safe. But I believe your offsetof is fine; you've followed the rules so the compiled must correctly translate it. – Alan Stokes Mar 03 '12 at 19:03
  • This sounds like microoptimisation. Are you sure that you cannot rearrange your data structure in another way to save more ? (is it sparse ? is there dups ?, is there groups ?) – BatchyX Mar 03 '12 at 19:12
  • It's not sparse, it doesn't have neither dupes nor groups so I'm forced to store all of them individually. But since the map will be really I'm going to save a lot of space if this works seamlessly. – Jack Mar 03 '12 at 19:13
  • The code is perfectly understandable as is. It wouldn't hurt to subtract offsetof(struct MapFile, top) in the first case. This should be optimized away when top is the first member. – Kaz Mar 05 '12 at 23:33

1 Answers1

0

Actually evertything seemed to work really fine with my solution. The optimizations suggested in comments (using block - 1) did work too so feel free to use my example if you have a similar problem!

Jack
  • 131,802
  • 30
  • 241
  • 343