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

from_json not working for boost::optional example #718

Closed
popizdeh opened this issue Aug 29, 2017 · 7 comments
Closed

from_json not working for boost::optional example #718

popizdeh opened this issue Aug 29, 2017 · 7 comments
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@popizdeh
Copy link

popizdeh commented Aug 29, 2017

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

@theodelrieu
Copy link
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.

@popizdeh
Copy link
Author

popizdeh commented Aug 29, 2017

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.

@theodelrieu
Copy link
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

@nlohmann nlohmann added kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation labels Aug 29, 2017
@popizdeh
Copy link
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)...

@nlohmann
Copy link
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.

@popizdeh
Copy link
Author

popizdeh commented Aug 30, 2017

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.

@nlohmann
Copy link
Owner

Ok. Thanks for checking back!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question solution: proposed fix a fix for the issue has been proposed and waits for confirmation
Projects
None yet
Development

No branches or pull requests

3 participants