nemequ / hedley

A C/C++ header to help move #ifdefs out of your code
https://nemequ.github.io/hedley/
Creative Commons Zero v1.0 Universal
774 stars 51 forks source link

Question on integration in libraries #25

Closed nlohmann closed 5 years ago

nlohmann commented 5 years ago

I am the author of nlohmann/json and recently found out about Hedley, and I really love it since we more or less have similar feature macros in our code base, but supporting fewer compilers and without that much documentation. Therefore, we would like to replace our macros with those of Hedley.

As our library is header-only, we would include Hedley in it, meaning we would copy/paste the code into the library header. With this, our library remains self-contained, and we could live happily ever after. However, we would add all of Hedley's macros to the code that includes our library header. So far, we undefined all macros defined by our library.

Do you have any advice how to cope with such situations? The best scenario for us would be if we could define a prefix to the Hedley macros (say, NLOHMANN_JSON_HEDLEY) and then undefine each macro at the end of our main header.

nemequ commented 5 years ago

Hedley is designed to be safe to include in your public headers without having to undefine the macros at the end of your code. It basically works like this

#if !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < x)
#  if defined(HEDLEY_VERSION)
#    undef HEDLEY_VERSION
#  endif
#  define HEDLEY_VERSION x

#  if defined(HEDLEY_FOO)
#    undef HEDLEY_FOO
#  endif
#  define HEDLEY_FOO …

#endif /* !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < x) */

This makes it safe to include Hedley multiple times. If you include a newer version of Hedley after including an older version all macros will be redefined to the newer version's. On the other hand, if you include an older version after including a newer version the older version will be ignored and the newer version's macros will be used. There is really no need for you to undefine the macros at the end of your header.

While the easiest thing to do would be to just drop a copy of hedley.h next to your json.hpp, it also wouldn't be hard to just copy the entire header into json.hpp. Both of those would be relatively easy to maintain (i.e., to update Hedley).

Of course, you're also free to simply copy whatever macros you want; after all, it is public domain. If you do that please modify the namespace… if you define HEDLEY_VERSION but don't include all the macros from that release it will break the logic I described earlier which allows you to include a later version to update the definitions.

The problem with that is that it's much more difficult to maintain since you'll have to re-apply your changes when you want to update Hedley. Hopefully your changes would just be s/HEDLEY_/NLOHMANN_JSON_HEDLEY_/g and maybe removing macros you don't use, which wouldn't be too bad… if that's the case then that might be a feasible path for you. I think the main reason people don't like to do that is most of the macros depend on a bunch of the macros for checking compiler versions, so even a small macro in Hedley requires more code than you might think.

FWIW, I'm happy to help if you have any more questions. Also, if you run into anything you think could/should be in Hedley but isn't I'd love to hear about it.

nlohmann commented 5 years ago

Thanks for the quick response! Cool idea with this "auto-updating" code snippet!

We have two ways of using the library:

As such, everything works fine and with your mentioned trick I am sure I would not break existing code. I am just unsure whether it would be good style to "leak" macros into client code. Therefore, replacing the HEDLEY_ prefix by something else and undefining these macros later is something I would like to try. An update of Hedley would then boil down to:

I'll keep you in the loop on this!

nemequ commented 5 years ago

Sure, that's a perfectly reasonable way to do it.

If you don't change the prefix it could conceivable cause problems, like if someone included hedley then your project then tried to use hedley. If you do change the prefix then it should be safe to undefine the macros. Something like

grep -oP '^\s*#\s*define\s+HEDLEY_[0-9A-Z_]+' hedley.h | grep -oP '[^ ]+$' | sort | uniq | while read 
MACRO; do echo -e "#if defined($MACRO)\n#  undef $MACRO\n#endif\n"; done

should do the trick (except you'll probably want to change the prefix).

I'm going to close this issue, but feel free to reopen (or open a new one) if you have any further questions.

nlohmann commented 5 years ago

FYI: The development version of JSON for Modern C++ now uses Hedley annotations. We use the following code to integrate: https://github.com/nlohmann/json/blob/develop/Makefile#L624

This all will go "live" with the next release 3.7.0, scheduled for release by the end of July.