Skip to content

Compilation error when using NLOHMANN_JSON_SERIALIZE_ENUM ordered_json on libc++ #2491

@alexezeder

Description

@alexezeder

Usage of NLOHMANN_JSON_SERIALIZE_ENUM with nlohmann::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 custom to_json and from_json functions. Both of these functions have the following code:

static const std::pair<Enum, BasicJsonType> m[] = /* macro argument */;

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 to nlohmann::ordered_json then there is a compilation error.

Entire compiler output with error
In file included from <source>:1:
In file included from /opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:37:
In file included from /opt/compiler-explorer/clang-11.0.0/bin/../include/c++/v1/algorithm:642:
/opt/compiler-explorer/clang-11.0.0/bin/../include/c++/v1/utility:514:58: error: no member named 'value' in 'std::__1::is_copy_assignable<nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>'
                        is_copy_assignable<second_type>::value,
                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:3856:17: note: in instantiation of template class 'std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>' requested here
    -> decltype(from_json(j, val), void())
                ^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:4451:17: note: while substituting deduced template arguments into function template 'operator()' [with BasicJsonType = nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>, T = nlohmann::ordered_map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>, std::__1::less<void>, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>>> *]
    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
                ^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:3094:37: note: while substituting deduced template arguments into function template 'from_json' [with BasicJsonType = const nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>> &, ValueType = nlohmann::ordered_map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>, std::__1::less<void>, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>>> *]
using from_json_function = decltype(T::from_json(std::declval<Args>()...));
                                    ^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:2911:33: note: in instantiation of template type alias 'from_json_function' requested here
struct detector<Default, void_t<Op<Args...>>, Op, Args...>
                                ^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:2921:1: note: during template argument deduction for class template partial specialization 'detector<Default, void_t<Op<Args...>>, Op, Args...>' [with Default = nlohmann::detail::nonesuch, Op = from_json_function, Args = <nlohmann::adl_serializer<nlohmann::ordered_map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>, std::__1::less<void>, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>>> *, void>, const nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>> &, nlohmann::ordered_map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>, std::__1::less<void>, std::__1::allocator<std::__1::pair<const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>>> *&>]
using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:2921:1: note: (skipping 22 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
<source>:12:50: note: in instantiation of template class 'std::__1::pair<Enum1, nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>' requested here
    static const std::pair<Enum1, BasicJsonType> m[] = { 
                                                 ^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:4420:16: note: in instantiation of function template specialization 'to_json<nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>>' requested here
        return to_json(j, std::forward<T>(val));
               ^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:4470:9: note: in instantiation of function template specialization 'nlohmann::detail::to_json_fn::operator()<nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>, Enum1>' requested here
        ::nlohmann::to_json(j, std::forward<ValueType>(val));
        ^
/opt/compiler-explorer/libs/nlohmann_json/trunk/single_include/nlohmann/json.hpp:18014:28: note: in instantiation of function template specialization 'nlohmann::adl_serializer<Enum1, void>::to_json<nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>, Enum1>' requested here
        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
                           ^
<source>:19:32: note: in instantiation of function template specialization 'nlohmann::basic_json<nlohmann::ordered_map, std::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long, unsigned long, double, std::allocator, adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>>::basic_json<Enum1, Enum1, 0>' requested here
    nlohmann::ordered_json j = Enum1::Value1;
                               ^

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 without NLOHMANN_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?

#include "nlohmann/json.hpp"

enum class Enum1
{
    Value1,
    Value2
};

NLOHMANN_JSON_SERIALIZE_ENUM( Enum1,
{
    {Enum1::Value1,   "Enum1::Value1"},
    {Enum1::Value2,   "Enum1::Value2"},
})

int main()
{
    nlohmann::ordered_json j = Enum1::Value1;
}

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?

  • Compiler: Clang 11 + libc++-11 on PC, Clang on Compiler Explorer
  • Operating system: Ubuntu 20.10

Which version of the library did you use?

  • latest release version 3.9.1
  • "trunk" on Compiler Explorer

If you experience a compilation error: can you compile and run the unit tests?

  • yes

Metadata

Metadata

Assignees

Labels

kind: bugsolution: proposed fixa fix for the issue has been proposed and waits for confirmation

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions