Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add versioned, ABI-tagged inline namespace and namespace macros #3590

Merged
merged 12 commits into from
Jul 30, 2022

Conversation

falbrechtskirchinger
Copy link
Contributor

@falbrechtskirchinger falbrechtskirchinger commented Jul 18, 2022

  • Add a versioned and ABI-tagged inline namespace to prevent ABI issues when linking code using multiple library versions.
  • Add namespace scope and name macros, i.e., NLOHMANN_JSON_NAMESPACE_BEGIN, NLOHMANN_JSON_NAMESPACE_END, NLOHMANN_JSON_NAMESPACE.

Fixes #3588.
Fixes #3360.

To Do:

  • Update documentation.
    • Add macro pages.
    • Mixing JSON_DIAGNOSTICS on and off is now supported.
    • Mention namespace scope macros wherever the user is supposed to place code into the nlohmann namespace (adl_serializer, are there more?).
  • Maybe add a special unit test. (Would have to add 3.10.5 release single header now and pre-3.11.0 develop header later for complete coverage.)
  • Check namespace use in unit tests.

@coveralls
Copy link

coveralls commented Jul 18, 2022

Coverage Status

Coverage remained the same at 100.0% when pulling 0ac4c8a on falbrechtskirchinger:inline-ns into d1d79b9 on nlohmann:develop.

@gregmarr
Copy link
Contributor

@falbrechtskirchinger
Copy link
Contributor Author

Yes. I already have a modified version, although that script needs several updates because of other changes.

@falbrechtskirchinger falbrechtskirchinger changed the title Add versioned inline namespace Add versioned, ABI-tagged inline namespace and add namespace macros Jul 19, 2022
@falbrechtskirchinger
Copy link
Contributor Author

falbrechtskirchinger commented Jul 19, 2022

Now that I've switched to macros, updating the version via bump_version.py is even easier.

Overall, the solution isn't all that pretty - with or without macros. Also, json_fwd.hpp does now include macro_scope.hpp. I think it's time to split macro_scope.hpp and move the more complicated things (NLOHMANN_JSON_SERIALIZE_ENUM, NLOHMANN_CAN_CALL_STD_FUNC_IMPL, ...) into a separate file.

I'm strongly leaning toward adding special unit tests for these changes (see To Do in the description).

@falbrechtskirchinger falbrechtskirchinger changed the title Add versioned, ABI-tagged inline namespace and add namespace macros Add versioned, ABI-tagged inline namespace and namespace macros Jul 19, 2022
@falbrechtskirchinger
Copy link
Contributor Author

Note to self: 196f0acc7e39de04b730a5f33f038da9af2847f2 "Replace uses of nlohmann namespace with macro"

@falbrechtskirchinger falbrechtskirchinger force-pushed the inline-ns branch 3 times, most recently from c27d0d2 to 57f7dc0 Compare July 19, 2022 13:36
docs/mkdocs/docs/api/macros/index.md Outdated Show resolved Hide resolved
docs/mkdocs/docs/api/macros/json_diagnostics.md Outdated Show resolved Hide resolved
include/nlohmann/detail/macro_scope.hpp Show resolved Hide resolved
include/nlohmann/detail/meta/call_std/begin.hpp Outdated Show resolved Hide resolved
tests/abi/use_this.cpp Outdated Show resolved Hide resolved
@falbrechtskirchinger falbrechtskirchinger marked this pull request as ready for review July 19, 2022 14:03
Copy link
Contributor

@gregmarr gregmarr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this eliminate the need for this check, or does it simply mean that we only need to check for versions prior to 3.11.0 being included?

https://github.com/nlohmann/json/pull/3590/files?diff=split&w=1#diff-b56a00981d8f3b87e3ce49a7eb27d36f4586d9c54c3fb628a88cfc000aa5fed4R42-R48

Could we have a test of an object created with JSON_DIAGNOSTICS 1 being passed to a function defined in a file with JSON_DIAGNOSTICS 0? I'm hoping that it fails with a reasonable error message, but it could also copy, like ordered_json to json.

docs/mkdocs/docs/api/macros/json_diagnostics.md Outdated Show resolved Hide resolved
docs/mkdocs/docs/api/macros/json_diagnostics.md Outdated Show resolved Hide resolved
docs/mkdocs/docs/api/macros/json_diagnostics.md Outdated Show resolved Hide resolved
docs/mkdocs/docs/api/macros/nlohmann_json_namespace.md Outdated Show resolved Hide resolved
include/nlohmann/detail/conversions/to_json.hpp Outdated Show resolved Hide resolved
@gregmarr
Copy link
Contributor

Overall, the solution isn't all that pretty - with or without macros. Also, json_fwd.hpp does now include macro_scope.hpp. I think it's time to split macro_scope.hpp and move the more complicated things (NLOHMANN_JSON_SERIALIZE_ENUM, NLOHMANN_CAN_CALL_STD_FUNC_IMPL, ...) into a separate file.

Yes, I could see splitting this up into something like "macros which affect the definition of the basic_json type and need to be included in json_fwd" and "macros which are used in the implementation or by users".

