-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
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
Compilation error when using NLOHMANN_JSON_SERIALIZE_ENUM ordered_json on libc++ #2491
Comments
Additional info. I refuse to comprehend it, but forced instantination of |
Good catch, but mega confusing... |
Posted bug in llvm bugtracker. Maybe someone who understands type-traits implementation will answer. |
This is definitively a Clang compiler bug (not to be mixed with Qt bug from #2519 :). As @YarikTH pointed out, an unrelated declaration can change the compiler behavior. Consider also the following:
Assuming |
I minimized the library code as much as I can. Still 400 lines of complicated template code, but at least it's a good starting point to investigate further. It's too much to paste here, so here are the links: Godbolt: https://godbolt.org/z/zf17vT |
Is there anything we can do for this library at the moment? |
I'm stuck with 400 lines of templated spaghetti code and because I neither nlohmann::json maintainer, nor llvm specialist I have no idea how to dig it further. I see no meaningful workarounds of the problem on the nlohmann::json side. So maybe it's time to say that it's obviously llvm bug and call it a day. |
Maybe some kind of "magic" in |
I think that some workaround can be added unless it is something forbidden for the library. I mean, I found only one workaround (for MSVC) in code with a proper comment... |
Ok. If the common decision is to add a workaround, then I have it for you: #include "nlohmann/json.hpp"
enum class Enum1
{
Value1,
Value2
};
// Work around llvm bug described in #2491
#if JSON_USING_WORKAROUND //JSON_HAS_CPP_17
# define JSON_LIBCXX_STD17_WORKAROUND( BasicJsonType ) \
static_assert( std::is_copy_constructible<BasicJsonType>::value )
#else
# define JSON_LIBCXX_STD17_WORKAROUND( BasicJsonType ) static_assert(true, "")
#endif
/*!
@brief macro to briefly define a mapping between an enum and JSON
@def NLOHMANN_JSON_SERIALIZE_ENUM
@since version 3.4.0
*/
#define NLOHMANN_JSON_SERIALIZE_ENUM_2(ENUM_TYPE, ...) \
template<typename BasicJsonType> \
inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \
{ \
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
JSON_LIBCXX_STD17_WORKAROUND(BasicJsonType); \
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
auto it = std::find_if(std::begin(m), std::end(m), \
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
{ \
return ej_pair.first == e; \
}); \
j = ((it != std::end(m)) ? it : std::begin(m))->second; \
} \
template<typename BasicJsonType> \
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
{ \
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
JSON_LIBCXX_STD17_WORKAROUND(BasicJsonType); \
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
auto it = std::find_if(std::begin(m), std::end(m), \
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
{ \
return ej_pair.second == j; \
}); \
e = ((it != std::end(m)) ? it : std::begin(m))->first; \
}
NLOHMANN_JSON_SERIALIZE_ENUM_2( Enum1, {
{Enum1::Value1, "Value1"},
{Enum1::Value2, "Value2"}} )
int main()
{
nlohmann::ordered_json j = Enum1::Value1;
} |
Thanks to your work here, @YarikTH, I was able to make a deeper investigation and found a bit smaller code example (69 lines) that also fails. But the example that I'm about to present also fails on libstdc++ too, GCC standard library implementation. While the original 400+ lines version fails only with libc++. Probably it's due to a reduced number of included files because I was only removing code, but not adding. Here are two demonstrations on Compiler Explorer with the same code: The first one just shows that Clang now also fails with libstdc++, while GCC does not fail, as well as MSVC with its own STD library. The second demonstrates that by using some workarounds a compilation error can be eliminated on Clang. There are 2 workarounds, but they have one thing in common - they manipulate somehow with I also posted this on LLVM bug tracker. |
I have another idea. If replacing of |
Dug in a little. Altering of internal type for using object_t = ObjectType<StringType,
basic_json,
object_comparator_t,
AllocatorType<std::pair<const StringType,
basic_json>>>; Allocator type is hardcoded into |
I think it would make sense to add a CI step that uses libc++, right? |
AFAIK, macOS uses some kind of libc++, so this problem would have been detected if the macOS pipeline had C++17 standard check, and there was also a unit-test for |
@alexezeder The CI now compiles the tests on macOS with Clang using C++11, C++14, C++17, and C++20. I have not seen a failed test though. |
The simple test case I added in #2825 fails with the latest version of the library without my fixes. Try this on
|
Yeah, the test should be added first to get this error. |
I just realized there was already a PR for this even before mine. @nlohmann Can this be fixed ASAP? I've gotten numerous reports of problems related to this over the past 6 months/year, some external and some internal at the company I work at. Everybody was obviously pointing to libc++ even though the problem is in this library. It's a pretty vexing issue for users in a widely used library, and it would be great to prioritize it as such. To clarify the situation regarding libc++:
So adding any CI that uses AppleClang on macOS should provide coverage for this. You should make sure to compile your library as C++17 though, since the issue is apparently invisible when compiling as C++20. |
Usage of
NLOHMANN_JSON_SERIALIZE_ENUM
withnlohmann::ordered_json
results in a compilation error for libc++ with-std=c++17
and greater.What is the issue you have?
The
NLOHMANN_JSON_SERIALIZE_ENUM
expands to the customto_json
andfrom_json
functions. Both of these functions have the following code:And they work fine with using GCC or using (Clang + libstdc++) or using (Clang + libc++) but trying to convert enum value to
nlohmann::json
, but when (Clang + libc++) is used and enum value is converting tonlohmann::ordered_json
then there is a compilation error.Entire compiler output with error
This probably depends on C++ standard version in libc++, for example with
-std=c++11
and-std=c++14
it works fine.Please describe the steps to reproduce the issue.
You can reproduce the issue by looking at this useful example on Compiler Explorer with Clang 11.0.0 and
-std=c++17
.Note that
NLOHMANN_JSON_SERIALIZE_ENUM
is expanded already there and a bit simplified, if this is not necessary then here is also an example withoutNLOHMANN_JSON_SERIALIZE_ENUM
macro expansion.Example with only
std::is_copy_assignable<nlohmann::ordered_json>::value
usage that also fails.Clang 11.0.0 with
-std=c++14
compiles this code on Compiler Explorer.Can you provide a small but working code example?
What is the expected behavior?
It should compile
And what is the actual behavior instead?
Compilation error
Which compiler and operating system are you using?
Which version of the library did you use?
If you experience a compilation error: can you compile and run the unit tests?
The text was updated successfully, but these errors were encountered: