json/include
Evan Driscoll 494be1c445 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.
2018-06-04 22:28:58 -05:00
..
nlohmann Refactor: rename the register_style overloads 2018-06-04 22:28:58 -05:00