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 <typename Predicate>
// ... (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 <JsonCallable Predicate> ... (Predicate, style)
//    template <JsonPointerCallable Predicate> ... (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 <functional>

    struct Main {};
    struct Secondary { Secondary(Main); };

    // http://en.cppreference.com/w/cpp/types/void_t
    template<typename... Ts> struct make_void { typedef void type;};
    template<typename... Ts> using void_t = typename make_void<Ts...>::type;

    template <typename, typename = void>
    struct can_be_called_with_main : std::false_type { };

    template <typename T>
    struct can_be_called_with_main<
        T,
        void_t<decltype(std::declval<T>()(std::declval<Main>()))>
    >: std::true_type { };

    template <typename, typename = void>
    struct can_be_called_with_secondary : std::false_type { };

    template <typename T>
    struct can_be_called_with_secondary<
        T,
        void_t<decltype(std::declval<T>()(std::declval<Secondary>()))>
    >: std::true_type { };

    template <typename Functor>
    auto
    func(Functor f)
    -> typename std::enable_if<can_be_called_with_main<Functor>::value, int>::type
    {
        return 0;
    }

    template <typename Functor>
    auto
    func(Functor f)
    -> typename std::enable_if<
            can_be_called_with_secondary<Functor>::value
            && !can_be_called_with_main<Functor>::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.
This commit is contained in:
Evan Driscoll 2018-06-04 22:28:58 -05:00
parent edb6b25569
commit 494be1c445
3 changed files with 22 additions and 24 deletions

View File

@ -109,8 +109,9 @@ class basic_fancy_serializer_stylizer
return styles.back().second;
}
fancy_serializer_style& register_style(
json_matcher_predicate p,
template <typename Predicate>
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 <typename Predicate>
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);
}

View File

@ -10914,8 +10914,9 @@ class basic_fancy_serializer_stylizer
return styles.back().second;
}
fancy_serializer_style& register_style(
json_matcher_predicate p,
template <typename Predicate>
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 <typename Predicate>
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);
}

View File

@ -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<json>& context)
stylizer.register_style_context_pred(
[] (const json_pointer<json>& 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(