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 isn't as clean as I wanted -- there's a lot more context
than I realized -- but I think it's still an improvement and
will become even moreso later
1. Copy header and rename; add to Makefile; amalgamate
2. Add as friend to basic_json
3. Copy operator<< to 'fancy_dump' and simplify
4. Change test to refer to that
This overload is chosen only when BasicJsonType::string_t
is not constructible from std::map::key_type.
Currently, converting a map to json treats it as an array of pairs.
fixes#1079