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

Functional visit #1480

Closed
p-groarke opened this issue Feb 12, 2019 · 11 comments
Closed

Functional visit #1480

p-groarke opened this issue Feb 12, 2019 · 11 comments
Labels
state: help needed the issue needs help to proceed state: please discuss please discuss the issue or vote for your favorite option

Comments

@p-groarke
Copy link

p-groarke commented Feb 12, 2019

Currently the user needs to deserialize values manually. For example :

if (j.is_string()) {
    std::string s = j.get<std::string>();
} else if //etc

Feature : Add an api that deserializes the value for the user, and calls a provided function with it:

j.get([](auto&& my_deserialized_val){
    // Do something magical
});
@nlohmann
Copy link
Owner

Unfortunately, the auto parameter is not possible in C++11. Apart from that I need to better understand the idea. Can you provide some more examples or use cases?

@nlohmann nlohmann added the state: needs more info the author of the issue needs to provide more details label Feb 22, 2019
@p-groarke
Copy link
Author

p-groarke commented Feb 23, 2019

I forgot about C++11, it's been a while.

Examples include any time you don't really care about the type. For example when forwarding to another lib (in my case a mustache object creator). Or when you wish to use constexpr if. It is useful when using templates, factories, or some other generic code.

j.get([](auto&& n){
    if constexpr (!std::is_same_v<std::string, std::decay_t<decltype(n)>>) {
        // something
    } else {
        // something else
    }
});

You can imagine a scenario where a templated function is specialized for certain types, and not others. In that case you really don't care about the de-serialized object type.

j.get([](auto&& n){
    my_templated_function(std::move(n));
});

Lets say you have "components" (as in entity-components) that are to be stored in a SoA manner. Your container :

struct my_soa_component {
    std::vector<int> ints;
    std::vector<float> floats;

    /* ... */
    template <class T>
    void push_back(T&& t) {
        if constexpr(std::is_same_v<int, std::decay_t<T>>) {
        // etc
        }
    }
};

Basically, this feature allows your library to interact in a much more natural way when dealing with templated code.

@p-groarke
Copy link
Author

p-groarke commented Feb 23, 2019

It is just a convenience so we don't have to do all the ifs.

@jaredgrubb
Copy link
Contributor

It wouldn't be hard to implement (in C++11) such that your examples would work. In C++11, you could create a struct with templated-operator() (or with all the right non-template overloads) that is callable by all the JSON types. Then your example (using C++14 lambda-auto) would just naturally work.

I would not call this function "get" as I don't think you could overload it to work well, maybe "visit" or something like that.

    template <typename F>
    void visit(F&& visitor) {
        if (is_string()) { visitor(this->get<string>()); }
        // etc
    }

Bonus points if the visitor can return a value (that is returned by visit). And you could have && overload to all the value to be moved-from into the visitor.

@p-groarke
Copy link
Author

p-groarke commented Feb 23, 2019

If the passed in function could return values, then it would be a boon for interacting with tuples as well (I think).

However, I had forgotten c++11 didn't have templated lambdas. So, feel free to close the issue if you think that would introduce too much version dependent code. I was thinking of something rather simple though.

@p-groarke
Copy link
Author

I agree visit is the appropriate name, as this is really treating your json object as one would std::variant. In a way, you can think of a json object as a variant hierarchy. Makes sense to me.

@p-groarke p-groarke changed the title Functional get Functional visit Feb 23, 2019
@nlohmann
Copy link
Owner

OK, let me summarize so I can check whether I understood everything.

You would like to have a member function basic_json::visit with a signature like

template<class ReturnType, class Visitor>
ReturnType visit(Visitor&& visitor)

This visit function then calls visitor for each underlying type, i.e.,

  • bool for boolean
  • uint64_t / int64_t/ double for numbers
  • std::string for string (or a std::string&?)
  • nullptr_t for null

What to do in case of an array? I would assume std::for_each calling visit for each member; but what happens to the return values? And what to do with objects?

I still need some help, and maybe some use cases to better grasp the problem we are trying to solve.

@nlohmann
Copy link
Owner

(Looking at the examples in https://en.cppreference.com/w/cpp/utility/variant/visit, it seems this could also be a way to realize #1512)

@nlohmann nlohmann added state: please discuss please discuss the issue or vote for your favorite option state: help needed the issue needs help to proceed and removed state: needs more info the author of the issue needs to provide more details labels Mar 13, 2019
@p-groarke
Copy link
Author

p-groarke commented Mar 14, 2019

I was prototyping this in c++11, and I don't believe a deduced return type is possible?
void visit(Func&& f) would be a good start, though not as powerful.

For an array, I would expect to receive an array type. So simply a std::vector.
For objects, I would expect to receive a json::json object.

edit : Whether you wish to make this a member function or global function is entirely up to you. I personally prefer a member function, but I don't believe this would change much and I'm fine with either.

@nlohmann
Copy link
Owner

I still don't get the overall idea. I need more examples/use cases. And I need to understand whether this should be part of the library.

@p-groarke
Copy link
Author

No problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state: help needed the issue needs help to proceed state: please discuss please discuss the issue or vote for your favorite option
Projects
None yet
Development

No branches or pull requests

3 participants