Skip to content

Commit

Permalink
implement min_element (guarded for SFINAE)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecrypa committed Oct 9, 2019
1 parent ebda810 commit 7e1f955
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 0 deletions.
183 changes: 183 additions & 0 deletions example/src/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -677,4 +677,187 @@ IS_SAME(
);
/// [accumulate]
)

HIDE(
/// [min_element]
using nums = metal::numbers<4, 42, 17, -7, 0>;

using comp_lt = metal::lambda<metal::less>;
IS_SAME(metal::min_element<nums, comp_lt>, metal::number<-7>);

using comp_gt = metal::lambda<metal::greater>;
IS_SAME(metal::min_element<nums, comp_gt>, metal::number<42>);

using vals = metal::list<const char[3], char[1], char[1], char[3], const char[1]>;

template<typename x, typename y>
using smaller = metal::number<(sizeof(x) < sizeof(y))>;
IS_SAME(metal::min_element<vals, metal::lambda<smaller>>, char[1]);

template<typename x, typename y>
using larger = metal::number<(sizeof(x) > sizeof(y))>;
IS_SAME(metal::min_element<vals, metal::lambda<larger>>, const char[3]);
/// [min_element]
)

HIDE(
template<template<class...> class tmpl, class... args>
using is_sfinae_friendly = metal::is_invocable<metal::lambda<tmpl>, args...>;

using some_extra_argument = int;

using attempt_to_instantiate = is_sfinae_friendly<
metal::detail::_min_element<metal::lambda<std::is_void>>::template combiner,
void, some_extra_argument, char
>;
)

HIDE(
template<class, class>
using good_comp = metal::false_;

static_assert(
metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<good_comp>
>{}, "");
)

HIDE(
template<class...>
using good_comp = metal::false_;

static_assert(
metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<good_comp>
>{}, "");
)

HIDE(
template<class, class...>
using good_comp = metal::false_;

static_assert(
metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<good_comp>
>{}, "");
)

HIDE(
template<class, class>
struct good_lazy_comp {
using type = metal::false_;
};

template<class... args>
using good_comp = typename good_lazy_comp<args...>::type;

static_assert(
metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<good_comp>
>{}, "");
)

HIDE(
template<class, class, class...>
struct good_lazy_comp {
template<class...>
using type = metal::false_;
};

template<class... args>
using good_comp = typename good_lazy_comp<args...>::template type<args...>;

static_assert(
metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<good_comp>
>{}, "");
)

HIDE(
template<class>
struct bad_lazy_comp {
using type = metal::false_;
};

template<class... args>
using bad_comp = typename bad_lazy_comp<args...>::type;

static_assert(
!metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<bad_comp>
>{}, "");
)

HIDE(
template<class>
using bad_comp = metal::false_;

static_assert(
!metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<bad_comp>
>{}, "");
)

HIDE(
template<class, class, class>
using bad_comp = metal::false_;

static_assert(
!metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<bad_comp>
>{}, "");
)

HIDE(
template<class, class, class, class...>
using bad_comp = metal::false_;

static_assert(
!metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<bad_comp>
>{}, "");
)

HIDE(
template<class, class>
struct bad_comp;

static_assert(
!metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<bad_comp>
>{}, "");
)

HIDE(
template<class, class>
struct bad_comp {};

static_assert(
!metal::is_invocable<
metal::lambda<metal::min_element>,
metal::numbers<4, 42, 17, -7, 0>,
metal::lambda<bad_comp>
>{}, "");
)

#endif
1 change: 1 addition & 0 deletions include/metal/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "list/iota.hpp"
#include "list/join.hpp"
#include "list/list.hpp"
#include "list/min_element.hpp"
#include "list/none_of.hpp"
#include "list/partition.hpp"
#include "list/powerset.hpp"
Expand Down
51 changes: 51 additions & 0 deletions include/metal/list/min_element.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#ifndef METAL_LIST_MIN_ELEMENT_HPP
#define METAL_LIST_MIN_ELEMENT_HPP

#include "../config.hpp"
#include "../lambda/apply.hpp"
#include "../lambda/lambda.hpp"
#include "../lambda/partial.hpp"
#include "../number/if.hpp"
#include "../value/fold_left.hpp"

namespace metal {
namespace detail {
template<bool>
struct _min_element_binary_expr_guard {
template<template<class...> class expr, class a, class b>
using type = expr<a, b>;
};

template<class...>
struct _guard { // alternate version for arity n, but possibly(?) slower
template<template<class...> class expr, class... args>
using type = expr<args...>;
};

template<typename lbd>
struct _min_element {};

template<template<class...> class expr>
struct _min_element<lambda<expr>> {
template<class x, class y, class... empty>
using combiner =
if_<typename _min_element_binary_expr_guard<!sizeof...(
empty)>::template type<expr, y, x>,
y, x>;

template<class x, class y, class... _>
using more_general_but_possibly_slower_combiner = // TODO: benchmark
if_<typename _guard<_...>::template type<expr, y, x>, y, x>;

template<class seq>
using type =
apply<partial<lambda<fold_left>, lambda<combiner>>, seq>;
};

} // detail

template<typename seq, typename lbd>
using min_element = typename detail::_min_element<lbd>::template type<seq>;
} // metal

#endif

0 comments on commit 7e1f955

Please sign in to comment.