15

I have a 10+ years old C library which -- I believe -- used to work just fine in the good old days, but when I tried to use it with a C++ source (containing the main function) the other day I ran into some difficulties.

Edit: to clarify, the C library compiles just fine with gcc, and it generates an object file old_c_library.o. This library was supposed to be used in a way so that the C header file old_c_library.h is #included in your main.c C source file. Then your main C source file should be compiled and linked together with old_c_library.o via gcc. Here I want to use a C++ source file main.cpp instead, and compile/link it with g++.

The following three problems occurred, during the compilation of the C++ source file:

  1. one of the header files of the C library contains the C++ reserved word new (it is the name of an integer), which resulted in fatal error; and
  2. one of the header files of the C library contains a calloc call (an explicit typecast is missing), which resulted in fatal error; and
  3. various files of the C library contain code where comparison of signed and unsigned integers happen, which resulted in warnings.

Edit: I tried to use the #extern "C" { #include "obsolete_c_library.h" } "trick", as suggested in the comments, but that did not solve any of my problems.

I can sort out problem 1 by renaming all instances of the reserved words and replacing them by -- basically -- anything else. I can sort out problem 2 by typecasting the calloc call. I might try to sort out the warnings by ideas suggested here: How to disable GCC warnings for a few lines of code.

But I still wonder, is there a way to overcome these difficulties in an elegant, high-level way, without actually touching the original library?


Relevant: Where is C not a subset of C++? and Do I cast the result of malloc? and How do I use extern to share variables between source files?.

Community
  • 1
  • 1
Matsmath
  • 1,164
  • 2
  • 21
  • 40
  • 1
    you could put a `#define new old` before and a `#undef new` after you include the header that uses `new` as variable name – 463035818_is_not_an_ai Apr 26 '16 at 14:07
  • 3
    If the keyword problem occurs only in the implementation of that old library (and not its API), why not compile separately and only link it into your C++ project? – nils Apr 26 '16 at 14:09
  • 2
    The only elegant way would probably be to update the library. As soon as you are required to let code rot, elegance goes out the window and you will have to make do with whatever dirty hack gets the job done. – ComicSansMS Apr 26 '16 at 14:10
  • 1
    I am afraid I might not fully understand your concern. I compile the C library, and then in a separate step I link the generated object files with the C++ source via g++. However, since the C++ source file `#include`s one of the C library header files, I get a compilation (=probably it is a linking) error. – Matsmath Apr 26 '16 at 14:12
  • Then it's probably best if you paste that error message. – nils Apr 26 '16 at 14:14
  • Instead of including the h file, use extern keyword for each function in your library you use, you can do this at the top of the compilation units that will use them, this will tell the compiler that the linker will have to find those symbols at link time and to assume they are valid. This will avoid you reading the parts of the h file that are a problem.. – Rob Apr 26 '16 at 14:15
  • @Rob I tried this, and this most certainly did not solve my problem. As far as I understand, the `extern` keyword is used for something else to deal with (perhaps, with overloaded functions?). – Matsmath Apr 26 '16 at 14:19
  • 1
    Your explanation does not make sense. Header files are involved only at the compilation stage, not at the link stage. The problems you describe must arise when you try to *compile* the C++ main program. – John Bollinger Apr 26 '16 at 14:20
  • 1
    Also, depending on the warning-control options, the problems described in (3) will result in warnings from the `gcc` compiler driver just as they do from the `g++` compiler driver. This is not a C to C++ (in)compatibility issue. – John Bollinger Apr 26 '16 at 14:25
  • @Matsmath Really? have you looked at this post http://stackoverflow.com/questions/1041866/in-c-source-what-is-the-effect-of-extern-c This should also be helpful read to the end http://www.cplusplus.com/forum/general/21368/ – Rob Apr 26 '16 at 14:26

2 Answers2

3

Generally speaking, it is not safe to #include C header files into C++ sources if those headers were not built in anticipation of such usage. Under some circumstances it can be made to work, but you need to be prepared to either modify the headers or write your own declarations for the functions and global variables you want to access.

At minimum, if the C headers declare any functions and you are not recompiling those functions in C++ then you must ensure that the declarations are assigned C linkage in your C++ code. It's not uncommon for C headers to account for that automatically via conditional compilation directives, but if they do not then you can do it on the other side by wrapping the inclusion(s) in a C linkage block:

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

If the C headers declare globals whose names conflict with C++ keywords, and which you do not need to reference, then you may be able to use the preprocessor to redefine them:

#define new extern_new
#include "myclib.h"
#undef  new

That's not guaranteed to work, but it's worth a try. Do not forget to #undef such macros after including the C headers, as shown.

There may be other fun tricks you can play with macros to adapt specific headers to C++, but at some point it makes more sense to just copy / rewrite the needed declarations (and only those), either in your main C++ source or in your own C++ header. Note that doing so does not remove the need to declare C linkage -- that requirement comes from the library having been compiled by a C compiler rather than a C++ compiler.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
2
  1. You can write a duplicate of the c header with the only difference being lack of declaration of extern int new. Then use the newly created c++ friendly header instead of the old, incompatible one.

    If you need to refer that variable from c++, then you need to do something more complex. You will need to write a new wrapper library in c, that exposes read and write functions a pointer to c++ that can be used to access the unfortunately named variable.

    If some inline functions refer to the variable, then you can leave those out from the duplicate header and if you need to call them from c++, re-implement them in a c++ friendly manner. Just don't give the re-implementation same name within extern "C", because that would give you a conflict with the original inline function possibly used by some existing c code.

  2. Do the same header duplication as described in 1. leaving out the problematic inline function and re-implementing if needed.

  3. Warnings can be ignored or disabled as you already know. No need to modify the original headers. You could rewrite any {const,type}-unsafe inline functions with versions that don't generate warnings, but you have to consider whether your time is worth it.

The disadvantage of this approach is that you now have two vesions of some/all of the headers. New ones used by c++, and the old ones that may be used by some old code.


If you can give up the requirement of not touching the original library and don't need to work with an existing compiled binary, then an elegant solution would be to simply rename the problematic variables (1.). Make non-c++-compatible inline functions (2.) non-inline and move them into c source files. Fix unsafe code (3.) that generates warnings or if the warning is c++ specific, simply make the function non-inline.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • All right, I take back that this is a "linking" problem, as pointed out by many comments. It might take some time until I digest all of this. As I thought, the most elegant way is to update the rusty library, if possible. – Matsmath Apr 26 '16 at 15:10