Thread: Unions and void pointers

  1. #1
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057

    Unions and void pointers

    There are at least two questions in this post. I thought it might be somewhat hard to find them amongst all of the text, so I made them green.

    ----

    I recently wrote some code that used a union of pointers to implement a sort of inheritance:
    Code:
    struct cat_t {
        int mice_caught;
    };
    
    struct dog_t {
        int squirrels_chased;
        int bones_hidden;
    };
    
    enum type_t {
        ANIMAL_CAT,
        ANIMAL_DOG
    };
    
    /* The union method */
    struct animal_t {
        const char *name;
        enum type_t type;
    
        union {
            struct cat_t *cat;
            struct dog_t *dog;
        } p;
    };
    (This code is, of course, entirely fictional. But the real code was much the same.)

    Anyway, I thought of doing this.
    Code:
    /* The union-with-void-pointer method */
    struct animal_t {
        const char *name;
        enum type_t type;
    
        union {
            struct cat_t *cat;
            struct dog_t *dog;
            void *all;
        } p;
    };
    That way, I could use
    Code:
    free(animal.p->all);
    and perhaps even
    Code:
    animal.p->all = malloc(sizeof_animal(type));
    
    size_t sizeof_animal(enum type_t type) {
        switch(type) {
        case ANIMAL_CAT:
            return sizeof(struct cat_t);
        /* ... */
        }
    }
    But I don't know if that would be a good idea. It relys on the fact that sizeof(struct cat_t *) == sizeof(struct dog_t *) == sizeof(void *), which is probably not a good idea.

    It's not really that important; I can just free() and malloc() using the right pointer. But I was wondering if this is standard and portable. Can one to rely on the sizeof() a void pointer being the same as the sizeof() a pointer to a structure?

    ----

    As for the union of void pointers, another way of implementing it would be:
    Code:
    /* The void pointer method */
    struct animal_t {
        const char *name;
        enum type_t type;
    
        void *p;
    };
    But then a cast would be required.
    Code:
    animal.p.cat->mice_caught;  /* union method */
    ((struct cat_t *)animal.p)->mice_caught;  /* void pointer method */
    I thought the first method was a little cleaner. But, of course, the second would allow free()ing and malloc()ing the data without worrying about sizeof(void *).

    Does anyone else have some other ideas of how to implement this quasi-inheritance? (No, I don't really want to use C++. )
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  2. #2
    Registered User
    Join Date
    Oct 2001
    Posts
    2,129
    what usage do you want for the code that uses the structs? are you adverse to init and cleanup functions?

  3. #3
    Technical Lead QuantumPete's Avatar
    Join Date
    Aug 2007
    Location
    London, UK
    Posts
    894
    Quote Originally Posted by dwks View Post
    Can one to rely on the sizeof() a void pointer being the same as the sizeof() a pointer to a structure?
    In C, yes! all pointers, regardless of what they point to are the same size. This is essentially, because they all hold an address to a piece of memory. The pointer itself is not concerned with what you're storing there, it could be an int, the first element of a char array or a cat (struct).

    QuantumPete

  4. #4
    Woof, woof! zacs7's Avatar
    Join Date
    Mar 2007
    Location
    Australia
    Posts
    3,459
    Quote Originally Posted by QuantumPete View Post
    In C, yes! all pointers, regardless of what they point to are the same size. This is essentially, because they all hold an address to a piece of memory. The pointer itself is not concerned with what you're storing there, it could be an int, the first element of a char array or a cat (struct).

    QuantumPete
    According to Salem, apparently not http://cboard.cprogramming.com/showp...87&postcount=5

  5. #5
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    Quote Originally Posted by dwks View Post
    Does anyone else have some other ideas of how to implement this quasi-inheritance? (No, I don't really want to use C++. )
    Well, how serious do you want to go with this?

    A very serious solution woulc be something along the lines of using shared libaries (ie. .dlls on Windows) where each one represents a new type. This way you can add new types as you go along. Each library could contain the implementation of new data types that you void pointer would point to, as well as known functions that return the size of the data type that you are using and other such things.

    If you want a less serious solution, then I guess you're on the right track. C++ would probably be better for a true OOP, but since you're against it, I'd just stick with the unions that have known types and a void * if you know you'll have unknown types.
    Last edited by MacGyver; 08-30-2007 at 01:59 AM.

  6. #6
    Technical Lead QuantumPete's Avatar
    Join Date
    Aug 2007
    Location
    London, UK
    Posts
    894
    Quote Originally Posted by zacs7 View Post
    I think he meant across different machines. I.e. you can't rely on your *nix system to have the same size pointers as a BBC compact or a Vista machine.
    On the same machine however, all pointers are the same size (you can after all cast any pointer to any other pointer!)
    This is like the fact that on most machines an int is 32 bits, but it doesn't *have* to be. Same for a pointer, they are normally 32 bits (i.e. a WORD, i.e. the size of your address bus) but this can vary from machine to machine.

  7. #7
    Registered User
    Join Date
    Sep 2006
    Posts
    835
    No, he meant for a given machine. Read the link.

    http://www.c-faq.com/null/machexamp.html

  8. #8
    Technical Lead QuantumPete's Avatar
    Join Date
    Aug 2007
    Location
    London, UK
    Posts
    894
    :-S I'm not convinced. I don't think anyone here is programming on Cray mainframes, old HP3000, or Honeywell-Bull machines.
    Given your good old SUN or IBM machines, I think you can safely assume that all pointers are the same size (how else would GTK work otherwise, which does nothing but cast pointers?!)

    QuantumPete

  9. #9
    Woof, woof! zacs7's Avatar
    Join Date
    Mar 2007
    Location
    Australia
    Posts
    3,459
    Agreed, but what if someone had a <insert arch here> PC, and they wanted a pet database program, oh noes!

  10. #10
    Technical Lead QuantumPete's Avatar
    Join Date
    Aug 2007
    Location
    London, UK
    Posts
    894
    I give up :-P

  11. #11
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > size_t sizeof_animal(enum type_t type)
    In the same vein, you should implement the malloc_animal and free_animal functions.

    Eg.
    Code:
    animal_t *malloc_animal(enum type_t type) {
      animal_t *a = malloc ( sizeof *a );
      a->type = type;
      a->name = NULL;
      switch ( type ) {
        case ANIMAL_CAT:
          a->p.cat = malloc ( sizeof *a->p.cat );
          break;
        default:
          // diagnostic for unknown animal
      }
      return a;
    }
    Likewise, free_animal() also has a switch/case.

    No casting magic, no assumptions on the representation of different pointer types, and the ability to add some additional diagnostic if you need to.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Quote Originally Posted by zacs7
    Well, how serious do you want to go with this?

    A very serious solution woulc be something along the lines of using shared libaries (ie. .dlls on Windows) where each one represents a new type. This way you can add new types as you go along. Each library could contain the implementation of new data types that you void pointer would point to, as well as known functions that return the size of the data type that you are using and other such things.

    If you want a less serious solution, then I guess you're on the right track. C++ would probably be better for a true OOP, but since you're against it, I'd just stick with the unions that have known types and a void * if you know you'll have unknown types.
    I don't think that it's "serious" enough to warrant separate shared libraries and all that, though it's a good idea and one I'll certainly keep it in mind.

    I don't have any unknown types, so there's no reason to use a void* pointer on that count. It was just there to facilitate allocation and freeing.

    Given your good old SUN or IBM machines, I think you can safely assume that all pointers are the same size (how else would GTK work otherwise, which does nothing but cast pointers?!)

    QuantumPete
    I think it would probably use (I've never used it myself) this sort of code:
    Code:
    animal.p.cat->mice_caught;  /* union method */
    ((struct cat_t *)animal.p)->mice_caught;  /* void pointer method */
    Like you said, this is casting. There's nothing wrong with it, even if void pointers and struct cat_t pointers are different sizes, because the compiler will know how to convert between the types. It's what I was doing, treating a void pointer and a struct cat_t pointer as exactly the same size and edianness and everything, that is suspect.

    Quote Originally Posted by robwhit
    are you adverse to init and cleanup functions?
    > size_t sizeof_animal(enum type_t type)
    In the same vein, you should implement the malloc_animal and free_animal functions.
    I think that's what I'll do. It's what I had, actually, until I hit upon this brilliant idea to use a void pointer . . .
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Unions & Structs... Puzzled.
    By Sparrowhawk in forum C Programming
    Replies: 10
    Last Post: 12-14-2008, 04:45 PM
  2. IDEA: Polygon unions and intersections
    By Magos in forum Contests Board
    Replies: 3
    Last Post: 05-21-2003, 07:16 PM
  3. unions
    By mickle in forum C Programming
    Replies: 6
    Last Post: 02-27-2003, 11:46 PM
  4. unions / functions
    By task in forum C Programming
    Replies: 2
    Last Post: 07-31-2002, 01:20 PM
  5. unions and files
    By Unregistered in forum C Programming
    Replies: 4
    Last Post: 09-04-2001, 03:25 PM