Open dawinaj opened 1 year ago
In case someone was interested in how to proceed with fixing this issue:
This bug is caused by the way the macro functional meta programming is implemented for this project and by a standard behaviour of C preprocessor's __VA_ARGS__
that expands as an empty token when no arguments are passed to a variadic macro.
Due to this property of __VA_ARGS__
, for example, #define M(...) __VA_ARGS__
would be expanded to an empty token ` for
M()`).
So, for instance, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MyStruct)
leads to an expansion, that contains NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, )
, thus expanding NLOHMANN_JSON_TO()
.
See https://github.com/nlohmann/json/blob/a0c1318830519eac027a31edec1a99ce1ae5670e/include/nlohmann/detail/macro_scope.hpp#L406 for more details.
There are several ways how to tackle with this problem:
, __VA_ARGS__
with , ## __VA_ARGS__
, this effectively discards ,
in case of empty arguments.
Unfortunately, only supported by gcc/clang/msvc and maybe other compilers .
See the last paragraph of https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.htmlAlternatively, just add another macro, eg. _EMPTY
which only takes the type argument. But I guess it's not ideal.
In the beginning I thought "Just add an overload with 1 arg". And then I understood, yeah, macros are not functions 😩
It can be easily fixed with __VA_OPT__
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
friend void to_json(nlohmann::json& __VA_OPT__(nlohmann_json_j), const Type& __VA_OPT__(nlohmann_json_t)) { __VA_OPT__(NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__))) } \
friend void from_json(const nlohmann::json& __VA_OPT__(nlohmann_json_j), Type& __VA_OPT__(nlohmann_json_t)) { __VA_OPT__(NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__))) }
But __VA_OPT__
requires c++20. I guess that also can be circumvented with
#ifndef JSON_HAS_CPP_20
#define __VA_OPT__(x) x
#endif
but that still won't fix the bug for the earlier versions.
Description
I found it annoying when working with multiple tiny polymorphic classes, trying to provide identical interface for each of them, that I can't use the macro for a class that has no members to (de)serialize.
Reproduction steps
Use (probably) any of the
NLOHMANN_DEFINE_TYPE_*
macros with only first argument and no members. F.e.NLOHMANN_DEFINE_TYPE_INTRUSIVE(MyClass)
Expected vs. actual results
Expected:
Actual:
Minimal code example
Error messages
Compiler and operating system
Microsoft Visual C++ 2022
Library version
3.11.2
Validation
develop
branch is used.