Serialization in C

E

Ersek, Laszlo

typedef struct base_s {
  int tag;

} BASE;

typedef struct point_s {
  BASE b;
  double x, y, z,

} POINT;

typedef struct name_s {
  BASE b;
  char name[10];

} NAME;

if (POINT_TAG == p->tag) handle_point((POINT *)p);

Or, in your original example, forget BASE completely, and pass around
pointers-to-int ((int*)).

if (POINT_TAG == *p) handle_point((POINT *)p);

lacos- Hide quoted text -

- Show quoted text -

Alas, no. Other articles in this group indicate, and they appear
correct, that this is not compliant because the p in p->tag is not
"suitably converted." If p does not point to a BASE (in my example),
p->tag has no meaning under the Standard.

In your example, you'd throw out BASE. You would construct NAME and POINT
instances, take their addresses, and immediately cast those pointers to
(int *), then pass around those pointers-to-int. This is explicitly
allowed by the standard. Based on the int value pointed-to by the
pointer-to-int, you can cast back te pointer correctly, which is also
explicitly allowed by the standard. All because the "tag" member is the
initial member of each "derived" struct. Note that you do not convert
between pointers to different "derived" struct instances, you convert only
between pointer-to-base and pointer-to-specific-derived, any given time.

I've seen an example similar to my BASE / POINT where a certain version
of GCC actually demonstrated bad behavior because a record containing a
tag and a double was aligned differently that one containing only a tag.

That is no problem. You don't try to access one as if it was the other.
That would be type-punning. (I hope this time I used the term correctly.)
However, I strongly suspect both alignments were correct for the initial
member!


#include <stdio.h>

enum tag
{
TAG_A,
TAG_B
};


struct a
{
enum tag tag;
double d;
};

struct b
{
enum tag tag;
};


static void
f(const enum tag *tagged)
{
switch (*tagged) {
case TAG_A:
(void)fprintf(stdout, "%g\n", ((const struct a *)tagged)->d);
return;

case TAG_B:
(void)fprintf(stdout, "nothing to see here, move along\n");
return;
}
(void)fprintf(stderr, "nonsense!");
}


int
main(void)
{
struct a a = { TAG_A, -1.0 };
struct b b = { TAG_B };

f(&a.tag);
f(&b.tag);
f((enum tag *)&a);
f((enum tag *)&b);

return 0;
}


If this doesn't work with the compiler in question, then I'm willing to
call that compiler (or compiler mode) non-conformant. Note that the code
doesn't try to access a "struct a" instance as "struct b" or vice versa.
It only alternates between "struct X *" and "enum tag *", that is pointer
to the whole structure, and pointer to its first member. (The first member
can be a structure itself, like BASE.)

Cheers,
lacos
 
G

Gene

typedef struct base_s {
  int tag;
} BASE;
typedef struct point_s {
  BASE b;
  double x, y, z,
} POINT;
typedef struct name_s {
  BASE b;
  char name[10];
} NAME;
if (POINT_TAG == p->tag) handle_point((POINT *)p);
Or, in your original example, forget BASE completely, and pass around
pointers-to-int ((int*)).
if (POINT_TAG == *p) handle_point((POINT *)p);
lacos- Hide quoted text -
- Show quoted text -
Alas, no.  Other articles in this group indicate, and they appear
correct, that this is not compliant because the p in p->tag is not
"suitably converted."  If p does not point to a BASE (in my example),
p->tag has no meaning under the Standard.

In your example, you'd throw out BASE. You would construct NAME and POINT
instances, take their addresses, and immediately cast those pointers to
(int *), then pass around those pointers-to-int. This is explicitly
allowed by the standard. Based on the int value pointed-to by the
pointer-to-int, you can cast back te pointer correctly, which is also
explicitly allowed by the standard. All because the "tag" member is the
initial member of each "derived" struct. Note that you do not convert
between pointers to different "derived" struct instances, you convert only
between pointer-to-base and pointer-to-specific-derived, any given time.
I've seen an example similar to my BASE / POINT where a certain version
of GCC actually demonstrated bad behavior because a record containing a
tag and a double was aligned differently that one containing only a tag..

That is no problem. You don't try to access one as if it was the other.
That would be type-punning. (I hope this time I used the term correctly.)
However, I strongly suspect both alignments were correct for the initial
member!

       #include <stdio.h>

       enum tag
       {
         TAG_A,
         TAG_B
       };

       struct a
       {
         enum tag tag;
         double d;
       };

       struct b
       {
         enum tag tag;
       };

       static void
       f(const enum tag *tagged)
       {
         switch (*tagged) {
           case TAG_A:
             (void)fprintf(stdout, "%g\n", ((const struct a *)tagged)->d);
             return;

           case TAG_B:
             (void)fprintf(stdout, "nothing to see here, move along\n");
             return;
         }
         (void)fprintf(stderr, "nonsense!");
       }

       int
       main(void)
       {
         struct a a = { TAG_A, -1.0 };
         struct b b = { TAG_B };

         f(&a.tag);
         f(&b.tag);
         f((enum tag *)&a);
         f((enum tag *)&b);

         return 0;
       }

If this doesn't work with the compiler in question, then I'm willing to
call that compiler (or compiler mode) non-conformant. Note that the code
doesn't try to access a "struct a" instance as "struct b" or vice versa.
It only alternates between "struct X *" and "enum tag *", that is pointer
to the whole structure, and pointer to its first member. (The first member
can be a structure itself, like BASE.)

Okay. Great. I appreciate this. Though it makes my head hurt that a
pointer to the tag must be used to encode the pointer to its enclosing
struct. This is want I wasn't getting.

Gene
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,085
Messages
2,570,597
Members
47,220
Latest member
AugustinaJ

Latest Threads

Top