@falbrechtskirchinger
Copy link
Contributor Author

Overall, the solution isn't all that pretty - with or without macros. Also, json_fwd.hpp does now include macro_scope.hpp. I think it's time to split macro_scope.hpp and move the more complicated things (NLOHMANN_JSON_SERIALIZE_ENUM, NLOHMANN_CAN_CALL_STD_FUNC_IMPL, ...) into a separate file.

Yes, I could see splitting this up into something like "macros which affect the definition of the basic_json type and need to be included in json_fwd" and "macros which are used in the implementation or by users".

I've added abi_macros.hpp and included it where appropriate.

@falbrechtskirchinger
Copy link
Contributor Author

Does this eliminate the need for this check, or does it simply mean that we only need to check for versions prior to 3.11.0 being included?

https://github.com/nlohmann/json/pull/3590/files?diff=split&w=1#diff-b56a00981d8f3b87e3ce49a7eb27d36f4586d9c54c3fb628a88cfc000aa5fed4R42-R48

I think it is still useful. Including multiple versions in one translation unit can potentially be handled by ordering the includes carefully and creating using aliases with the current value of NLOHMANN_JSON_NAMESPACE, but should be discouraged.
Come to think of it, I meant to not blindly re-define NLOHMANN_JSON_NAMESPACE and did in fact document, that it's user-definable.

Could we have a test of an object created with JSON_DIAGNOSTICS 1 being passed to a function defined in a file with JSON_DIAGNOSTICS 0? I'm hoping that it fails with a reasonable error message, but it could also copy, like ordered_json to json.

Will do. If it converts, that would be a problematic outcome.

@gregmarr
Copy link
Contributor

I think it is still useful. Including multiple versions in one translation unit can potentially be handled by ordering the includes carefully and creating using aliases with the current value of NLOHMANN_JSON_NAMESPACE, but should be discouraged.

Oh, it's just a warning. I was thinking it was an error.

@falbrechtskirchinger
Copy link
Contributor Author

falbrechtskirchinger commented Jul 19, 2022

Alright, I've spent some time building a test that passes a nlohmann::json_v3_10_5_diag::json (i.e., diagnostics on) to a function accepting nlohmann::json_v3_10_5::json (i.e., diagnostics off). Worse than expected, the result is effectively a reinterpret_cast. :-(
This happens because the return type in the header is written as nlohmann::json (i.e., without the inline namespace) and is interpreted as whatever that type expands to based on the header included into the current translation unit.

Inline namespaces only solve the use case where multiple components use the JSON library internally but don't expose those objects to the user. I still think interoperability is possible with very carefully written glue code. That's what I'll try next (using typeid() to verify correctness).

The documentation needs some more work in light of this.

@nlohmann
Copy link
Owner

I was not aware of inline namespaces. Thanks for adding this. I found https://www.foonathan.net/2018/11/inline-namespaces/ pretty useful.

tests/abi/CMakeLists.txt Show resolved Hide resolved
@falbrechtskirchinger
Copy link
Contributor Author

I was not aware of inline namespaces. Thanks for adding this. I found https://www.foonathan.net/2018/11/inline-namespaces/ pretty useful.

This reminds me that I wanted to rethink the documentation changes around arbitrary type conversion.

@nlohmann nlohmann added this to the Release 3.11.0 milestone Jul 23, 2022
@falbrechtskirchinger falbrechtskirchinger force-pushed the inline-ns branch 3 times, most recently from dd0c855 to 01f6c23 Compare July 24, 2022 09:19
Add a versioned inline namespace to prevent ABI issues when linking code
using multiple library versions.
Add _diag suffix to inline namespace if JSON_DIAGNOSTICS is enabled, and
_ldvcmp suffix if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON is enabled.
@falbrechtskirchinger
Copy link
Contributor Author

@nlohmann I'm done here. Ready for review.

Copy link
Owner

@nlohmann nlohmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@nlohmann nlohmann self-assigned this Jul 30, 2022
@nlohmann nlohmann merged commit d909f80 into nlohmann:develop Jul 30, 2022
@falbrechtskirchinger falbrechtskirchinger deleted the inline-ns branch July 30, 2022 20:02
hahnjo added a commit to hahnjo/root that referenced this pull request Aug 16, 2022
Since nlohmann/json#3590, the basic_json class
and the json using-declaration are located in a "versioned, ABI-tagged
inline namespace". This makes it impossible to forward declare the type
in REveElement.hxx.
Instead introduce a new struct REveJsonWrapper that just wraps a json
object (after including the full nlohmann/json.hpp). As the struct is
under our control, we can easily forward declare the type and use it
for method arguments.
hahnjo added a commit to hahnjo/root that referenced this pull request Aug 16, 2022
Since nlohmann/json#3590, the basic_json class
and the json using-declaration are located in a "versioned, ABI-tagged
inline namespace". This makes it impossible to forward declare the type
in REveElement.hxx.
Instead introduce a new struct REveJsonWrapper that just wraps a json
object (after including the full nlohmann/json.hpp). As the struct is
under our control, we can easily forward declare the type and use it
for method arguments.

Fixes root-project#11130
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants