Skip to content

from_json not working for boost::optional example #718

Closed

Description

@popizdeh

I have a verbatim copy of adl_serializer for boost::optional from the documentation. Version 2.1.1 of the library.

nlohmann::json json{{"test", nullptr}};

boost::optional<int> works = json["test"];

boost::optional<int> broken;
broken = json["test"];

Assignment to 'broken' variable won't go through the adl_serializer and will throw std::domain_error: type must be number, but is null

Activity

  1. theodelrieu commented on Aug 29, 2017

    @theodelrieu
    Contributor

    Does your code compile? Your snippet might not be complete, since I had to change the code to nlohmann::json j{{"test", nullptr}} and I get a compiler error.

    This is because boost::optional has multiple overloads of operator=, we updated the Readme (some important things section) to warn about this issue.

  2. popizdeh commented on Aug 29, 2017

    @popizdeh
    Author

    Sorry about that, I've modified the snippet. I'd be happy if the code didn't compile but it fails at runtime which I think is really bad.

  3. theodelrieu commented on Aug 29, 2017

    @theodelrieu
    Contributor

    Here is my full file:

    #include <json.hpp>
    #include <boost/optional.hpp>
    
    // partial specialization (full specialization works too)
    namespace nlohmann {
        template <typename T>
        struct adl_serializer<boost::optional<T>> {
            static void to_json(json& j, const boost::optional<T>& opt) {
                if (opt == boost::none) {
                    j = nullptr;
                } else {
                  j = *opt; // this will call adl_serializer<T>::to_json which will
                            // find the free function to_json in T's namespace!
                }
            }
    
            static void from_json(const json& j, boost::optional<T>& opt) {
                if (j.is_null()) {
                    opt = boost::none;
                } else {
                    opt = j.get<T>(); // same as above, but with 
                                      // adl_serializer<T>::from_json
                }
            }
        };
    }
    
    int main(int argc, char const *argv[])
    {
      nlohmann::json json{{"test", nullptr}};
    
      boost::optional<int> works = json["test"];
    
      boost::optional<int> broken;
      broken = json["test"];
    }

    I still got the same compiler error, which boost version are you using? I have 1.64 on Debian 8 x64

  4. popizdeh commented on Aug 30, 2017

    @popizdeh
    Author

    Yes, that's the code that's failing for me on Mac with XCode 8.3.3 and Boost 1.64. It seems to be an issue with Apple's version of Clang. I tried reproducing this using wandbox.org by changing the compiler version and I get the compiler error like you with every version of Clang they have. The problem here is that Apple ships a custom version of Clang that we know nothing about and they obviously diverged in behaviour (or it's a command line switch I'm unaware of)...

  5. nlohmann commented on Aug 30, 2017

    @nlohmann
    Owner

    On OSX, I also can't compile the code. GCC and Clang (all with different versions) agree on this error:

    In file included from /usr/local/include/boost/optional.hpp:15:
    /usr/local/include/boost/optional/optional.hpp:315:12: error: call to member function 'construct' is ambiguous
          else construct(boost::forward<Expr>(expr),tag);
               ^~~~~~~~~
    /usr/local/include/boost/optional/optional.hpp:904:15: note: in instantiation of function template specialization
          'boost::optional_detail::optional_base<int>::assign_expr<nlohmann::basic_json<std::map, std::vector, std::__1::basic_string<char>, bool, long
          long, unsigned long long, double, std::allocator, adl_serializer> &, nlohmann::basic_json<std::map, std::vector, std::__1::basic_string<char>,
          bool, long long, unsigned long long, double, std::allocator, adl_serializer> >' requested here
            this->assign_expr(boost::forward<Expr>(expr),boost::addressof(expr));
                  ^
    op.cpp:35:10: note: in instantiation of function template specialization 'boost::optional<int>::operator=<nlohmann::basic_json<std::map, std::vector,
          std::__1::basic_string<char>, bool, long long, unsigned long long, double, std::allocator, adl_serializer> &>' requested here
      broken = json["test"];
             ^
    /usr/local/include/boost/optional/optional.hpp:367:10: note: candidate function [with Args = <const nlohmann::basic_json<std::map, std::vector,
          std::__1::basic_string<char>, bool, long long, unsigned long long, double, std::allocator, adl_serializer> *&>]
        void construct ( in_place_init_t, Args&&... args )
             ^
    /usr/local/include/boost/optional/optional.hpp:616:10: note: candidate function [with Expr = nlohmann::basic_json<std::map, std::vector,
          std::__1::basic_string<char>, bool, long long, unsigned long long, double, std::allocator, adl_serializer> &]
        void construct ( Expr&& expr, void const* )
             ^
    /usr/local/include/boost/optional/optional.hpp:546:10: note: candidate function [with Expr = nlohmann::basic_json<std::map, std::vector,
          std::__1::basic_string<char>, bool, long long, unsigned long long, double, std::allocator, adl_serializer> &] not viable: no known conversion from
          'const nlohmann::basic_json<std::map, std::vector, std::__1::basic_string<char>, bool, long long, unsigned long long, double, std::allocator,
          adl_serializer> *' to 'const boost::in_place_factory_base *' for 2nd argument
        void construct ( Expr&& factory, in_place_factory_base const* )
             ^
    /usr/local/include/boost/optional/optional.hpp:554:10: note: candidate function [with Expr = nlohmann::basic_json<std::map, std::vector,
          std::__1::basic_string<char>, bool, long long, unsigned long long, double, std::allocator, adl_serializer> &] not viable: no known conversion from
          'const nlohmann::basic_json<std::map, std::vector, std::__1::basic_string<char>, bool, long long, unsigned long long, double, std::allocator,
          adl_serializer> *' to 'const boost::typed_in_place_factory_base *' for 2nd argument
        void construct ( Expr&& factory, typed_in_place_factory_base const* )
             ^
    /usr/local/include/boost/optional/optional.hpp:355:10: note: candidate function not viable: requires single argument 'val', but 2 arguments were
          provided
        void construct ( rval_reference_type val )
             ^
    /usr/local/include/boost/optional/optional.hpp:348:10: note: candidate function not viable: requires single argument 'val', but 2 arguments were
          provided
        void construct ( argument_type val )
             ^
    1 error generated.
    
  6. popizdeh commented on Aug 30, 2017

    @popizdeh
    Author

    It's the boost version, not the compiler, sorry for the wild goose chase. This only happens with boost 1.56 and is not that relevant in my view, it'll get resolved as we moved to latest version of boost. I'm happy for you to close this bug.

    Thanks for the quick responses everyone.

  7. nlohmann commented on Aug 30, 2017

    @nlohmann
    Owner

    Ok. Thanks for checking back!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions