From 494be1c445a5203c340282f689dcb73213ea8fbf Mon Sep 17 00:00:00 2001 From: Evan Driscoll Date: Mon, 4 Jun 2018 22:28:58 -0500 Subject: [PATCH] Refactor: rename the register_style overloads I really really wanted to name these the same and overload them, but I couldn't get the metaprogramming to work right. Here's a comment I wrote that describes the problems and what I *planned* to do: // What we want is the register_style overloads below. I chose to // keep them with the same name. But there are two problems with // that. First, because I need to wrap them in a std::function // when promoting to two arguments, I want to make register_style // themselves take the function parameter by a template argument // so it doesn't get type-erased "twice" (with two virtual // calls). But then that means that both versions would have the // generic signature "template // ... (Predicate, style)" and that would lead to ambiguous calls. // // The second problem is that ever if you keep the first parameter // a std::function, because 'json_pointer' is implicitly // convertible to a 'json', if you rely on the implicit conversion // to std::function then you'd get an ambugious call. // // So what we want is, using Concept terms: // // template ... (Predicate, style) // template ... (Predicate, style) // // where JsonCallable is additionally *not* // JsonPointerCallable. The following is my attempt to get that. I then wrote some code that is similar to this: #include struct Main {}; struct Secondary { Secondary(Main); }; // http://en.cppreference.com/w/cpp/types/void_t template struct make_void { typedef void type;}; template using void_t = typename make_void::type; template struct can_be_called_with_main : std::false_type { }; template struct can_be_called_with_main< T, void_t()(std::declval
()))> >: std::true_type { }; template struct can_be_called_with_secondary : std::false_type { }; template struct can_be_called_with_secondary< T, void_t()(std::declval()))> >: std::true_type { }; template auto func(Functor f) -> typename std::enable_if::value, int>::type { return 0; } template auto func(Functor f) -> typename std::enable_if< can_be_called_with_secondary::value && !can_be_called_with_main::value , int>::type { return 0; } auto x1 = func([] (Main) {}); auto x2 = func([] (Secondary) {}); where Main is like 'json' and Secondary like 'json_pointer'. Problem is it doesn't work -- in the SFIANE context, it looks like predicates of both `bool (json)` and `bool (json_pointer)` are callable with both json and json_pointer objects. In the case of `bool (json)` being called with a 'json_pointer', that is via the implicit conversion discussed in the comment above. In the caes of `bool (json_pointer)` being called with a `json`, my guess as to what is going on is that `json` provides an implicit to-anything conversion, which uses a `from_json` function. However, that isn't implemented in a SFIANE-friendly way -- when you try to actually make that conversion, there's a static_assert failure. An alternative approach would be to extract the first argument to the provided predicate via some technique like those described in https://functionalcpp.wordpress.com/2013/08/05/function-traits/, and then is_same them vs json and json_pointer. --- .../nlohmann/detail/output/fancy_serializer.hpp | 15 ++++++++------- single_include/nlohmann/json.hpp | 15 ++++++++------- test/src/unit-fancy-serialization.cpp | 16 ++++++---------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/include/nlohmann/detail/output/fancy_serializer.hpp b/include/nlohmann/detail/output/fancy_serializer.hpp index 7949db491..62798d5e4 100644 --- a/include/nlohmann/detail/output/fancy_serializer.hpp +++ b/include/nlohmann/detail/output/fancy_serializer.hpp @@ -109,8 +109,9 @@ class basic_fancy_serializer_stylizer return styles.back().second; } - fancy_serializer_style& register_style( - json_matcher_predicate p, + template + fancy_serializer_style& register_style_object_pred( + Predicate p, fancy_serializer_style style = fancy_serializer_style()) { auto wrapper = [p](const json_pointer_t&, const BasicJsonType & j) @@ -121,8 +122,9 @@ class basic_fancy_serializer_stylizer return styles.back().second; } - fancy_serializer_style& register_style( - context_matcher_predicate p, + template + fancy_serializer_style& register_style_context_pred( + Predicate p, fancy_serializer_style style = fancy_serializer_style()) { auto wrapper = [p](const json_pointer_t& c, const BasicJsonType&) @@ -137,12 +139,11 @@ class basic_fancy_serializer_stylizer string_t str, fancy_serializer_style style = fancy_serializer_style()) { - using pred = context_matcher_predicate; - return register_style(pred([str](const json_pointer_t& pointer) + return register_style_context_pred([str](const json_pointer_t& pointer) { return (pointer.cbegin() != pointer.cend()) && (*pointer.crbegin() == str); - }), + }, style); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f94f42998..b888e1545 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -10914,8 +10914,9 @@ class basic_fancy_serializer_stylizer return styles.back().second; } - fancy_serializer_style& register_style( - json_matcher_predicate p, + template + fancy_serializer_style& register_style_object_pred( + Predicate p, fancy_serializer_style style = fancy_serializer_style()) { auto wrapper = [p](const json_pointer_t&, const BasicJsonType & j) @@ -10926,8 +10927,9 @@ class basic_fancy_serializer_stylizer return styles.back().second; } - fancy_serializer_style& register_style( - context_matcher_predicate p, + template + fancy_serializer_style& register_style_context_pred( + Predicate p, fancy_serializer_style style = fancy_serializer_style()) { auto wrapper = [p](const json_pointer_t& c, const BasicJsonType&) @@ -10942,12 +10944,11 @@ class basic_fancy_serializer_stylizer string_t str, fancy_serializer_style style = fancy_serializer_style()) { - using pred = context_matcher_predicate; - return register_style(pred([str](const json_pointer_t& pointer) + return register_style_context_pred([str](const json_pointer_t& pointer) { return (pointer.cbegin() != pointer.cend()) && (*pointer.crbegin() == str); - }), + }, style); } diff --git a/test/src/unit-fancy-serialization.cpp b/test/src/unit-fancy-serialization.cpp index 2c9954072..1ac0ec720 100644 --- a/test/src/unit-fancy-serialization.cpp +++ b/test/src/unit-fancy-serialization.cpp @@ -283,18 +283,16 @@ TEST_CASE("serialization") SECTION("example of more sophisticated context matcher") { - using pred = fancy_serializer_stylizer::context_matcher_predicate; - fancy_serializer_stylizer stylizer; stylizer.get_default_style() = fancy_serializer_style::preset_multiline; - stylizer.register_style( - pred([] (const json_pointer& context) + stylizer.register_style_context_pred( + [] (const json_pointer& context) { // Matches if context[-2] is "each elem on one line" return (context.cend() - context.cbegin() >= 2) && (*(context.cend() - 2) == "each elem on one line"); - }) + } ).space_after_comma = true; auto str = fancy_to_string( @@ -331,16 +329,14 @@ TEST_CASE("serialization") SECTION("example of more sophisticated json matcher") { - using pred = fancy_serializer_stylizer::json_matcher_predicate; - fancy_serializer_stylizer stylizer; stylizer.get_default_style() = fancy_serializer_style::preset_multiline; - stylizer.register_style( - pred([] (const json & j) + stylizer.register_style_object_pred( + [] (const json & j) { return j.type() == json::value_t::array; - }) + } ) = fancy_serializer_style::preset_one_line; auto str = fancy_to_string(