1

I was looking at the code of some class I was using, and I came across code like this:

#ifdef SOME_OBSCURE_CONDITION
class A {
#elif 
class A : public B {
#endif

Can there be any problems with such code?

Specifically, suppose file x.cpp includes y.h and z.h. z.h and y.h both include a.h (which defines class A), but additionally y.h defines SOME_OBSCURE_CONDITION. In this case, will two conflicting definitions of A not be present in x.cpp?

apoorv020
  • 5,420
  • 11
  • 40
  • 63
  • it's the reason why i hate macros – onof Oct 04 '11 at 10:59
  • 3
    @onof: By the same logic you should hate electricity. – Lightness Races in Orbit Oct 04 '11 at 11:01
  • @TomalakGeret'kal :) I prefer other techniques. Macros are for C. http://www2.research.att.com/~bs/bs_faq2.html#macro – onof Oct 04 '11 at 11:02
  • 1
    @onof: Nobody's disputing that they have downsides. But generalised "don't do this" like Bjarne's promoting is dangerous. – Lightness Races in Orbit Oct 04 '11 at 11:05
  • 3
    @onof: No language construct is inherently evil, What makes them evil is incorrect usage. – Alok Save Oct 04 '11 at 11:06
  • Does a.h have an include guard? If so, there will be only one definition of A, but which one it is depends on which file is included first (and the other file probably got the wrong definition and produced errors). I would consider changing that to `class A_obscure {` and `class A_regular : public B {`, and perhaps adding something like `#ifndef A`/`#ifdef SOME_OBSCURE_CONDITION`/`#define A A_obscure`/`#else`/`#define A A_regular`/`#endif`/`#else`/`#undef A`/`#endif` after the class definitions, so we can be 100% sure we know which A class we're talking about, without losing the shorter name. – Chris Lutz Oct 04 '11 at 11:07
  • @onof - Anyone who says "feature X is evil and has no reasonable use" is wrong. (Unless they're talking about `gets`.) – Chris Lutz Oct 04 '11 at 11:08
  • @chris: Wouldn't the include guard work only for the files compiled together? Suppose I compile y.cpp and z.cpp separately, and then link them with x, I think it would still fail regardless of guard. – apoorv020 Oct 04 '11 at 11:14
  • @apoorv020 - It depends (partially) on what's in the class. If it was a POD type (basically a C-style `struct`) then there wouldn't really be any problems, but since you're inheriting you're probably not just using it as a POD, so the function names might clash and cause dual-definition linker errors. If you did my little hack, it wouldn't fail, and `A` would still work fine for files that only include one definition. You'd likely have to custom-tailor your include guard to check for `SOME_OBSCURE_CONDITION` so you know whether or not you need to include a new class definition. – Chris Lutz Oct 04 '11 at 11:20

5 Answers5

3

yes, the two variations simultaneously would violate the ODR (One Definition Rule) and may lead to anything ranging from

  • compile errors
  • link errors
  • undefined behaviour (including but not limited to crashing)

As long as you can make sure that the SOME_OBSCURE_CONDITION define is globally identical (also across partial builds/relinks...) there will not be an issue.

sehe
  • 374,641
  • 47
  • 450
  • 633
2

If one is using such a code construct then it is the users responsibility to handle the macro correctly.
Can you break it?
Yes, C++ allows you to shoot yourself in the foot, it is up to you to not do so.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
0

Impossible to answer in the general case. I'm sure you could find ways to break this, but that's true of pretty much anything.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

It is your responsibility to guarantee that all translation units see identical type and class definitions. The compiler is not required to produce a diagnostic (and in fact that's not even possible if you want to maintain the benefits of a modular compilation system).

You can produce examples of ill-formed code by giving different TUs different definitions of the same class even without any preprocessor use. Using header files simply requires a certain discipline on your part.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • In this case the compiler cant even provide a diagnostic because the precompiler evaluates the macro, and the poor compiler tries to compile what it gets. – Alok Save Oct 04 '11 at 11:04
0

The result of this situation is compilation failure or undefined behavior at runtime. If you need to use such class, define this constant in the project settings and not in the code. If you are wrighting this class... well, it is better to think about something else, without using conditional compilation.

Alex F
  • 42,307
  • 41
  • 144
  • 212