The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Old style C structs and mapping.

I've been given a pile of code, the heart of which is a C struct (the compiler is VS 6.0) It's extremely long, but here's a short version:

struct AAA{
char foo[4];
char bar[4];
char foobar[4];
}

...now, this maps up with a particular file format. Problem is, there is another file format which is very similar, but not exactly so. ALMOST the exact same struct is needed:

struct BBB {
char foo[4];
char bar[6];
char foobar[4];
}

...there is a method that takes the struct and uses foo and bar and foobar to map the bytes in the input files. Okay. So, the simplest way to solve the problem is to use #ifndef and make two processes, one for each file.

That brings up the problem of having a separate program for each format. Is there a way to essentially pass the process() method an instance of AAA or BBB and have it it ->foo and ->bar etc and resolve the different sizes?  Essentially, is there a way to make a generic struct (or class) and define the arrays in sub classes?

(Yes, there are ugly ways, but I'm trying to avoid that...)
anonymouser
Wednesday, November 28, 2007
 
 
Perhaps you want to create a template class that is templated on the particular struct?
BillAtHRST Send private email
Wednesday, November 28, 2007
 
 
I might be inclined to concoct an idealised data format without care for how it looks on disk. Then for each format provide functions to transform the disk format into the idealised format and, if desired, vice versa. New file formats may then be added straightforwardly, and all the details are localized in the source text.

The rest of the program deals with the idealized format, so it is less likely to need changing to support new file formats and so on.

I can't judge from your post whether this would be a useful time-saving suggestion or a huge dose of over-engineering :)
Tom_
Wednesday, November 28, 2007
 
 
The dynamic format option has been suggested, and I'm avoiding it mainly because we don't have 1..n formats, but strictly 1..3. So, a hack is in order.

(Also, a hack is in order because we are simply massaging this before selling it lock stock and barrel. None of us will be maintaining it.)
anonymouser
Wednesday, November 28, 2007
 
 
This is the reason why i want to run away, far away from C/C++ code. Please god let the pain stop.
Entity Send private email
Wednesday, November 28, 2007
 
 
"Essentially, is there a way to make a generic struct (or class) and define the arrays in sub classes? "

Yes, sort of. You can safely assume that if two structs have identical prefixes, those part of the structs are laid out the same. So, assuming the structure definitions cannot be changed:

typedef void (*process_fn)(void *);

void process(void *thingy, process_fn frob)
{
  do_stuff_with_foo(((struct AAA *)thingy)->foo);
  frob(thingy);
}

The cast is technically illegal if you pass a BBB, but you should always get away with it because the underlying assumption about the location of foo is valid.

It's also guaranteed that a pointer to a structure may be converted to a pointer to its first member, and vice versa. So you could do this:

struct base {
  char foo[4];
  process_fn frob;
};

struct AAA{
struct base base;
char bar[4];
char foobar[4];
}

struct BBB {
struct base base;
char bar[6];
char foobar[4];
}


void process(struct base *thingy)
{
  do_stuff_with_foo(thingy->foo);
  thingy->frob(thingy);
}

void frob_AAA(struct base *b)
{
  /* This cast is perfectly valid and portable standard C */
  struct AAA *thingy = (struct AAA *)b;
  do_stuff_with(thingy->bar);
}

struct AAA mything;
mything.base.frob = frob_aaa;
process(&(mything.base));
/* Or this way, also portable */
process((struct base *)&mything);
clcr
Wednesday, November 28, 2007
 
 
I think the problem is that if I pass BBB to this:

struct AAA *thingy = (struct AAA *)b;

...it will cast without error, but my offsets are still going to be 2 off. (I'm only eyeballing your code...I might be missing something.)

I worked out a nasty solution that involves what I'll call "runtime #ifdef" processing...meaning I copy-and-pasted a massive loop leaving the new loops with one line differences:

CCC * ccc=(CCC *)param;

...and

BBB * bbb= (BBB *)param;

...and

AAA * aaa= (AAA *)param;

It works. It blows.
anonymouser
Wednesday, November 28, 2007
 
 
Couldn't you use the largest structure, BBB, and just remember to only use the first 4 chars of bar when its actually an AAA?  Need a flag member to identify which type of structure you have.

cheers
Honu
Thursday, November 29, 2007
 
 
I take it you're using plain old C if the template suggestion doesn't suit. If an "idealised" format is inappropriately heavyweight, that would be my next suggestion.

Another possibility is to put your duplicated loop in a file, and insert references to #defines in the places that need changing for each instance. Then #include this file 3 times in your project, with a different set of #defines each time (according to necessity), including one to change the function name. You can do this in 3 files, or in 1 (with #undef) as you see fit.

You want a different function name each time, rather than (say) including it in 3 seperate files, making it static, and keeping it with the same name, because when you're debugging the result you need to tell which version you're stepping through. With a different name for each "instantiation", you can determine this from the return stack in the debugger.

This is not very nice, but it keeps the code in one place.
Tom_
Thursday, November 29, 2007
 
 
How about a new struct that has pointers to the fields in each struct?

struct QQQ
{
  char *foo;
  char *bar;
  char *foobar;
}


func(struct AAA *a, struct BBB *b)
{
  struct QQQ qA, qB;

  qA.foo = a->foo;
  qA.bar = a->bar;
  qA.foobar = a->fooBar;

  qB.foo = b->foo;
  qB.bar = b->bar;
  qB.foobar = b->fooBar;

  processFunc(&qA);
  processFunc(&qB);

processFunc(struct QQQ *q)
{
  fooFunc(q->foo);
  barFunc(q->bar);
  foobarFunc(q->fooBar);
}
JB Send private email
Thursday, November 29, 2007
 
 
Is there only one struct used in the code but 3 different file formats which need to be read into that?  Or 3 structs used in the code and 3 different file formats?
Cade Roux Send private email
Thursday, November 29, 2007
 
 
You need to be really careful.  Unless you have set up the compilation carefully, you don't even know that you will be two bytes off.  Depending on the compiler/hardware, the structure may be aligned in some way that you aren't aware of.

Probably the easiest thing is to pass the three variables separately into the processing function, or if that would be problematic, pass the offset of each variable.  As someone else suggested, you could just use the larger structure and provide some way to pass type information.

It's hard to suggest much more without knowing the structure of the code you are calling.
Andrew Bell Send private email
Friday, November 30, 2007
 
 
The underlying problem here is that you've got three very similar structs, but you've told the compiler that they are actually completely unrelated. Therefore you either find yourself writing code to do almost the same thing to almost the same info, or doing unsafe casting to relate type A to type B.

How to express the similarity? You need to change the definition of the structs a bit. Define a new struct (or several, as needed) which express the common parts of your other struct. Suppose it's called D. Then include D as a member of A,B and C.

struct A
{
  stuff not the same everywhere....
  D commonstuff;
};

struct B
{
  stuff not the same everywhere.....
  D commonstuff;
};

You can then make yourself functions which read and interpret the D.

If the structs are already found everywhere and you can't change the layout without rewriting the world, you can use union to overlay the old syntax with the new one.

Duncan
Duncan Sharpe
Friday, November 30, 2007
 
 
struct AAA
{
unsigned short type;
char foo[4];
char bar[4];
char foobar[4];
};

struct BBB
{
unsigned short type;
char foo[4];
char bar[6];
char foobar[4];
};

union AAAandBBB
{
unsigned short type;
AAA aaa;
BBB bbb;
}



void adapterFunctionForUnionAAAandBBB(union AAAandBBB *u)
{
  switch(u->type)
  {
    case AAAtype:
      functionThatHandlesAAAType((struct AAA*)u);
      break;
    case BBBtype:
      functionThatHandlesBBBType((struct BBB*)u);
      break;
  }
  throw std::exception("Whoopsee");
}
Fake Programmer
Friday, December 07, 2007
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz