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

Please post example of specialization for boost::filesystem #692

Closed
solvingj opened this issue Aug 11, 2017 · 8 comments
Closed

Please post example of specialization for boost::filesystem #692

solvingj opened this issue Aug 11, 2017 · 8 comments
Labels
solution: proposed fix a fix for the issue has been proposed and waits for confirmation

Comments

@solvingj
Copy link

solvingj commented Aug 11, 2017

You gave an example for boost::optional which was very nice, however i spent some time trying to adapt with no success, here was my best guess.

  template<>
  struct adl_serializer<boost::filesystem::path>
  {
    static void to_json(json& j, const boost::filesystem::path& path)
    {
      j = path.string();
    }

    static void from_json(const json& j, boost::filesystem::path& path)
    {
      path = boost::filesystem::path(j);
    }
  };
  

Can you please advise?

@nlohmann
Copy link
Owner

Did you put the struct into the nlohmann namespace?

@nlohmann
Copy link
Owner

nlohmann commented Aug 11, 2017

I realized you pass the JSON value j to boost::filesystem::path in you from_json function.

This may work:

#include "src/json.hpp"
#include <boost/filesystem.hpp>

namespace nlohmann {
  template<>
  struct adl_serializer<boost::filesystem::path>
  {
    static void to_json(json& j, const boost::filesystem::path& path)
    {
      j = path.string();
    }

    static void from_json(const json& j, boost::filesystem::path& path)
    {
      path = j.get<std::string>();
    }
  };
}

int main()
{
  ...
}

@nlohmann nlohmann added the solution: proposed fix a fix for the issue has been proposed and waits for confirmation label Aug 11, 2017
@solvingj
Copy link
Author

Thanks for the response!
This produces:
explicit specialization; 'nlohmann::adl_serializer<T,void>' has already been instantiated
explicit specialization; 'nlohmann::adl_serializer<T,void>' has already been defined

@theodelrieu
Copy link
Contributor

Hello,

This means you specialized the adl_serializer after one piece of code using filesystem::path altogether with nlohmann::json.

You should specialize adl_serializer inside a header that only contains such specializations:

// JsonSpecializations.hpp

#include <json/json.hpp>

namespace nlohmann {
template<>
struct adl_serializer<boost::filesystem::path> {
/* ... */
};
}

In your case, the library already instantiated the default adl_serializer with boost::filesystem::path.
The reason why you don't see the usual static_assert trigger is due to complex template machinery.

I think that if you comment out your specialization, you will see a message saying:

could not find method to_json for type T

@solvingj
Copy link
Author

solvingj commented Aug 11, 2017

Ok, if I comment out the section as you did, I get the same error about already instantiated/defined.

Also, I had a separate file for this specialization of a third-party library, however I did also have to_json and from_json methods in my own classes, and those methods exist alongside their respective class files.

So, there is indeed a slightly non-basic interaction in my case, because my custom classes have filesystem::path members. So, when I convert my classes to JSON, their to_json methods try to convert filesystem::path members to json, which implicitly should call the to_json from the specialization above.

Below is one of those classes. Interestingly, I just discovered that when I added the ADL_Specialization, the specialization logic, the below fails to compile now.

The question is, how does one organize specializations of both custom classes and third-party classes when the former depends on the latter. Do I have to aggregate all my custom class specialization methods into one big header along with the third-party ones?

#ifndef GLOBAL_CONFIG_HPP_
#define GLOBAL_CONFIG_HPP_

#include <string>
#include <boost/filesystem.hpp>
#include <json.hpp>

using nlohmann::json;

namespace daemon
{
  namespace config
  {
    namespace bfs = boost::filesystem;
    struct global_config
    {
      std::string log_level;
      bfs::path log_path;
      bfs::path config_path;
    };

    void to_json(json& j, const global_config& gc)
    {
      j = json
      {
        { "log_level", gc.log_level },
        { "log_path", gc.log_path },
        { "config_path", gc.config_path }
      };
    }
  }
}

@theodelrieu
Copy link
Contributor

Oh, I meant you needed to comment the whole specialization to see the static_assert, not the part I did comment in the example code, sorry about that.

I don't know how many third party classes you need to specialize adl_serializer for, with only two or three, you could put them in a single header (e.g json_boost.hpp).

As for your custom types that use third party types, you MUST include the header json_boost.hpp before the definitions of to_json/from_json.

Finally, if you have to specialize adl_serializer for some of your types (e.g. a template custom container), it's up to you whether to put them in their own headers or in one big. Your types that provide ADL from_json/to_json will need to include them as for third-party types.

@solvingj
Copy link
Author

Ok, finally got it working. It does seem it was a result of my lack of experience in C++.

I did not realize I had to add the #include my json_adl_specialization.hpp file to any file that needs to serialize/deserialize any of the third-party types I've specialized. It makes sense now, but I thought that somehow the nlohmann library would automatically find and use my json_adl_specialization.hpp file because it had the nlohmann namespace. This is because I come from other languages, but also because much of what this library does seems like magic ;)

So, maybe it's worth updating the README documentation to explain that part about including the json_adl_specialization.hpp along with json.hpp in the section about specializing for third-party types. Maybe that's obvious to most C++ developers though, not sure.

@theodelrieu
Copy link
Contributor

Yes I agree, the doc needs some clarifications for those use-cases!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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