14

Class A uses a library written in C. This library provides some data types and constants which are used in A. Unfortunately, the library also defines macros in its header file, which collide with my C++ code in main.cpp or in other classes using A.

How can I prevent macros of c_library.h being executed when A.h is included somewhere? I would also be open for architectural changes, but I would prefer not to touch the C library.

Of course, there's the #undef directive. But this would mean a lot of manual work for every macro or for every collision. (Ok, there are not too many - but hey, this must be possible more elegant?)

Code:

//main.cpp

#include "A.h"

...
A a(...)
...
std::max(x, y); // oops, problem since max is defined as macro in c_library.h
...

//A.h
#include "c_library.h"

class A{
public:
    A(...);
    static void callbackForCLibrary(datatypeOfCLibrary d){...}
private:
    private datatypeOfCLibrary1;
    private datatypeOfCLibrary2;
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Michael
  • 7,407
  • 8
  • 41
  • 84
  • 4
    With the `#undef` preprocessor directive. – mafso Jul 30 '14 at 20:41
  • 2
    And it's not nice for a C header to define unprefixed, not uppercased macros like `max`. Is this a real problem or a theoretical question? – mafso Jul 30 '14 at 20:42
  • 1
    http://stackoverflow.com/questions/1543736/how-do-i-temporarily-disable-a-macro-expansion-in-c-c – lakshmen Jul 30 '14 at 20:42
  • @mafso: 1. I edited a statement about #undef in the question. 2. It is a real problem. And it's not some crappy library written by an amateur, it's a sophisticated solver library developed by a renowned research institution. – Michael Jul 30 '14 at 20:46
  • 2
    @Michael Sadly, "sophisticated solver library developed by a renowned research institution" and "crappy library written by an amateur" are not mutually exclusive conditions. I've seen plenty of poorly designed software written by people who are geniuses in their field. They just don't know software engineering. Imagine having a chemist play construction engineer and build his own chemistry lab. – Rob K Jul 31 '14 at 17:55

6 Answers6

18

You already know about the #undef option, which would do what you need.

There is another option however. You could completely hide the fact that your A uses library C from your users: Define your own types and interface in the header and class definition of A and remove the library include from your A header. Then in your implementation file you can include the library header and utilize the library in whatever manner is needed, all the while hiding the include of the c_library.h from your users. This has the added advantage of reducing the coupling between your class users, your class, and the library that it depends on.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • I will go for your second option. I will have to copy some lines of code from the header, but it prevents me from #undef-ing all macros to avoid potential future name collisions. – Michael Jul 30 '14 at 20:55
  • @Michael for more info and examples, you might want to google for the terms *compilation firewall* and "*pimpl idiom*" – Arne Mertz Jul 31 '14 at 06:06
10

You could create a "wrap_c_library.h" which is something like:

#ifndef WRAP_C_LIBRARY_H
#define WRAP_C_LIBRARY_H

#include "c_library.h"

#undef TROUBLESOME_MACRO_FROM_C_LIBRARY

#endif // WRAP_C_LIBRARY_H
Dale Wilson
  • 9,166
  • 3
  • 34
  • 52
Rob K
  • 8,757
  • 2
  • 32
  • 36
8

Potentially (not sure how well this will work in practice) #undef doesn't have to mean manual work - you could auto-generate a second file to include that #undefs all of the definitions from the first header.

e.g. given this header:

#define A(X, Y) [X ; Y]
#define B(X, Y) {X...Y}
#define C this is C
#define D this is D

...run the following short script:

gcc -undef -dN -E foo.h > undef_foo.h
sed -i ".bak" 's/#define[ \t]\([A-Za-z0-9_]*\)/#undef \1/g' undef_foo.h
gcc -undef -dD -E - < /dev/null >> undef_foo.h
sed -i ".bak" '/#[du]/!d' undef_foo.h

...to produce this counter-header:

#undef __STDC__
#undef __STDC_HOSTED__
#undef __DYNAMIC__
#undef A
#undef B
#undef C
#undef D
#define __STDC__ 1
#define __STDC_HOSTED__ 1
#define __DYNAMIC__ 1

The basic idea: get a list of all definitions that are a consequence of including foo.h. The -undef -dN parameters to GCC minimize the amount of system-provided stuff that will be included in this list (down to three for me, not sure how consistent this is), to minimize the collateral and simplify the output. Then replace all #define lines with equivalent #undef lines (-dN makes this easier by not listing the replacements). Then append whatever few system-defined macros GCC still included to the end of the file, so their values are restored. Finally, remove all directives from the file that are not #define or #undef.

Usage:

#include "foo.h"
#include "undef_foo.h"

A(1, 2)
B(3, 4)
C
D

Run through gcc -E and observe the macros not expanding.

Someone with better scripting skills can probably make this a lot better, but that's the basic idea.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
6

You could use the #ifdef and #undef preprocessor directives to detect the functions you want to call in C++, and disable the colliding macros declared in the C-header

Jason
  • 31,834
  • 7
  • 59
  • 78
  • FYI, it's totally valid to `#undef` a macro not defined before (so the `#ifdef` is superfluous). – mafso Jul 30 '14 at 20:50
5

1) For any specific macro, you can "disable" it with #undef.

2) If you don't want ANY definitions from a particular header: just don't #include it.

3) If the header is being implicitly included from something else, and you still want to "disable" the entire header, then you can #define the header guard before your first inclusion.

4) I can't imagine any of these options would apply to "std::max()":

http://en.cppreference.com/w/cpp/algorithm/max

FoggyDay
  • 11,962
  • 4
  • 34
  • 48
0

Another approach is to add a new macro switch to the troublesome file "DO_NOT_DEFINE_MACROS"

Modify the troublesome file to NOT define macros if that macro is defined,

Then before you include it, define the macro, include the file, then undef the macro.

stux
  • 2,526
  • 1
  • 13
  • 6