diff --git a/src/bsoncxx/include/CMakeLists.txt b/src/bsoncxx/include/CMakeLists.txt index ae5b3c7186..1f7ea4b7e3 100644 --- a/src/bsoncxx/include/CMakeLists.txt +++ b/src/bsoncxx/include/CMakeLists.txt @@ -59,6 +59,7 @@ set_dist_list(src_bsoncxx_include_DIST bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp bsoncxx/v_noabi/bsoncxx/stdx/optional.hpp bsoncxx/v_noabi/bsoncxx/stdx/string_view.hpp + bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp bsoncxx/v_noabi/bsoncxx/string/to_string.hpp bsoncxx/v_noabi/bsoncxx/string/view_or_value.hpp bsoncxx/v_noabi/bsoncxx/types.hpp diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/impl.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/impl.hpp index bde3a54803..5f65301f61 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/impl.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/impl.hpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include @@ -27,31 +27,26 @@ namespace basic { namespace impl { template -using takes_document = typename util::is_functor; - -template -using takes_array = typename util::is_functor; - -template -BSONCXX_INLINE typename std::enable_if::value, void>::type generic_append( - core* core, T&& func) { +BSONCXX_INLINE detail::requires_t> // +generic_append(core* core, T&& func) { core->open_document(); - func(sub_document(core)); + detail::invoke(std::forward(func), sub_document(core)); core->close_document(); } -template -BSONCXX_INLINE typename std::enable_if::value, void>::type generic_append(core* core, - T&& func) { +template // placeholder 'void' for VS2015 compat +BSONCXX_INLINE detail::requires_t> // +generic_append(core* core, T&& func) { core->open_array(); - func(sub_array(core)); + detail::invoke(std::forward(func), sub_array(core)); core->close_array(); } -template -BSONCXX_INLINE - typename std::enable_if::value && !takes_array::value, void>::type - generic_append(core* core, T&& t) { +template +BSONCXX_INLINE detail::requires_not_t, + detail::is_invocable> +generic_append(core* core, T&& t) { core->append(std::forward(t)); } diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/sub_document.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/sub_document.hpp index ee0e328db5..dda94fd340 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/sub_document.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/basic/sub_document.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -59,8 +60,7 @@ class sub_document { // Appends a basic::kvp where the key is a non-owning string view. // template - BSONCXX_INLINE typename std::enable_if< - std::is_same::type, stdx::string_view>::value>::type + BSONCXX_INLINE detail::requires_t> // append_(std::tuple&& t) { _core->key_view(std::forward(std::get<0>(t))); impl::value_append(_core, std::forward(std::get<1>(t))); @@ -70,8 +70,7 @@ class sub_document { // Appends a basic::kvp where the key is an owning STL string. // template - BSONCXX_INLINE typename std::enable_if< - std::is_same::type, std::string>::value>::type + BSONCXX_INLINE detail::requires_t> // append_(std::tuple&& t) { _core->key_owned(std::forward(std::get<0>(t))); impl::value_append(_core, std::forward(std::get<1>(t))); diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/core.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/core.hpp index 4b845e2a29..dcd504366b 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/core.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/core.hpp @@ -18,6 +18,7 @@ #include #include +#include "bsoncxx/stdx/type_traits.hpp" #include #include #include @@ -508,7 +509,7 @@ class BSONCXX_API core { /// template BSONCXX_INLINE core& append(T* v) { - static_assert(std::is_same::type, char>::value, + static_assert(detail::is_alike::value, "append is disabled for non-char pointer types"); append(types::b_string{v}); diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/array_context.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/array_context.hpp index 70ec1b47d8..24e6c640b1 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/array_context.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/array_context.hpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include @@ -67,11 +67,10 @@ class array_context { /// The value to append /// template - BSONCXX_INLINE typename std::enable_if< - !(util::is_functor)>::value || - util::is_functor::value || - std::is_same::type, const finalize_type>::value), - array_context>::type& + BSONCXX_INLINE detail::requires_not_t>, + detail::is_invocable, + detail::is_alike> operator<<(T&& t) { _core->append(std::forward(t)); return *this; @@ -86,11 +85,12 @@ class array_context { /// The callback to invoke /// template - BSONCXX_INLINE typename std::enable_if<(util::is_functor)>::value || - util::is_functor::value), - array_context>::type& - operator<<(Func&& func) { - func(*this); + BSONCXX_INLINE + detail::requires_t, + detail::is_invocable>> + operator<<(Func&& func) { + detail::invoke(std::forward(func), *this); return *this; } @@ -105,12 +105,10 @@ class array_context { /// @return A value type which holds the complete bson document. /// template - BSONCXX_INLINE typename std::enable_if< - std::is_same::value && - std::is_same::type, const finalize_type>::value, - // TODO(MSVC): This should just be 'array::value', but - // VS2015U1 can't resolve the name. - bsoncxx::array::value>::type + BSONCXX_INLINE detail::requires_t, + detail::is_alike> + // VS2015U1 can't resolve the name. operator<<(T&&) { return _core->extract_array(); } diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/key_context.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/key_context.hpp index 115f7a388d..5302aae767 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/key_context.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/key_context.hpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include @@ -108,10 +108,9 @@ class key_context { /// The callback to invoke /// template - BSONCXX_INLINE - typename std::enable_if)>::value, key_context>::type& - operator<<(T&& func) { - func(*this); + BSONCXX_INLINE detail::requires_t> // + operator<<(T&& func) { + detail::invoke(std::forward(func), *this); return *this; } @@ -126,12 +125,9 @@ class key_context { /// @return A value type which holds the complete bson document. /// template - BSONCXX_INLINE typename std::enable_if< - std::is_same::value && - std::is_same::type, const finalize_type>::value, - // TODO(MSVC): This should just be 'document::value', but - // VS2015U1 can't resolve the name. - bsoncxx::document::value>::type + BSONCXX_INLINE detail::requires_t, + detail::is_alike> operator<<(T&&) { return _core->extract_document(); } diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/value_context.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/value_context.hpp index fb1d56d0c2..c0312e6e38 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/value_context.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/builder/stream/value_context.hpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include @@ -63,9 +63,8 @@ class value_context { /// The value to append /// template - BSONCXX_INLINE - typename std::enable_if::value, base>::type - operator<<(T&& t) { + BSONCXX_INLINE detail::requires_not_t> // + operator<<(T&& t) { _core->append(std::forward(t)); return unwrap(); } @@ -78,10 +77,9 @@ class value_context { /// The callback to invoke /// template - BSONCXX_INLINE - typename std::enable_if::value, base>::type - operator<<(T&& func) { - func(*this); + BSONCXX_INLINE detail::requires_t> // + operator<<(T&& func) { + detail::invoke(std::forward(func), *this); return unwrap(); } diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/document/value.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/document/value.hpp index 97e31c50ca..5202f84823 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/document/value.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/document/value.hpp @@ -16,7 +16,9 @@ #include #include +#include +#include "bsoncxx/stdx/type_traits.hpp" #include #include @@ -84,8 +86,7 @@ class BSONCXX_API value { /// @param t /// A user-defined object to serialize into a BSON object. /// - template ::value, int>::type = 0> + template > = 0> explicit value(const T& t) : value({}) { to_bson(t, *this); } diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp index abcedc3a4f..3ff81f7ce5 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp @@ -19,6 +19,8 @@ #include #include +#include "./type_traits.hpp" + #include namespace bsoncxx { diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp new file mode 100644 index 0000000000..b3a9a376c6 --- /dev/null +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp @@ -0,0 +1,429 @@ +#pragma once + +#include + +#include + +namespace bsoncxx { +inline namespace v_noabi { +namespace detail { + +#define bsoncxx_ttparam \ + template \ + class + +/// Obtain the nested ::type of the given type argument +template +using type_t = typename T::type; + +/// Obtain the value_type member type of the given argument +template +using value_type_t = typename T::value_type; + +template +using enable_if_t = typename std::enable_if::type; + +#pragma push_macro("DECL_ALIAS") +#define DECL_ALIAS(Name) \ + template \ + using Name##_t = type_t> +DECL_ALIAS(decay); +DECL_ALIAS(make_signed); +DECL_ALIAS(make_unsigned); +DECL_ALIAS(remove_reference); +DECL_ALIAS(remove_const); +DECL_ALIAS(remove_volatile); +DECL_ALIAS(remove_cv); +DECL_ALIAS(add_const); +DECL_ALIAS(add_volatile); +DECL_ALIAS(add_lvalue_reference); +DECL_ALIAS(add_rvalue_reference); +#pragma pop_macro("DECL_ALIAS") + +template +using common_type_t = type_t>; + +/** + * @brief Remove top-level const+volatile+reference qualifiers from the given type. + */ +template +using remove_cvref_t = remove_cv_t>; + +/** + * @brief Create a reference-to-const for the given type + */ +template +using const_reference_t = add_lvalue_reference_t>; + +/** + * @brief A "do-nothing" alias template that always evaluates to void + * + * @tparam Ts Zero or more type arguments, all discarded + */ +template +using void_t = void; + +/** + * @brief Alias for integral_constant + */ +template +using bool_constant = std::integral_constant; + +/** + * @brief Holds a list of types. + * + * This template is never defined, so cannot be used in contexts that require a complete type. + */ +template +struct mp_list; + +/// ## Implementation of the C++11 detection idiom +namespace impl_detection { + +// Implementation of detection idiom for is_detected: true case +template < + // A metafunction to try and apply + bsoncxx_ttparam Oper, + // The arguments to be given. These are deduced from the mp_list argument + typename... Args, + // Apply the arguments to the metafunction. If this yields a type, this function + // will be viable. If substitution fails, this function is discarded from the + // overload set. + typename SfinaeHere = Oper> +std::true_type is_detected_f(mp_list*); + +// Failure case for is_detected. Because this function takes an elipsis, this is +// less preferred than the above overload that accepts a pointer type directly. +template +std::false_type is_detected_f(...); + +// Provides the detected_or impl +template +struct detection; + +// Non-detected case: +template <> +struct detection { + // We just return the default, since the metafunction will not apply + template + using f = Default; +}; + +// Detected case: +template <> +struct detection { + template + using f = Oper; +}; + +} // namespace impl_detection + +/** + * @brief The type yielded by detected_t if the given type operator does not + * yield a type. + */ +struct nonesuch { + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; +}; + +/** + * @brief Results in true_type if the given metafunction yields a valid type when applied to the + * given arguments, otherwise yields false_type + * + * @tparam Oper A template that evaluates to a type + * @tparam Args Some number of arguments to apply to Oper + */ +template +struct is_detected + : decltype(impl_detection::is_detected_f(static_cast*>(nullptr))) {}; + +/** + * @brief If Oper evaluates to a type, yields that type. Otherwise, yields + * the Dflt type + * + * @tparam Dflt The default type to return if the metafunction does not apply + * @tparam Oper A metafunction to speculatively apply + * @tparam Args The arguments to give to the Oper metafunction + */ +template +using detected_or = typename impl_detection::detection< + is_detected::value>::template f; + +/** + * @brief If Oper evaluates to a type, yields that type. Otherwise, yields + * the sentinel type `nonesuch` + * + * @tparam Oper A metafunction to try to apply + * @tparam Args The metafunction arguments to apply to Oper + */ +template +using detected_t = detected_or; + +/** + * @brief Impl of conditional_t + * + * Separating the boolean from the type arguments results in significant speedup to compilation + * due to type memoization + */ + +template +struct conditional { + template + using f = IfTrue; +}; + +template <> +struct conditional { + template + using f = IfFalse; +}; + +/** + * @brief Pick one of two types based on a boolean + * + * @tparam B A boolean value + * @tparam T If `B` is true, pick this type + * @tparam F If `B` is false, pick this type + */ +template +using conditional_t = typename conditional::template f; + +// impl for conjunction+disjunction +namespace impl_logic { + +template +struct conj; + +template +struct conj, mp_list> : H {}; + +template +struct conj> : conj> {}; + +template +struct conj> : H {}; + +template <> +struct conj> : std::true_type {}; + +template +struct disj; + +template +struct disj, mp_list> : H {}; + +template +struct disj> : disj> {}; + +template +struct disj> : H {}; + +template <> +struct disj> : std::false_type {}; + +} // namespace impl_logic + +/** + * @brief inherits unambiguously from the first of `Ts...` for which + * `Ts::value` is a valid expression equal to `false`, or the last of `Ts...` otherwise. + * + * conjunction<> (given no arguments) inherits from std::true_type. + * + * If any of `Ts::value == false`, then no subsequent `Ts::value` will be instantiated. + */ +template +struct conjunction : impl_logic::conj> {}; + +/** + * @brief Inherits unambiguous from the first of `Ts...` where `Ts::value` is `true`, + * or the last of `Ts...` otherwise. + * + * Given no arguments, inherits from std::false_type; + * + * If any of `Ts::value == true`, then no subsequent `Ts::value` will be instantiated. + */ +template +struct disjunction : impl_logic::disj> {}; + +/** + * @brief Given a boolean type trait, returns a type trait which is the logical negation thereof + * + * @tparam T A type trait with a static member ::value + */ +template +struct negation : bool_constant {}; + +/** + * @brief Yields std::true_type, regardless of type arguments. + * + * Useful for wrapping potential decltype() substitution failures in positions + * that expect a bool_constant type. + */ +template +using true_t = std::true_type; + +namespace impl_requires { + +template +R norm_conjunction(...); + +template +conjunction norm_conjunction(const conjunction&); + +template +using norm_conjunction_t = decltype(norm_conjunction(std::declval())); + +template +struct requirement; + +template +struct failed_requirement { + failed_requirement(int) = delete; + + template + static T explain(failed_requirement); +}; + +template +struct failed_requirement> { + failed_requirement(int) = delete; + + template + static auto explain(int) + -> common_type_t::test::template explain(0))...>; +}; + +template +struct requirement { + using test = failed_requirement>; +}; + +template +struct requirement> { + struct test { + template + static T explain(int); + }; +}; + +} // namespace impl_requires + +/** + * @brief If none of `Ts::value is 'false'`, yields the type `Type`, otherwise + * this type is undefined. + * + * Use this to perform enable-if style template constraints. + * + * @tparam Type The type to return upon success + * @tparam Traits A list of type traits with nested ::value members + */ +template +#if defined _MSC_VER && _MSC_VER < 1920 +// VS 2015 has trouble with expression SFINAE. +using requires_t = enable_if_t::value, Type>; +#else +// Generates better error messages in case of substitution failure than a plain enable_if_t: +using requires_t = + decltype(impl_requires::requirement>::test::template explain(0)); +#endif + +/** + * @brief If any of `Ts::value` is 'true', this type is undefined, otherwise + * yields the type `Type`. + * + * Use this to perform enable-if template contraints. + * + * @tparam Type The type to return upon success + * @tparam Traits A list of type traits with nested ::value members + */ +template +using requires_not_t = requires_t>>; + +// Impl: invoke/is_invocable +namespace impl_invoke { + +#pragma push_macro("RETURNS") +#define RETURNS(...) \ + noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \ + return __VA_ARGS__; \ + } \ + static_assert(true, "") + +template +struct invoker { + template + constexpr static auto apply(F&& fun, Args&&... args) + RETURNS(static_cast(fun)(static_cast(args)...)); +}; + +template <> +struct invoker { + template + constexpr static auto apply(F&& fun, Self&& self, Args&&... args) + RETURNS((static_cast(self).*fun)(static_cast(args)...)); +}; + +template <> +struct invoker { + template + constexpr static auto apply(F&& fun, Self&& self) RETURNS(static_cast(self).*fun); +}; + +} // namespace impl_invoke + +static constexpr struct invoke_fn { + /** + * @brief Invoke the given object with the given arguments. + * + * @param fn An invocable: A callable, member object pointer, or member function pointer. + * @param args The arguments to use for invocation. + */ + + template > + constexpr auto operator()(F&& fn, Args&&... args) const + RETURNS(impl_invoke::invoker::value, + std::is_member_function_pointer::value> // + ::apply(static_cast(fn), static_cast(args)...)); +} invoke; + +#pragma pop_macro("RETURNS") + +/** + * @brief Yields the type that would result from invoking F with the given arguments. + * + * @tparam Fun A invocable: A function pointer or callable object, or a member pointer + * @tparam Args The arguments to apply + */ +template +using invoke_result_t = decltype(invoke(std::declval(), std::declval()...)); + +/** + * @brief Trait type to detect if the given object can be "invoked" using the given arguments. + * + * @tparam Fun A invocable: A function pointer or callable object, or a member pointer + * @tparam Args The arguments to match against + */ +template +#if defined(_MSC_VER) && _MSC_VER < 1910 +using is_invocable = is_detected; +#else +struct is_invocable : is_detected { +}; +#endif + +/** + * @brief Trait detects whether the given types are the same after the removal + * of top-level CV-ref qualifiers + */ +template +struct is_alike : std::is_same, remove_cvref_t> {}; + +} // namespace detail + +} // namespace v_noabi + +} // namespace bsoncxx + +#include diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp index 2d28050af2..6c38bfa5f1 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -131,9 +132,7 @@ struct BSONCXX_API b_string { /// @param t /// The value to wrap. /// - template ::type>::value, - int>::type = 0> + template > = 0> BSONCXX_INLINE explicit b_string(T&& t) : value(std::forward(t)) {} stdx::string_view value; @@ -394,8 +393,7 @@ struct BSONCXX_API b_regex { /// template ::type>::value, - int>::type = 0> + detail::requires_not_t> = 0> BSONCXX_INLINE explicit b_regex(T&& regex, U&& options = U{}) : regex(std::forward(regex)), options(std::forward(options)) {} @@ -446,9 +444,7 @@ struct BSONCXX_API b_code { /// @param t /// The js code /// - template ::type>::value, - int>::type = 0> + template > = 0> BSONCXX_INLINE explicit b_code(T&& t) : code(std::forward(t)) {} stdx::string_view code; @@ -485,9 +481,7 @@ struct BSONCXX_API b_symbol { /// @param t /// The symbol. /// - template ::type>::value, - int>::type = 0> + template > = 0> BSONCXX_INLINE explicit b_symbol(T&& t) : symbol(std::forward(t)) {} stdx::string_view symbol; @@ -524,11 +518,9 @@ struct BSONCXX_API b_codewscope { /// @param scope /// A bson document view holding the scope environment. /// - template < - typename T, - typename U, - typename std::enable_if::type>::value, - int>::type = 0> + template > = 0> BSONCXX_INLINE explicit b_codewscope(T&& code, U&& scope) : code(std::forward(code)), scope(std::forward(scope)) {} @@ -628,10 +620,7 @@ struct BSONCXX_API b_decimal128 { /// @param t /// The value to wrap. /// - template < - typename T, - typename std::enable_if::type>::value, - int>::type = 0> + template > = 0> BSONCXX_INLINE explicit b_decimal128(T&& t) : value(std::forward(t)) {} }; diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types/bson_value/view.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types/bson_value/view.hpp index e640cca6e5..99408d196e 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types/bson_value/view.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types/bson_value/view.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -306,32 +307,36 @@ class BSONCXX_API view { }; }; -// sfinae in the bool return to avoid competing with the value == value -// operators. template -using not_view = typename std::enable_if< - std::is_constructible::value && - !std::is_same::type, bson_value::view>::value && - !std::is_same::type, bson_value::value>::value, - bool>::type; +using is_bson_view_compatible = detail::conjunction< + std::is_constructible, + detail::negation, + detail::is_alike>>>; template -BSONCXX_INLINE not_view operator==(const bson_value::view& lhs, T&& rhs) { +using not_view = is_bson_view_compatible; + +template +BSONCXX_INLINE detail::requires_t> // +operator==(const bson_value::view& lhs, T&& rhs) { return lhs == bson_value::view{std::forward(rhs)}; } template -BSONCXX_INLINE not_view operator==(T&& lhs, const bson_value::view& rhs) { +BSONCXX_INLINE detail::requires_t> // +operator==(T&& lhs, const bson_value::view& rhs) { return bson_value::view{std::forward(lhs)} == rhs; } template -BSONCXX_INLINE not_view operator!=(const bson_value::view& lhs, T&& rhs) { +BSONCXX_INLINE detail::requires_t> // +operator!=(const bson_value::view& lhs, T&& rhs) { return lhs != bson_value::view{std::forward(rhs)}; } template -BSONCXX_INLINE not_view operator!=(T&& lhs, const bson_value::view& rhs) { +BSONCXX_INLINE detail::requires_t> // +operator!=(T&& lhs, const bson_value::view& rhs) { return bson_value::view{std::forward(lhs)} != rhs; } diff --git a/src/bsoncxx/test/CMakeLists.txt b/src/bsoncxx/test/CMakeLists.txt index b505b81356..f3c224b02e 100644 --- a/src/bsoncxx/test/CMakeLists.txt +++ b/src/bsoncxx/test/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable(test_bson oid.cpp view_or_value.cpp make_unique.test.cpp + type_traits.test.cpp ) # Common target properties for test executables. @@ -120,5 +121,6 @@ set_dist_list(src_bsoncxx_test_DIST test_macro_guards.cpp.in to_string.hh view_or_value.cpp + type_traits.test.cpp make_unique.test.cpp ) diff --git a/src/bsoncxx/test/type_traits.test.cpp b/src/bsoncxx/test/type_traits.test.cpp new file mode 100644 index 0000000000..5c3a452d91 --- /dev/null +++ b/src/bsoncxx/test/type_traits.test.cpp @@ -0,0 +1,182 @@ +#include +#include + +#include +#include + +#if __GNUC__ +// We declare variables that are only used for compilation checking +// (applies to Clang as well) +#pragma GCC diagnostic ignored "-Wunused" +#endif + +namespace { +namespace tt = bsoncxx::detail; + +template +struct assert_same { + static_assert(std::is_same::value, "Fail"); + using x = void; +}; + +template +struct Case { + template + struct apply { + using x = typename assert_same, Expect>::x; + }; +}; + +template +struct one_case { + using x = typename Case::template apply::x; +}; + +template +struct check_cases : one_case... {}; + +constexpr check_cases< // + tt::decay_t, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case> + decay; + +constexpr check_cases< // + tt::remove_cvref_t, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case, + Case> + remove_cvref; + +constexpr check_cases< // + tt::const_reference_t, + Case, + Case, + Case, + Case, + Case, + Case> + const_reference; + +constexpr check_cases< // + tt::void_t, + Case, + Case, + Case, + Case> + void_t; + +constexpr assert_same> t; +constexpr assert_same> f; + +static_assert(tt::is_detected::value, "fail"); + +template +struct hard_error { + static_assert(std::is_void::value, "fail"); +}; + +struct my_false { + static constexpr bool value = false; +}; + +static_assert(std::is_base_of> // + ::value, + "fail"); + +static_assert( + std::is_base_of>> // + ::value, + "fail"); + +template +tt::requires_t> add_one(T v) { + return v + 1; +} + +template +tt::requires_t>> add_one(T other) { + return other + "one"; +} + +TEST_CASE("requires_t") { + CHECK(add_one(1) == 2); + CHECK(add_one(std::string("twenty-")) == "twenty-one"); +} + +struct something { + int value; + + int memfn(int, std::string); +}; + +static_assert(tt::is_detected::value, + "fail"); + +static_assert( + std::is_same, int&>::value, + "fail"); + +static_assert( + std::is_same, int&&>::value, + "fail"); + +static_assert(std::is_same, + const int&>::value, + "fail"); + +static_assert( + std::is_same, + int>::value, + "fail"); + +// invoke_result_t disappears when given wrong argument types: +static_assert( + !tt::is_detected:: + value, + "fail"); + +struct constrained_callable { + // Viable only if F is callable as F(int, Arg) + template + tt::requires_t> operator()(F&&, Arg) const; +}; + +static_assert(!tt::is_detected::value, + "fail"); + +static_assert(tt::is_detected::value, + "fail"); + +static_assert( + tt::is_detected:: + value, + "fail"); + +} // namespace diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/test_util/mock.hh b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/test_util/mock.hh index a816428bf9..38d098d185 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/test_util/mock.hh +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/test_util/mock.hh @@ -26,6 +26,8 @@ #include #include +#include "bsoncxx/stdx/type_traits.hpp" + #include namespace mongocxx { @@ -85,8 +87,10 @@ class mock { return _callbacks.top(); } - template - typename std::enable_if::value, rule&>::type interpose(T r, U... rs) { + template > = 0> + rule& interpose(T r, U... rs) { std::array vec = {r, rs...}; std::size_t i = 0;