53
votes

I am trying to use a supplier's library in combination with my C++ application. The library is largely based on C, which is normally not a problem with the extern "C" option, but I ran into an issue that the C++ compiler does not accept.

I simplified my code into the following example files. header.h represents a header from the suppier library, main.c/cpp are my own files. My real application is a C++ application, so I want to get it to work with main.cpp.

header.h (note the line u64 u64;):

#ifndef HEADER_H
#define HEADER_H

#include <stdint.h>

typedef uint64_t u64;

union teststruct {
    u64 u64;
    struct {
        u64 x:32;
        u64 y:32;
    } s;
};

#endif

main.c:

#include <stdio.h>
#include "header.h"

int main() {
    union teststruct a;
    a.u64=5;
    printf("%x\n", a.u64);

    return 0;
}

main.cpp (same as main.c but with an extra extern "C" statement):

#include <stdio.h>

extern "C" {
#include "header.h"
}

int main() {
    union teststruct a;
    a.u64=5;
    printf("%x\n", a.u64);

    return 0;
}

Compiling main.c using the line

gcc -o test main.c

compiles without problems. However, compiling the C++ version using the g++ compiler with the command

g++ -o test main.cpp

gives the following compiler errors:

In file included from main.cpp:12:0:
header.h:11:9: error: ‘u64’ does not name a type
         u64 x:32;
         ^
header.h:12:9: error: ‘u64’ does not name a type
         u64 y:32;
         ^

The issue is that the supplier used the same name (u64) for both the type and the variable name, which seems like a bad idea to begin with, but gcc apparently accepts it. I do not want to change the library (i.e. header.h) as it is very large,this occurs a lot in the code, and I occasionally get updates for it. Is there a way to make g++ accept this combination, or a way to modify main.cpp to make it compile without changing header.h?

3
C and C++ are two very different languages, with very different rules and semantics. There are many things that are valid C but invalid C++. If you have a header that includes valid C but invalid C++, and you can't really edit the header file to "fix" it, then there's not really much you can do about it yourself. And extern "C" isn't really a "C backward compatibility mode", its main usage is to inhibit name mangling and not much else. - Some programmer dude
File a bug report to your library supplier. - n. 1.8e9-where's-my-share m.
Is the library claimed to be usable with C++ code? Or is it only claimed to be C code? - Gerhardh
@Someprogrammerdude Answers and Comments are two very different concepts, with very different rules. There are many things that are valid comments, but an answer to the question is not one of them. - pipe

3 Answers

48
votes

teststruct defines a scope in C++. You can form the qualified id teststruct::u64. So the language rules for name lookup account for that, allowing members of classes and unions to hide identifiers in outer scope. Once u64 u64; is introduced, the unqualified u64 cannot refer to the global ::u64, only the member. And the member is not a type.

In C union teststruct does not define a scope. The field can only be used in member access, so there can never arise a conflict. As such the field need not hide the file scope type identifier.

There is nothing, as far as I can tell, that you may do in order to easily work around it. This library (which is a perfectly valid C library), is not a valid C++ library. No different than if it used new or try as variable names. It needs to be adapted.

39
votes

It seems that you have a header file that is illegal in C++, so you cannot #include it in code compiled as C++. If you cannot effect a change in the library header file (e.g. by complaining to your library supplier) then the most straightforward option is to write a thin C++-compatible wrapper around the library:

To isolate your C++ code against the C header, create a Wrapper.h and Wrapper.c, where the .h is valid for inclusion in C++, does not include header.h, and provides all types and functions that you need for library interaction. Then, in the .c, you can #include "header.h" and implement all the calls (and whatever you need to do to safely convert between the types). This would obviously have to be compiled as C, not C++.

3
votes

If your mentioned incompatibility between C and C++ is the only one, you should be able to convert header.h to C++ compatible header file programmatically, name it something like header.hpp. And then you can convert newer versions the same way.

Compiler errors tell you everything about what and where should be changed:

header.h:11:9: error: ‘u64’ does not name a type
  1. Open header.h;
  2. Seek position 11:9;
  3. Insert :: there;
  4. Repeat for all does not name a type error.

Some string processing and it's done.

PS: C to C++ converters may able to do that too.