2

base.h is the basic library in my framework and cannot be modified.

// base.h
#ifndef OK
#define OK 0
#endif

grpc.h is the code segment of the open source framework.

// grpc.h
namespace grpc {
enum StatusCode { OK = 0 };
}

Could main.cc run without modifying the codes of base.h and grpc.h?

// main.cc
#include <iostream>
#include "base.h"
#include "grpc.h"
int main() {
  std::cout << "hello world\n";
  std::cout << grpc::StatusCode::OK;
  return 0;
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 9
    *"basic library in my framework"* What does this mean? Unprefixed macro in a library is a big no-no and warrants a bug report. – HolyBlackCat Aug 17 '21 at 08:03
  • 2
    If you program in C++, don't use macros. Use actual constant variables. Preferably in their own namespace. – Some programmer dude Aug 17 '21 at 08:04
  • Including `grpc.h` before `base.h` didn't work? – Ghasem Ramezani Aug 17 '21 at 08:06
  • @GhasemRamezani That will still make `grpc::StatusCode::OK` unusable, because preprocessor will replace it with `grpc::StatusCode::0` – Yksisarvinen Aug 17 '21 at 08:06
  • This is just a code segment in our company's huge basic library. I know the problem you said, but there are hundreds of thousands of lines of code that depend on it, so I can't modify it. – dearwangsen Aug 17 '21 at 08:09
  • In main you obviously want to use the OK from grpc.h. Which means you do not want to use anything with a conflicting definition. I.e. you cannot use base.h. I have long ago decided that any attempt of "ifndef define" constructs will only hide problems and make things worse. So I would not use your base.h even if not in conflict with anything else. If you had TWO ifndef-define constructs and they would SEEM to compile fine it only means that you will have bigger problems later. Been there, hated it. I enabled "evasion" by offering an alternative definition "SPECIAL_OK" and abolished the "OK". – Yunnosch Aug 17 '21 at 08:10
  • 6
    Even if you find a workaround, you should push for the framework to be fixed. Replacing the macro name is not a big problem, it can be done automatically. It can also be done gradually by adding a replacement macro (or better a enum) and deprecating the original one. – HolyBlackCat Aug 17 '21 at 08:11
  • @Yunnosch @HolyBlackCat Thank you for your suggestions. Actually, I have used the dynamic library scheme to avoid this conflict, but it makes me unable to use the interface of `base.h`. My team wants me to solve this problem, and I want to know if there are other solutions besides modifying `base.h`. – dearwangsen Aug 17 '21 at 08:21
  • *"My team wants me to solve this problem"* Then modifying `base.h` is the right thing. – HolyBlackCat Aug 17 '21 at 08:26
  • Unless actually do the conditional processing with your macro in the header, the place it in your source file. There you can provide the constant, whatever, where it will be used, and without any possibility of namespace collision. – David C. Rankin Aug 17 '21 at 09:39
  • Is any of the involved libraries written in C or is everything available in C++? – Gerhardh Aug 17 '21 at 09:39
  • Do not tag C for C++ questions. – Eric Postpischil Aug 17 '21 at 11:07

3 Answers3

2

The proper approach is to get the library fixed to not define a macro that is likely to clash with many other libraries (or don't use a macro at all, come into the 21st century and use a constant, which if properly namespaced can even still be called OK).

However if that isn't possible you can at least limit the damage by using #undef:

#include <iostream>

#include "base.h"

#ifdef OK
  #if OK != 0
    #error "unexpected value of OK"
  #endif
  #undef OK
  namespace mylib
  {
    const int OK = 0;
  }
#endif

#include "grpc.h"
int main() {
  std::cout << "hello world\n";
  std::cout << grpc::StatusCode::OK << "\n";
  std::cout << mylib::OK << "\n";
  return 0;
}

Note that if you include some other header that needs OK after you've done the #undef it'll obviously not work so you need to be careful of your #include ordering if that is the case.

Declaring the OK constant in the global namespace may even allow other code which depends on the OK macro to compile correctly but is likely (though less likely than a macro) to cause clashes with other libraries.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
1

Since base.h checks for an existing definition of OK, you can obviously do this:

// main.cc
#include <iostream>

#define OK 0
#include "base.h"
#define BASE_H_OK 0
#undef OK

#include "grpc.h"
int main() {
  std::cout << "hello world\n";
  std::cout << grpc::StatusCode::OK;
  return 0;
}

and then use BASE_H_OK where you wanted to use the original macro OK.

j6t
  • 9,150
  • 1
  • 15
  • 35
  • Why is `#define OK 0` needed? – HolyBlackCat Aug 17 '21 at 08:17
  • @HolyBlackCat It's not strictly needed, only for paranoia: since `base.h` checks for a definition of `OK`, the macro can theoretically have any value. Then, since we want to define `BASE_H_OK` to a particular value (`0`), we want to make sure that `base.h` uses that same value just in case it actually expands the macro `OK` somewhere. – j6t Aug 17 '21 at 08:22
  • If the value of `OK` isn't `0` you'd end up with a different value of `OK` in other translation units – Alan Birtles Aug 17 '21 at 08:23
  • @AlanBirtles Very true. Perhaps the authors of `base.h` didn't know that? ;) – j6t Aug 17 '21 at 08:25
  • @AlanBirtles Hah, I didn't realize redefining a macro is allowed if and only if you use the same value. – HolyBlackCat Aug 17 '21 at 08:25
0

As already stated, the only proper solution is to rewrite base.h.

Just for theoretical interest, here are two more inadvisable - yet functioning - workarounds.

Including base.h later:

#include "grpc.h"
#include <iostream>

void functionAfterMain();

int main() {
  std::cout << "hello world\n";
  std::cout << "StatusCode: " << grpc::StatusCode::OK << '\n';
  functionAfterMain();
  return 0;
}

#include "base.h"

void functionAfterMain()
{
    std::cout << "Macro: " << OK << '\n';
}

Undefine and re-include:

#include "grpc.h"
#include "base.h" // Change the include order
#include <iostream>

void functionAfterMain();

int main() {
  std::cout << "hello world\n";

#undef OK
  std::cout << "StatusCode: " << grpc::StatusCode::OK << '\n';
#include "base.h"

  return 0;
}
Yun
  • 3,056
  • 6
  • 9
  • 28
  • 1
    `#include "base.h"` inside main is fairly unlikely to work with the real version of `base.h` which I imagine contains more than just `#define OK` and probably has include guards too – Alan Birtles Aug 17 '21 at 09:24
  • True, the answer is purely a theoretical solution given the question's code snippets. – Yun Aug 17 '21 at 09:34