2

I use two third party libraries that I don't have control over. I include header files from both of them like this.

#include <libraryA.h>
#include <libraryB.h>

libraryB has an enum like this:

enum class Animal
{
   DOG,
   CAT,
   LION,
}

libraryA has a preprocessor directive like this:

#define DOG "dog"

In my program, if I include libraryA first and then include libraryB, the enum value DOG gets replaced by the string "dog" and everything fails. I am forced to include libraryB first, then include libraryA. While this time, this fix is simple enough for me, I wonder how to guard against this issue? Is there a way I can prevent a preprocessor directive of one library not apply on another library I'm including?

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Gee
  • 41
  • 2
  • You're likely searching for modules in c++20 – lorro Jul 04 '22 at 15:01
  • Since `libraryB` is a C++ library, it should have placed its stuff in a `namespace` . Tell the supplyer of `libraryB` to add a namespace. – Ted Lyngmo Jul 04 '22 at 15:07
  • Do namespaces help against macro shenanigans? – Yunnosch Jul 04 '22 at 15:08
  • @Yunnosch No :) I'm not sure where my head is :-) – Ted Lyngmo Jul 04 '22 at 15:09
  • 2
    It is really libraryA that is at fault. A library should avoid defining macros, but if it does, it should make sure to prefix them to be clearly unique to the library. In C++11 or later there is no reason for `#define DOG "dog"` anyway. It can just be `constexpr auto DOG = "dog";` instead with the same effect. But also as mentioned above, both libraries should be using unique namespaces to put their stuff in as well. – user17732522 Jul 04 '22 at 15:09
  • The preprocessor has no respect for namespaces. While `libraryA` contains `#define DOG …` and `libraryB` tries to use the name `DOG` with a different meaning from that imposed by `libraryA`, the problem is going to occur. Ideally, there'd be a way to avoid using the preprocessor: would `const char DOG[] = "dog";` stop the damage? – Jonathan Leffler Jul 04 '22 at 15:09
  • 1
    You could also create a wrapper around library A (and compile that to a static library of your own). That way you have full control of the header file you're going to use in the rest of your project. If you want design an abstract baseclass/interface for that library andyou can also make mocks/stubs for (the functionality you use of) A and improve your unit testing. – Pepijn Kramer Jul 04 '22 at 15:13
  • @Yunnosch Thanks, your #undef answer will work as a short term solution. To add to the discussion here, libraryA comes from a C codebase, libraryB comes from a C++ codebase. LibraryB does have namespace enclosing all its code, which doesn't matter. From your answer here: https://stackoverflow.com/questions/60174589/undef-seems-to-have-no-effect-on-macro-redefinitions-warning-c4005, can you elaborate on linking both files separately and how to do it? – Gee Jul 04 '22 at 15:22
  • I never had to go that dark path.... My hope is that you can make two libraries with selected non-overlapping external interfaces, which internally use one code file each, by you, liking to the only one of the problematic libaries. Then those two interface-shrunken libraries can be used in your actual project without conflicting. In your question the fact that the two libraries are overlapping on linker symbols is not clear. The question does not suffer itself. But admittedly, if that indeed is your problem, my proposed dupe is a bit weak and I cannot answer based on own experience or test. – Yunnosch Jul 04 '22 at 15:31
  • If library A comes from "C" it is understandable it has no namespace. This is another good reason to make your own libray, add a C++ wrapper with a namespace. So make a static library project, Create a new c++ header file that is to be included in your other project and add (C++) functions for what you use from A (in a namesapce!). Make a c++ source file, include your new header file and library A's header file and create some C++ functions that call the "C" functions. Compile into a lib, use the new header file and link to that from your main project. – Pepijn Kramer Jul 04 '22 at 15:31
  • Looks like you had to walk that path. @PepijnKramer You seem to have the answer I was only outlining. (good that I did not hammer my dupe proposal) Oops, still too late. I propose to add your answer on the dupe I proposed. In addition to my answer there. I think together they greatly cover this horrible topic and make a nicely reusable Q/A for future similar duplicate questions. – Yunnosch Jul 04 '22 at 15:33
  • @Yunnosh Well not exactly this path, but I do have experience with isolation to protect against changes in 3rd party API's and designing for unit testability (and they are not mutually exclusive). – Pepijn Kramer Jul 04 '22 at 15:37
  • Oh and adding a C++ api often also means you can hide the "C" style arrays and "C" style strings, malloc/free calls etc. And replace them with proper C++ types like std::unique_ptr, std::string and some other RAII stuff. – Pepijn Kramer Jul 04 '22 at 15:40
  • You might be looking for `#undef` – Paul Sanders Jul 04 '22 at 16:01

0 Answers0