Peeter Joot's (OLD) Blog.

Math, physics, perl, and programming obscurity.

Use of public inheritance to replace a typedef.

Posted by peeterjoot on February 9, 2012

I have some code that I want to conditionally compile, selecting (for now) between a compatibility interface for the vector library that we are using, and the new interface. It’s of the following form:

#if defined SOME_FEATURE_TEST_MACRO
   typedef XXInformation_t MyInfoType ;
#else
   typedef YYInformation_t MyInfoType ;
#endif

In the current code, it’s a bit of a pain to forward reference these vendor types since they are declared like:

typedef struct XXInformationInternalTagnameNeverUsedInTheCode
{
   // ...
} XXInformation_t;

Note the old style C typedef, as opposed to a more natural C++ definition of just:

struct XXInformation_t
{
   // ...
} ;

In an ideal world we would ask the vendor (and did) to change their types to be declared instead with the form

typedef struct XXInformation_t
{
   // ...
} XXInformation_t;

so that it would work with both C and C++ and would allow a forward reference ‘struct XXInformation_t’ in various headers without requiring the include. (Ironically the “vendor” here is an IBMproduct too, but there is infighting and politics and this request never went anywhere.) Because we have to live with the form of the vendor headers for now, how can we forward reference their types without pulling in their headers? This can be done by introducing a redundant typedef of the following form, exactly matching their internal structure

// HACK: "forward reference" for un-forward-reference-able type:
struct XXInformationInternalTagnameNeverUsedInTheCode ;
typedef struct XXInformationInternalTagnameNeverUsedInTheCode XXInformation_t ;

Curiously enough, this can be done without actually having the definitions of the content of the ‘struct XXInformationInternalTagnameNeverUsedInTheCode’ available. That is very handy. Formally, we really have no business knowing anything about their ‘struct XXInformationInternalTagnameNeverUsedInTheCode’ name and should they change this, we will have to alter our code. But should that actually happen, we can adjust to their changes more easily than asking them to make them, so this small possibility of a maintainance tweak is worth living with. Having chosen to do so, we can at least we can use ‘XXInformation_t’ in function prototypes of our own without dragging in their headers (which are not available on all the platforms we compile on).

You may ask why we need to be able to use these types in prototypes when we will not actually be able to compile the code that makes use of those types on all platforms. Some of this is because we have elected to introduce stub functions for some of the functions that cannot be called at runtime, but that we have to be able to “call” at runtime. So, for example we could have a function like

// header
void foo(MyInfoType * p) ;

// definition
void foo(MyInfoType * p) 
{
#if defined SUPPORTED_PLATFORM
...
#endif
}

Now back to my original problem. Having done a search and replace of all instances of both XXInformation_t and YYInformation_t with MyInfoType

I am, however, now unfortunately stuck with some compile errors I did not have before (something roughly like “structure cannot be used when the contents are not visible”). The place in our header heirarchy where I had done the “HACK: ” variation of forward reference above is much deeper than where I wanted my ‘#if defined SOME_FEATURE_TEST_MACRO’ typedefs. I could push my typedefs and the conditional compilation associated with them even deeper into the code, but they really don’t belong there. The idea that I came up with was to use a trivial form of public inheritance as an alternative form of typedef

#if defined SOME_FEATURE_TEST_MACRO
   struct MyInfoType : public XXInformation_t {} ;
#else
   struct MyInfoType : public YYInformation_t {} ;
#endif

Now my prototype for the foo function above can use a normal forward reference, with either

struct MyInfoType ; // explicit standalone forward reference for use in the rest of the header (or parts of the header hierarchy that this forward reference is visible)
void foo(MyInfoType * p) ;

or

void foo(struct MyInfoType * p) ;

None of the higher level (than mine) code has to know about the details of the internal types in question, and they know are also now not exposed to the internal details of the type at all. I’m free to introduce a stub structure for an unsupported platform if I want to, and will not have to touch the foo code to do so.

Perhaps this inheritance solution to the forward reference problem would be more obvious in a product that uses hardcore C++. Ours is really (largely) a C like product that’s been built with a C++ compiler, and interitance is discouraged. Because our source code browsing tools still only talk C well, any part of the code that uses inheritance ends up a maintanance nightmare to somebody who does not know the code, since our tools for navigating and learning the code cannot traverse inheritance trees. It is actually fairly hard to find a good source code browsing tool that is both very portable and also scalable and also supports C++ well (keeping in mind that our product is HUGE).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: