4

I am writing file conversion code from a proprietary file format to one more generic. My goal is to support multiple versions of the manufacturer's file format.

I have a multiple versions of the same proprietary headers. The headers define various structs which comprise the main file header (the file is simply a large header followed by raw data).

I need to read the first 4 bytes of the source file to determine the file version. The file version, in turn, tells me which version of the C-structs was used to create the file.

The issues are:

  1. I can't modify the proprietary headers
  2. The headers do not use namespaces or classes
  3. There are a good handful of macros defined in the headers

Possible solutions:

  • Build different converter binaries for each file version type :-(
    • Inconvenient for both user and developer
  • Dynamically load libraries for each version
    • The converter is plugin-oriented, so there's already a lot of this happening

I have tried hacking with namespaces:

namespace version1 {
    #include "version1.h"
}

namespace version2 {
    #include "version2.h"
}

int main (void) {
    version1::header *hdr = new version1::header;
    return 0;
}

But this won't work because of include guards, and because there are multiple macros are redefined in each header.

Is there an elegant way to handle this?

joe
  • 617
  • 7
  • 22

1 Answers1

5

You could use two different source files, together with a forward declaration:

// Forward declare in main.cpp:

namespace version1
{
   struct header;
}

namespace version2
{
   struct header;
}

// version1.cpp:

namespace version1
{
      #include <version1.h>
}

version1::header* new_v1_header()
{
   return new version1::header;
}

// other functions using `version1::header`

// version2.cpp:

namespace version2
{
      #include <version2.h>
}

version2::header* new_v2_header()
{
   return new version2::header;
}

// other functions using `version2::header`

Another alternative is to implement a wrapper class, which has a base-class that is just an empty shell:

class header_base
{
     virtual int func1(char *stuff) = 0; 
     ... many other virtual functions. 
};

// Create implementation of header_v1 or header_v2:
header_base* make_header(unsigned int magic);

header_base.cpp:

#include "header_v1.h"
#include "header_v2.h"

header_base* make_header(unsigned int magic)
{
    switch(magic)
    {
       case Magic_V1: 
          return new header_v1;
       case Magic_V2: 
          return new header_v2;
       default:
          assert(0);
          return 0;
    }
}

and then implement, in two separate

in headerv1.h:

class header_v1 : public header_base
{
    int func1(char *stuff);
    ... 
};

header_v1.cpp:

#include "header1.h"

int header_v1::func1(char *stuff)
{
   ... 
   return 17;
}

And similar for header_v2.h and header_v2.cpp.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 1
    This isn't a particularly great idea if the library is not header-only, because it messes with the mangled names. See http://stackoverflow.com/a/6670753/46642. – R. Martinho Fernandes Jul 10 '13 at 13:35
  • Exactly what I would do. I'd also probably declare an ABC that wraps the functionality declared by the headers, and have 2 concrete derivations of the ABC; one for each version. – John Dibling Jul 10 '13 at 13:36
  • The structs just define hundreds of primitive types, no functions. In order to use the header_base dynamically, I'd have to store a pointer to the top-level struct, then make virtual getters for each of the hundreds of struct members, then implement getters for each member for each derived wrapper class. – joe Jul 12 '13 at 18:49
  • That sounds like the right thing to do is to move the differentiation down a level or three. Or, simply change the actual header files... – Mats Petersson Jul 12 '13 at 21:01
  • Okay in the end I'm going with your alternative (second) idea... I made a virtual class to serve as an interface to the header and I'll either store (void*) references to the top-level struct so I can compile everything into one binary or fall back to dynamically loading libraries containing the implementation classes. – joe Jul 15 '13 at 20:01