Search code examples
ctypedefexternqa-c

how to share array type definition without common header file?


Situation

I'm using min GW compiler:

>bin\cpp --version
cpp.exe (GCC) 6.1.0
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

I have two header files main.h and submodule.h. For various reasons I cannot simply include one of this headers into the other.


[update]

I think you need to explain the various reasons why you cannot simply include one of this headers into the other because that's the obvious answer... – Andrew

  • I cannot import main.h into submodule.h because in that case a change in main.h would trigger a recompilation of the submodule althoug nothing changed here. Compile time is a major concern for my client.

  • I cannot include submodule.h into main.h because submodule.h defines lots of stuff but only a few definitions are public. My client wants to reduce visibility of identifiers as much as possible.

  • My client uses the content of main.h to verify compatibility of different versions of the target software. Existence and size of the mentioned array is one of the compatibility criteria. Therefore the definition of the array must stay in main.h

  • There are some versions of the target software that do not have the submodule at all. Therefore the files building this submodule may or may not be present. There is a lot of overhead (for my client) to deal with that situation which has to be done by someone else, not me. So my client also wants to limit the number of "flickery" files.


I also have lots of other *.h files that include main.h but not submodule.h, and they should not to hide some things in the sub module.

The submodule.h defines lots of stuff implemented in submodule.c. Among that is an array type definition and a global variable of that type:

typedef const char INDEX_TABLE_t[42]; 
const INDEX_TABLE_t INDEX_TABLE;

The submodule.c implements this array:

const INDEX_TABLE_t INDEX_TABLE {/* 42 random char values */};

The variable INDEX_TABLE ist used in that other *.h files:

char SOME_OTHER_INDEX[23] = {/* 23 random char values */};

#define SELECTOR_VALUE 5
#define a_fix_name INDEX_TABLE[SOME_OTHER_INDEX[SELECTOR_VALUE]]

these *.h files include main.h but not submodule.h.

Therefore I used to add the (exact same) type definition of INDEX_TABLE_t and INDEX_TABLE_t to main.h which compiles fine.

Problem

My client uses a code alaysis tool (QA-C) that complains about the doubled definition of the type INDEX_TABLE_t.

[C] More than one declaration of 'INDEX_TABLE_t' (with no linkage). 

The client instructed me to change the code so that this error will no longer issued by the code analysis tool.

I usually solve this by adding the extern keyword to all but one occurrence. But in this case the compiler throws an exception:

error: conflicting specifiers in declaration of 'INDEX_TABLE_t'

But the declaratios are equal (they are rendered based on a model).

Questions

Do I have any chance to make both happy, the compiler and the code analyser?

Is createing another header file to be included in in main.h or all the other *.h files my only option?


Solution

  • I have two header files main.h and submodule.h. For various reasons I cannot simply include one of this headers into the other.

    Then do yourself a favor and fix that, even if you don't actually #include "submodule.h" inside main.h. Your claim that you cannot do so has very bad smell.

    The submodule.c implements this array:

    const INDEX_TABLE_t INDEX_TABLE {/* 42 random char values */};
    

    You appear to have omitted an = before the initializer. Also, with INDEX_TABLE_t being an array type with const elements, I don't think the extra const there has any additional effect.

    My client uses a code alaysis tool (QA-C) that complains about the doubled definition of the type INDEX_TABLE_t.

    [C] More than one declaration of 'INDEX_TABLE_t' (with no linkage). 
    

    I suppose the tool is concerned by exactly the fact that the declaration is repeated in separate files, instead of being centralized in a single header. This is a valid concern, not so much for the program now, but for ongoing maintenance and development. You have set a trap for a future maintainer (maybe future you) wherein they might change only one of the type definitions, or change the two in incompatible ways, thus introducing a subtle but impactful bug.

    The client instructed me to change the code so that this error will no longer issued by the code analysis tool.

    I usually solve this by adding the extern keyword to all but one occurrence. But in this case the compiler throws an exception:

    error: conflicting specifiers in declaration of 'INDEX_TABLE_t'
    

    But the declaratios are equal (they are rendered based on a model).

    INDEX_TABLE_t designates a type, not an object or function. It cannot have external linkage (per extern) because it automatically and necessarily has no linkage.

    Do I have any chance to make both happy, the compiler and the code analyser?

    Yes.

    Is createing another header file to be included in in main.h or all the other *.h files my only option?

    Not exactly, but you do need to put the type definition in a single header, and have all your sources get it from there, directly or indirectly. One alternative to your idea would be to #include that header directly into your .c files, which probably would require careful management of the order of your #include statements.

    But overall, it sounds like your header collection could benefit from some refactoring. As a general rule, each header should (and should be able to) include all the headers needed to provide declarations for identifiers used but not declared within, and no other headers. This is facilitated in part by using include guards in every header. There may be other aspects to making that work if you did not design for it from the beginning, but it certainly can be done.


    In response to the edited question

    That some builds of the software do not include submodule but (presumably) do use main.h is a strong indication that main.h is the wrong place for a typedef for the type of an object of which only submodule provides an instance. It should go in a header associated with submodule or more broadly with the collection of different sources that all use this attribute of submodule.

    Perhaps that header could be submodule.h itself. Perhaps it should be a separate header, say submodule_general.h, which might even be a better place for some of the other stuff now in submodule.h, too. Perhaps there's stuff in submodule.h that doesn't need to be there, and removing it -- possibly in conjunction with converting some objects and functions from external to internal -- would make it more palatable to include submodule.h in more places.

    However you split declarations among headers to avoid duplication and serve whatever other objectives you may have, you always have the alternatives of including headers into the sources that need them either directly, or indirectly via other headers.