How to solve the conflict between C++ enum and define

Viewed 276

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;
}
3 Answers

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.

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.

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;
}
Related