diff --git a/.mci.yml b/.mci.yml index 51788d35c6..79bcef53c6 100644 --- a/.mci.yml +++ b/.mci.yml @@ -608,7 +608,6 @@ tasks: - name: compile_with_shared_libs commands: - func: "setup" - - func: "start_mongod" - func: "fetch_c_driver_source" - func: "compile" diff --git a/Doxyfile b/Doxyfile index 23b7f490ee..f9e16f709b 100644 --- a/Doxyfile +++ b/Doxyfile @@ -900,7 +900,8 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/operators.hpp \ + src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/src/bsoncxx/include/CMakeLists.txt b/src/bsoncxx/include/CMakeLists.txt index eddc8209f4..ef740ef40d 100644 --- a/src/bsoncxx/include/CMakeLists.txt +++ b/src/bsoncxx/include/CMakeLists.txt @@ -63,6 +63,7 @@ set_dist_list(src_bsoncxx_include_DIST bsoncxx/v_noabi/bsoncxx/config/compiler.hpp bsoncxx/v_noabi/bsoncxx/config/postlude.hpp bsoncxx/v_noabi/bsoncxx/config/prelude.hpp + bsoncxx/v_noabi/bsoncxx/config/util.hpp bsoncxx/v_noabi/bsoncxx/decimal128-fwd.hpp bsoncxx/v_noabi/bsoncxx/decimal128.hpp bsoncxx/v_noabi/bsoncxx/document/element-fwd.hpp @@ -83,6 +84,7 @@ set_dist_list(src_bsoncxx_include_DIST bsoncxx/v_noabi/bsoncxx/oid-fwd.hpp bsoncxx/v_noabi/bsoncxx/oid.hpp bsoncxx/v_noabi/bsoncxx/stdx/make_unique.hpp + bsoncxx/v_noabi/bsoncxx/stdx/operators.hpp bsoncxx/v_noabi/bsoncxx/stdx/optional.hpp bsoncxx/v_noabi/bsoncxx/stdx/string_view.hpp bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/compiler.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/compiler.hpp index 6fdf6000a1..866c194800 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/compiler.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/compiler.hpp @@ -12,21 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if defined(_MSC_VER) +// clang-format off + +#define BSONCXX_IF_MSVC(...) +#define BSONCXX_IF_GCC(...) +#define BSONCXX_IF_CLANG(...) +#define BSONCXX_IF_GNU_LIKE(...) \ + BSONCXX_IF_GCC(__VA_ARGS__) \ + BSONCXX_IF_CLANG(__VA_ARGS__) + +#ifdef __GNUC__ + #ifdef __clang__ + #undef BSONCXX_IF_CLANG + #define BSONCXX_IF_CLANG(...) __VA_ARGS__ + #else + #undef BSONCXX_IF_GCC + #define BSONCXX_IF_GCC(...) __VA_ARGS__ + #endif +#elif defined(_MSC_VER) + #undef BSONCXX_IF_MSVC + #define BSONCXX_IF_MSVC(...) __VA_ARGS__ +#endif + +// clang-format on // Disable MSVC warnings that cause a lot of noise related to DLL visibility // for types that we don't control (like std::unique_ptr). -#pragma warning(push) -#pragma warning(disable : 4251 4275) +BSONCXX_PUSH_WARNINGS(); +BSONCXX_DISABLE_WARNING(MSVC(4251)); +BSONCXX_DISABLE_WARNING(MSVC(5275)); #define BSONCXX_INLINE inline BSONCXX_PRIVATE - -#define BSONCXX_CALL __cdecl - -#else - -#define BSONCXX_INLINE inline BSONCXX_PRIVATE - -#define BSONCXX_CALL - -#endif +#define BSONCXX_CALL BSONCXX_IF_MSVC(__cdecl) diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/postlude.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/postlude.hpp index 2d18d353f3..22f1355dd8 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/postlude.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/postlude.hpp @@ -15,9 +15,7 @@ // compiler.hpp #undef BSONCXX_INLINE #pragma pop_macro("BSONCXX_INLINE") -#if defined(_MSC_VER) -#pragma warning(pop) -#endif +BSONCXX_POP_WARNINGS(); #undef BSONCXX_CALL #pragma pop_macro("BSONCXX_CALL") @@ -67,8 +65,35 @@ #undef BSONCXX_UNREACHABLE #pragma pop_macro("BSONCXX_UNREACHABLE") +#pragma pop_macro("bsoncxx_cxx14_constexpr") +#pragma pop_macro("BSONCXX_RETURNS") + // CXX-2769: out-of-place, but remains for backward compatibility. #ifdef BSONCXX_ENUM static_assert(false, "BSONCXX_ENUM must be undef'ed"); #endif #pragma pop_macro("BSONCXX_ENUM") + +// util.hpp +#pragma pop_macro("BSONCXX_PUSH_WARNINGS") +#pragma pop_macro("BSONCXX_POP_WARNINGS") +#pragma pop_macro("BSONCXX_DISABLE_WARNING") + +#pragma pop_macro("_bsoncxxDisableWarningImpl_for_MSVC") +#pragma pop_macro("_bsoncxxDisableWarningImpl_for_GCC") +#pragma pop_macro("_bsoncxxDisableWarningImpl_for_GNU") +#pragma pop_macro("_bsoncxxDisableWarningImpl_for_Clang") + +#pragma pop_macro("BSONCXX_CONCAT") +#pragma pop_macro("BSONCXX_CONCAT_IMPL") + +#pragma pop_macro("BSONCXX_PRAGMA") +#pragma pop_macro("_bsoncxxPragma") +#pragma pop_macro("BSONCXX_STRINGIFY_IMPL") +#pragma pop_macro("BSONCXX_STRINGIFY") +#pragma pop_macro("BSONCXX_FORCE_SEMICOLON") + +#pragma pop_macro("BSONCXX_IF_MSVC") +#pragma pop_macro("BSONCXX_IF_GCC") +#pragma pop_macro("BSONCXX_IF_CLANG") +#pragma pop_macro("BSONCXX_IF_GNU_LIKE") diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/prelude.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/prelude.hpp index b5c0432b87..7cfa56c1e8 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/prelude.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/prelude.hpp @@ -12,11 +12,53 @@ // See the License for the specific language governing permissions and // limitations under the License. +// util.hpp +#pragma push_macro("BSONCXX_CONCAT") +#undef BSONCXX_CONCAT +#pragma push_macro("BSONCXX_CONCAT_IMPL") +#undef BSONCXX_CONCAT_IMPL +#pragma push_macro("BSONCXX_STRINGIFY") +#undef BSONCXX_STRINGIFY +#pragma push_macro("BSONCXX_STRINGIFY_IMPL") +#undef BSONCXX_STRINGIFY_IMPL +#pragma push_macro("BSONCXX_PRAGMA") +#undef BSONCXX_PRAGMA +#pragma push_macro("_bsoncxxPragma") +#undef _bsoncxxPragma +#pragma push_macro("BSONCXX_FORCE_SEMICOLON") +#undef BSONCXX_FORCE_SEMICOLON +#pragma push_macro("BSONCXX_RETURNS") +#undef BSONCXX_RETURNS +#pragma push_macro("bsoncxx_cxx14_constexpr") +#undef bsoncxx_cxx14_constexpr +#pragma push_macro("BSONCXX_DISABLE_WARNING") +#undef BSONCXX_DISABLE_WARNING +#pragma push_macro("BSONCXX_PUSH_WARNINGS") +#undef BSONCXX_PUSH_WARNINGS +#pragma push_macro("BSONCXX_POP_WARNINGS") +#undef BSONCXX_POP_WARNINGS +#pragma push_macro("_bsoncxxDisableWarningImpl_for_GCC") +#undef _bsoncxxDisableWarningImpl_for_GCC +#pragma push_macro("_bsoncxxDisableWarningImpl_for_Clang") +#undef _bsoncxxDisableWarningImpl_for_Clang +#pragma push_macro("_bsoncxxDisableWarningImpl_for_MSVC") +#undef _bsoncxxDisableWarningImpl_for_MSVC +#pragma push_macro("_bsoncxxDisableWarningImpl_for_GNU") +#undef _bsoncxxDisableWarningImpl_for_GNU + // compiler.hpp #pragma push_macro("BSONCXX_INLINE") #undef BSONCXX_INLINE #pragma push_macro("BSONCXX_CALL") #undef BSONCXX_CALL +#pragma push_macro("BSONCXX_IF_MSVC") +#undef BSONCXX_IF_MSVC +#pragma push_macro("BSONCXX_IF_GCC") +#undef BSONCXX_IF_GCC +#pragma push_macro("BSONCXX_IF_CLANG") +#undef BSONCXX_IF_CLANG +#pragma push_macro("BSONCXX_IF_GNU_LIKE") +#undef BSONCXX_IF_GNU_LIKE // config.hpp (generated by CMake) #pragma push_macro("BSONCXX_INLINE_NAMESPACE_BEGIN") @@ -60,6 +102,8 @@ #pragma push_macro("BSONCXX_NO_DEPRECATED") #undef BSONCXX_NO_DEPRECATED +#include +// #include #include #include diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/util.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/util.hpp new file mode 100644 index 0000000000..b42ce4682b --- /dev/null +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/config/util.hpp @@ -0,0 +1,133 @@ +// clang-format off +/** + * @internal + * @brief Convert the given macro argument to a string literal, after macro expansion + */ +#define BSONCXX_STRINGIFY(...) BSONCXX_STRINGIFY_IMPL(__VA_ARGS__) +#define BSONCXX_STRINGIFY_IMPL(...) #__VA_ARGS__ + +/** + * @brief Token-paste two macro arguments, after macro expansion + */ +#define BSONCXX_CONCAT(A, ...) BSONCXX_CONCAT_IMPL(A, __VA_ARGS__) +#define BSONCXX_CONCAT_IMPL(A, ...) A##__VA_ARGS__ + +/** + * @internal + * @brief Expands to a _Pragma() preprocessor directive, after macro expansion + * + * The arguments an arbitrary "token soup", and should not be quoted like a regular + * _Pragma. This macro will stringify-them itself. + * + * Example: + * + * BSONCXX_PRAGMA(GCC diagnostic ignore "-Wconversion") + * + * will become: + * + * _Pragma("GCC diagnostic ignore \"-Wconversion\"") + * + */ +#define BSONCXX_PRAGMA(...) _bsoncxxPragma(__VA_ARGS__) +#ifdef _MSC_VER +// Old MSVC doesn't recognize C++11 _Pragma(), but it always recognized __pragma +#define _bsoncxxPragma(...) __pragma(__VA_ARGS__) +#else +#define _bsoncxxPragma(...) _Pragma(BSONCXX_STRINGIFY(__VA_ARGS__)) +#endif + +/** + * @internal + * @brief Use in a declaration position to force the appearence of a semicolon + * as the next token. Use this for statement-like or declaration-like macros to + * enforce that their call sites are followed by a semicolon + */ +#define BSONCXX_FORCE_SEMICOLON static_assert(true, "") + +/** + * @internal + * @brief Add a trailing noexcept, decltype-return, and return-body to a + * function definition. (Not compatible with lambda expressions.) + * + * Example: + * + * template + * auto foo(T x, T y) BSONCXX_RETURNS(x + y); + * + * Becomes: + * + * template + * auto foo(T x, T y) noexcept(noexcept(x + y)) + * -> decltype(x + y) + * { return x + y }; + * + */ +#define BSONCXX_RETURNS(...) \ + noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \ + return __VA_ARGS__; \ + } \ + BSONCXX_FORCE_SEMICOLON + +/** + * @internal + * @macro mongocxx_cxx14_constexpr + * @brief Expands to `constexpr` if compiling as c++14 or greater, otherwise + * expands to `inline`. + * + * Use this on functions that can only be constexpr in C++14 or newer, including + * non-const member functions. + */ +#if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L && _MSC_VER > 1910) +#define bsoncxx_cxx14_constexpr constexpr +#else +#define bsoncxx_cxx14_constexpr inline +#endif + +/** + * @internal + * @brief Disable a warning for a particular compiler. + * + * The argument should be of the form: + * + * - Clang() + * - GCC() + * - GNU() + * - MSVC() + * + * The "GNU" form applies to both GCC and Clang + */ +#define BSONCXX_DISABLE_WARNING(Spec) \ + BSONCXX_CONCAT(_bsoncxxDisableWarningImpl_for_, Spec) \ + BSONCXX_FORCE_SEMICOLON + +/** + * @internal + * @brief Push the current compiler diagnostics settings state + */ +#define BSONCXX_PUSH_WARNINGS() \ + BSONCXX_IF_GNU_LIKE(BSONCXX_PRAGMA(GCC diagnostic push)) \ + BSONCXX_IF_MSVC(BSONCXX_PRAGMA(warning(push))) \ + BSONCXX_FORCE_SEMICOLON + +/** + * @internal + * @brief Restore prior compiler diagnostics settings from before the most + * recent BSONCXX_PUSH_WARNINGS() + */ +#define BSONCXX_POP_WARNINGS() \ + BSONCXX_IF_GNU_LIKE(BSONCXX_PRAGMA(GCC diagnostic pop)) \ + BSONCXX_IF_MSVC(BSONCXX_PRAGMA(warning(pop))) \ + BSONCXX_FORCE_SEMICOLON + +#define _bsoncxxDisableWarningImpl_for_GCC(...) \ + BSONCXX_IF_GCC(BSONCXX_PRAGMA(GCC diagnostic ignored __VA_ARGS__)) + +#define _bsoncxxDisableWarningImpl_for_Clang(...) \ + BSONCXX_IF_CLANG(BSONCXX_PRAGMA(GCC diagnostic ignored __VA_ARGS__)) + +#define _bsoncxxDisableWarningImpl_for_GNU(...) \ + _bsoncxxDisableWarningImpl_for_GCC(__VA_ARGS__) \ + _bsoncxxDisableWarningImpl_for_Clang(__VA_ARGS__) + +#define _bsoncxxDisableWarningImpl_for_MSVC(...) \ + BSONCXX_IF_MSVC(BSONCXX_PRAGMA(warning(disable : __VA_ARGS__))) 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 2d6daf36b6..a6ebd6fe48 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/document/value.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/document/value.hpp @@ -235,17 +235,13 @@ class value { std::size_t _length{0}; }; -#if !defined(__clang__) && defined(__GNUC__) && (__cplusplus >= 201709L) -// Silence false positive with g++ 10.2.1 on Debian 11. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif BSONCXX_INLINE document::view value::view() const noexcept { + // Silence false positive with g++ 10.2.1 on Debian 11. + BSONCXX_PUSH_WARNINGS(); + BSONCXX_DISABLE_WARNING(GCC("-Wmaybe-uninitialized")); return document::view{static_cast(_data.get()), _length}; + BSONCXX_POP_WARNINGS(); } -#if !defined(__clang__) && defined(__GNUC__) && (__cplusplus >= 201709L) -#pragma GCC diagnostic pop -#endif BSONCXX_INLINE value::operator document::view() const noexcept { return view(); diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/operators.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/operators.hpp new file mode 100644 index 0000000000..8e889b0b1a --- /dev/null +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/operators.hpp @@ -0,0 +1,184 @@ +// Copyright 2023 MongoDB Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +#include + +#include + +namespace bsoncxx { +namespace detail { + +template +auto is_equality_comparable_f(...) -> std::false_type; + +template +auto is_equality_comparable_f(int, + bool b = false, + const_reference_t l = soft_declval(), + const_reference_t r = soft_declval()) + -> true_t; + +/** + * @brief Detect whether two types are equality-comparable. + * + * Requires L == R, L != R, R == L, and R != L + */ +template +struct is_equality_comparable : decltype(is_equality_comparable_f(0)) {}; + +/** + * @brief Callable object and tag type for equality comparison + */ +struct equal_to { + template + constexpr requires_t> // + operator()(L&& l, R&& r) const noexcept(noexcept(l == r)) { + return l == r; + } +}; + +/** + * @brief Derive from this class to define ADL-only operator== and operator!= on the basis of + * an ADL-only tag_invoke(equal_to, l, r) + */ +class equality_operators { + template + constexpr static auto impl(rank<1>, L& l, R& r) BSONCXX_RETURNS(tag_invoke(equal_to{}, l, r)); + + template + constexpr static auto impl(rank<0>, L& l, R& r) BSONCXX_RETURNS(tag_invoke(equal_to{}, r, l)); + + template + constexpr friend auto operator==(const Left& self, const Other& other) + BSONCXX_RETURNS(equality_operators::impl(rank<1>{}, self, other)); + + template + constexpr friend auto operator!=(const Left& self, const Other& other) + BSONCXX_RETURNS(!equality_operators::impl(rank<1>{}, self, other)); +}; + +/** + * @brief Very basic impl of C++20 std::strong_ordering + * + * We don't need other weaker orderings yet, so this is all that we have + */ +class strong_ordering { + signed char _c; + struct _construct {}; + + constexpr strong_ordering(_construct, signed char c) noexcept : _c(c) {} + + public: + static const strong_ordering less; + static const strong_ordering greater; + static const strong_ordering equivalent; + static const strong_ordering equal; + + constexpr strong_ordering(std::nullptr_t) noexcept : strong_ordering(_construct{}, 0) {} + + constexpr bool operator==(strong_ordering o) const noexcept { + return _c == o._c; + } + constexpr bool operator!=(strong_ordering o) const noexcept { + return !(*this == o); + } +#pragma push_macro("DEFOP") +#undef DEFOP +#define DEFOP(Op) \ + constexpr bool operator Op(std::nullptr_t) const noexcept { \ + return _c Op 0; \ + } \ + static_assert(true, "") + DEFOP(<); + DEFOP(>); + DEFOP(<=); + DEFOP(>=); +#pragma pop_macro("DEFOP") +}; + +#pragma push_macro("INLINE_VAR") +#undef INLINE_VAR +#define INLINE_VAR BSONCXX_IF_GNU_LIKE([[gnu::weak]]) BSONCXX_IF_MSVC(__declspec(selectany)) + +INLINE_VAR const strong_ordering strong_ordering::less = + strong_ordering(strong_ordering::_construct{}, -1); +INLINE_VAR const strong_ordering strong_ordering::greater = + strong_ordering(strong_ordering::_construct{}, 1); +INLINE_VAR const strong_ordering strong_ordering::equivalent = + strong_ordering(strong_ordering::_construct{}, 0); +INLINE_VAR const strong_ordering strong_ordering::equal = + strong_ordering(strong_ordering::_construct{}, 0); + +#pragma pop_macro("INLINE_VAR") + +/** + * @brief Implements a three-way comparison between two objects. That is, in + * a single operation, determine whether the left operand is less-than, greater-than, + * or equal-to the right-hand operand. + */ +struct compare_three_way { + template () < std::declval()), + typename = decltype(std::declval() == std::declval())> + constexpr static strong_ordering impl(L const& l, R const& r, rank<1>) { + return (l < r) ? strong_ordering::less + : (l == r ? strong_ordering::equal // + : strong_ordering::greater); + } + + template (), std::declval(), std::declval()))> + constexpr strong_ordering impl(L const& l, R const& r, rank<2>) const { + return tag_invoke(*this, l, r); + } + + template + constexpr auto operator()(L const& l, R const& r) const + BSONCXX_RETURNS((impl)(l, r, rank<2>{})); +}; + +/** + * @brief Inherit to define ADL-visible ordering operators based on an ADL-visible + * implementation of tag_invoke(compare_three_way, l, r) + */ +struct ordering_operators { +#pragma push_macro("DEFOP") +#undef DEFOP +#define DEFOP(Oper) \ + template \ + constexpr friend auto operator Oper(const L& l, const R& r) \ + BSONCXX_RETURNS(tag_invoke(compare_three_way{}, l, r) Oper 0) + DEFOP(<); + DEFOP(>); + DEFOP(<=); + DEFOP(>=); +#pragma pop_macro("DEFOP") +}; + +} // namespace detail +} // namespace bsoncxx + +#include diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/string_view.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/string_view.hpp index 71e72255d8..785e873d77 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/string_view.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/string_view.hpp @@ -1,4 +1,4 @@ -// Copyright 2015 MongoDB Inc. +// Copyright 2023 MongoDB Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,101 +14,515 @@ #pragma once -#include - -#if defined(BSONCXX_POLY_USE_MNMLSTC) - -#include - -namespace bsoncxx { -namespace v_noabi { -namespace stdx { - -using ::core::basic_string_view; -using ::core::string_view; - -} // namespace stdx -} // namespace v_noabi -} // namespace bsoncxx - -#elif defined(BSONCXX_POLY_USE_BOOST) - -#include +#include +#include +#include +#include +#include +#include +#include -#if BOOST_VERSION >= 106100 +#include +#include -#include - -namespace bsoncxx { -namespace v_noabi { -namespace stdx { - -using ::boost::basic_string_view; -using ::boost::string_view; - -} // namespace stdx -} // namespace v_noabi -} // namespace bsoncxx +#include -#else +#ifdef __has_include +#if __has_include() +#include +#endif +#endif -#include +#ifdef __cpp_lib_string_view +#include +#endif namespace bsoncxx { namespace v_noabi { namespace stdx { -template > -using basic_string_view = ::boost::basic_string_ref; -using string_view = ::boost::string_ref; - -} // namespace stdx -} // namespace v_noabi -} // namespace bsoncxx - +/** + * @brief Implementation of std::string_view-like class template + */ +template > +class basic_string_view : bsoncxx::detail::equality_operators, bsoncxx::detail::ordering_operators { + public: + // Pointer to (non-const) character type + using pointer = Char*; + // Pointer to const-character type + using const_pointer = const Char*; + // Type representing the size of a string + using size_type = std::size_t; + // Type representing the offset within a string + using difference_type = std::ptrdiff_t; + // The type of the string character + using value_type = Char; + + // Constant sentinel value to represent an impossible/invalid string position + static constexpr size_type npos = static_cast(-1); + + private: + // Pointer to the beginning of the string being viewed + const_pointer _begin = nullptr; + // The size of the array that is being viewed via `_begin` + size_type _size = 0; + + public: + using traits_type = Traits; + using reference = Char&; + using const_reference = const Char&; + using const_iterator = const_pointer; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator; + using reverse_iterator = const_reverse_iterator; + + /** + * @brief Default constructor. Constructs to an empty/null string view + */ + constexpr basic_string_view() noexcept = default; + constexpr basic_string_view(const basic_string_view&) noexcept = default; + bsoncxx_cxx14_constexpr basic_string_view& operator=(const basic_string_view&) noexcept = + default; + + /** + * @brief Construct a new string view from a pointer-to-character and an + * array length. + */ + constexpr basic_string_view(const_pointer s, size_type count) : _begin(s), _size(count) {} + + /** + * @brief Construct a new string view from a C-style null-terminated character array. + * + * The string size is inferred as-if by strlen() + */ + constexpr basic_string_view(const_pointer s) : _begin(s), _size(traits_type::length(s)) {} + + /** + * @brief Implicit conversion from string-like ranges. + * + * Requires that `StringLike` is a non-array contiguous range with the same + * value type as this string view, and is a std::string-like value. + */ + template + constexpr basic_string_view( + const std::basic_string& str) noexcept + : _begin(str.data()), _size(str.size()) {} + +#if __cpp_lib_string_view + constexpr basic_string_view(std::basic_string_view sv) noexcept + : _begin(sv.data()), _size(sv.size()) {} #endif -#elif defined(BSONCXX_POLY_USE_STD_EXPERIMENTAL) - -#include - -namespace bsoncxx { -namespace v_noabi { -namespace stdx { + // Construction from a null pointer is deleted + basic_string_view(std::nullptr_t) = delete; + + constexpr const_iterator begin() const noexcept { + return const_iterator(_begin); + } + constexpr const_iterator end() const noexcept { + return begin() + size(); + } + constexpr const_iterator cbegin() const noexcept { + return begin(); + } + constexpr const_iterator cend() const noexcept { + return end(); + } + + constexpr const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator{end()}; + } + + constexpr const_reverse_iterator rend() const noexcept { + return const_reverse_iterator{begin()}; + } + + constexpr const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator{cend()}; + } + + constexpr const_reverse_iterator crend() const noexcept { + return const_reverse_iterator{crbegin()}; + } + + /** + * @brief Access the Nth element of the referred-to string + * + * @param offset A zero-based offset within the string to access. Must be less + * than size() + */ + constexpr const_reference operator[](size_type offset) const { + return _begin[offset]; + } + + /** + * @brief Access the Nth element of the referred-to string. + * + * @param pos A zero-based offset within the string to access. If not less + * than size(), throws std::out_of_range + */ + bsoncxx_cxx14_constexpr const_reference at(size_type pos) const { + if (pos >= size()) { + throw std::out_of_range{"bsoncxx::stdx::basic_string_view::at()"}; + } + return _begin[pos]; + } + /// Access the first character in the string + constexpr const_reference front() const { + return (*this)[0]; + } + /// Access the last character in the string + constexpr const_reference back() const { + return (*this)[size() - 1]; + } + + /// Obtain a pointer to the beginning of the referred-to character array + constexpr const_pointer data() const noexcept { + return _begin; + } + /// Obtain the length of the referred-to string, in number of characters + constexpr size_type size() const noexcept { + return _size; + } + /// Obtain the length of the referred-to string, in number of characters + constexpr size_type length() const noexcept { + return size(); + } + /// Return `true` if size() == 0, otherwise `false` + constexpr bool empty() const noexcept { + return size() == 0; + } + /// Return the maximum value that could be returned by size() + constexpr size_type max_size() const noexcept { + return static_cast(std::numeric_limits::max()); + } + + /** + * @brief In-place modify the string_view to view N fewer characters from the beginning + * + * @param n The number of characters to remove from the beginning. Must be less than size() + */ + bsoncxx_cxx14_constexpr void remove_prefix(size_type n) { + _begin += n; + _size -= n; + } + + /** + * @brief In-place modify the string_view to view N fewer characters from the end + * + * @param n The number of characters to remove from the end. Must be less than size() + */ + bsoncxx_cxx14_constexpr void remove_suffix(size_type n) { + _size -= n; + } + + /** + * @brief Swap the reference with another string_view + */ + bsoncxx_cxx14_constexpr void swap(basic_string_view& other) { + std::swap(_begin, other._begin); + std::swap(_size, other._size); + } + + /** + * @brief Copy the contents of the viewed string into the given output destination. + * + * @param dest The destination at which to write characters + * @param count The maximum number of characters to copy. + * @param pos The offset within the viewed string to begin copying from. + * @returns The number of characters that were copied to `dest`. The number + * of copied characters is always the lesser of `size()-pos` and `count` + * + * @throws std::out_of_range if pos > size() + */ + size_type copy(pointer dest, size_type count, size_type pos = 0) const { + if (pos > size()) { + throw std::out_of_range{"bsoncxx::stdx::basic_string_view::substr()"}; + } + count = (std::min)(count, size() - pos); + Traits::copy(dest, data() + pos, count); + return count; + } + + /** + * @brief Obtain a substring of this string + * + * @param pos The zero-based index at which to start the new string. + * @param count The number of characters to include following `pos` in the new string. + * Automatically clamped to the available size + * + * @throws std::out_of_range if `pos` is greater than this->size() + */ + bsoncxx_cxx14_constexpr basic_string_view substr(size_type pos = 0, + size_type count = npos) const { + if (pos > size()) { + throw std::out_of_range{"bsoncxx::stdx::basic_string_view::substr()"}; + } + return basic_string_view(_begin + pos, (std::min)(count, size() - pos)); + } + + /** + * @brief Compare two strings lexicographically + * + * @param other The "right hand" operand of the comparison + * @returns `0` If *this == other + * @returns `n : n < 0` if *this is "less than" other. + * @returns `n : n > 0` if *this is "greater than" other. + */ + constexpr int compare(basic_string_view other) const noexcept { + // Another level of indirection to support restricted C++11 constexpr + return _compare2(Traits::compare(data(), other.data(), (std::min)(size(), other.size())), + other); + } + + /** + * @brief Compare *this with the given C-string + * + * @returns compare(basic_string_view(cstr)) + */ + constexpr int compare(const_pointer cstr) const { + return compare(basic_string_view(cstr)); + } + + /** + * @brief Compare a substring of *this with `other` + * + * @returns substr(po1, count1).compare(other) + */ + constexpr int compare(size_type pos1, size_type count1, basic_string_view other) const { + return substr(pos1, count1).compare(other); + } + + /** + * @brief Compare a substring of *this with the given C-string + * + * @returns substr(pos1, count1, basic_string_view(cstr)) + */ + constexpr int compare(size_type pos1, size_type count1, const_pointer cstr) const { + return compare(pos1, count1, basic_string_view(cstr)); + } + + /** + * @brief Compare a substring of *this with a substring of `other` + * + * @returns substr(pos1, count1).compare(other.substr(pos2, count2)) + */ + constexpr int compare(size_type pos1, + size_type count1, + basic_string_view other, + size_type pos2, + size_type count2) const { + return substr(pos1, count1).compare(other.substr(pos2, count2)); + } + + /** + * @brief Compare a substring of *this with a string viewed through the given pointer+size + * + * @returns substr(pos1, count1).compare(basic_string_view(str, count2)) + */ + constexpr int compare(size_type pos1, + size_type count1, + const_pointer str, + size_type count2) const { + return substr(pos1, count1).compare(basic_string_view(str, count2)); + } + + /** + * @brief Find the zero-based offset of the left-most occurrence of the given infix, + * starting with pos. If infix does not occur, returns npos. + */ + bsoncxx_cxx14_constexpr size_type find(basic_string_view infix, size_type pos = 0) const + noexcept { + if (pos > size()) { + return npos; + } + basic_string_view sub = this->substr(pos); + if (infix.empty()) { + // The empty string is always "present" at the beginning of any string + return pos; + } + const_iterator found = std::search(sub.begin(), sub.end(), infix.begin(), infix.end()); + if (found == sub.end()) { + return npos; + } + return static_cast(found - begin()); + } + + /** + * @brief Find the zero-based offset of the right-most occurrence of the given infix, + * starting with (and including) pos. If infix does not occur, returns npos. + */ + bsoncxx_cxx14_constexpr size_type rfind(basic_string_view infix, size_type pos = npos) const + noexcept { + // Calc the endpos where searching should begin, which includes the infix size + const size_type substr_size = pos != npos ? pos + infix.size() : pos; + if (infix.empty()) { + return (std::min)(pos, size()); + } + basic_string_view searched = this->substr(0, substr_size); + auto f = std::search(searched.rbegin(), searched.rend(), infix.rbegin(), infix.rend()); + if (f == searched.rend()) { + return npos; + } + return static_cast(rend() - f) - infix.size(); + } + + /** + * @brief Find the zero-based index of the left-most occurrence of any character of the given + * set, starting at pos + */ + constexpr size_type find_first_of(basic_string_view set, size_type pos = 0) const noexcept { + return _find_if(pos, [&](value_type chr) { return set.find(chr) != npos; }); + } + + /** + * @brief Find the zero-based index of the right-most occurrence of any character of the + * given set, starting at (and including) pos + */ + constexpr size_type find_last_of(basic_string_view set, size_type pos = npos) const noexcept { + return _rfind_if(pos, [&](value_type chr) { return set.find(chr) != npos; }); + } + + /** + * @brief Find the zero-based index of the left-most occurrence of any character that + * is NOT a member of the given set of characters + */ + constexpr size_type find_first_not_of(basic_string_view set, size_type pos = 0) const noexcept { + return _find_if(pos, [&](value_type chr) { return set.find(chr) == npos; }); + } + + /** + * @brief Find the zero-based index of the right-most occurrence of any character that + * is NOT a member of the given set of characters, starting at (and including) pos + */ + constexpr size_type find_last_not_of(basic_string_view set, size_type pos = npos) const + noexcept { + return _rfind_if(pos, [&](value_type chr) { return set.find(chr) == npos; }); + } + +#pragma push_macro("DECL_FINDERS") +#undef DECL_FINDERS +#define DECL_FINDERS(Name, DefaultPos) \ + constexpr size_type Name(value_type chr, size_type pos = DefaultPos) const noexcept { \ + return Name(basic_string_view(&chr, 1), pos); \ + } \ + constexpr size_type Name(const_pointer cstr, size_type pos, size_type count) const { \ + return Name(basic_string_view(cstr, count), pos); \ + } \ + constexpr size_type Name(const_pointer cstr, size_type pos = DefaultPos) const { \ + return Name(basic_string_view(cstr), pos); \ + } \ + BSONCXX_FORCE_SEMICOLON + DECL_FINDERS(find, 0); + DECL_FINDERS(rfind, npos); + DECL_FINDERS(find_first_of, 0); + DECL_FINDERS(find_last_of, npos); + DECL_FINDERS(find_first_not_of, 0); + DECL_FINDERS(find_last_not_of, npos); +#pragma pop_macro("DECL_FINDERS") + + /** + * @brief Explicit-conversion to a std::basic_string + */ + template + explicit operator std::basic_string() const { + return std::basic_string(data(), size()); + } + +#if __cpp_lib_string_view + explicit operator std::basic_string_view() const noexcept { + return std::basic_string_view(data(), size()); + } +#endif -using ::std::experimental::basic_string_view; -using ::std::experimental::string_view; + private: + // Additional level-of-indirection for constexpr compare() + constexpr int _compare2(int diff, basic_string_view other) const noexcept { + // "diff" is the diff according to Traits::cmp + return diff ? diff : static_cast(size() - other.size()); + } + + // Implementation of equality comparison + constexpr friend bool tag_invoke(bsoncxx::detail::equal_to, + basic_string_view left, + basic_string_view right) noexcept { + return left.size() == right.size() && left.compare(right) == 0; + } + + // Implementation of a three-way-comparison + constexpr friend bsoncxx::detail::strong_ordering tag_invoke( + bsoncxx::detail::compare_three_way cmp, + basic_string_view left, + basic_string_view right) noexcept { + return cmp(left.compare(right), 0); + } + + friend std::basic_ostream& operator<<(std::basic_ostream& out, + basic_string_view self) { + out << std::basic_string(self); + return out; + } + + // Find the first in-bounds index I in [pos, size()) where the given predicate + // returns true for substr(I). If no index exists, returns npos + template + bsoncxx_cxx14_constexpr size_type _find_if(size_type pos, F pred) const noexcept { + const auto sub = substr(pos); + const iterator found = std::find_if(sub.begin(), sub.end(), pred); + if (found == end()) { + return npos; + } + return static_cast(found - begin()); + } + + // Find the LAST index I in [0, pos] where the given predicate returns true for + // substr(0, I). If no such index exists, returns npos. + template + bsoncxx_cxx14_constexpr size_type _rfind_if(size_type pos, F pred) const noexcept { + // Adjust 'pos' for an inclusive range in substr() + const auto rpos = pos == npos ? npos : pos + 1; + // The substring that will be searched: + const auto prefix = substr(0, rpos); + const const_reverse_iterator found = std::find_if(prefix.rbegin(), prefix.rend(), pred); + if (found == rend()) { + return npos; + } + // Adjust by 1 to account for reversed-ness + return static_cast(rend() - found) - 1u; + } +}; + +// Required to define this here for C++≤14 compatibility. Can be removed in C++≥17 +template +const std::size_t basic_string_view::npos; + +using string_view = basic_string_view; } // namespace stdx } // namespace v_noabi } // namespace bsoncxx -#elif defined(BSONCXX_POLY_USE_STD) - -#include - namespace bsoncxx { -namespace v_noabi { namespace stdx { -using ::std::basic_string_view; -using ::std::string_view; +using ::bsoncxx::v_noabi::stdx::basic_string_view; +using ::bsoncxx::v_noabi::stdx::string_view; } // namespace stdx -} // namespace v_noabi } // namespace bsoncxx -#else -#error "Cannot find a valid polyfill for string_view" -#endif - #include -namespace bsoncxx { -namespace stdx { +namespace std { -using ::bsoncxx::v_noabi::stdx::basic_string_view; -using ::bsoncxx::v_noabi::stdx::string_view; +template +struct hash> + : private std::hash> { + std::size_t operator()( + const bsoncxx::v_noabi::stdx::basic_string_view& str) const { + return std::hash>::operator()( + std::basic_string(str.data(), str.size())); + } +}; -} // namespace stdx -} // namespace bsoncxx +} // namespace std \ No newline at end of file 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 index f9b2cf91f0..8c1f7a9e7a 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/stdx/type_traits.hpp @@ -1,3 +1,17 @@ +// Copyright 2023 MongoDB Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #pragma once #include @@ -32,7 +46,9 @@ DECL_ALIAS(make_unsigned); DECL_ALIAS(remove_reference); DECL_ALIAS(remove_const); DECL_ALIAS(remove_volatile); +DECL_ALIAS(remove_pointer); DECL_ALIAS(remove_cv); +DECL_ALIAS(add_pointer); DECL_ALIAS(add_const); DECL_ALIAS(add_volatile); DECL_ALIAS(add_lvalue_reference); @@ -54,13 +70,25 @@ using remove_cvref_t = remove_cv_t>; template using const_reference_t = add_lvalue_reference_t>; +// Workaround for CWG issue 1558 +template +struct _just_void_ { + using type = void; +}; /** * @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; +using void_t = +#if defined(_MSC_VER) && _MSC_VER < 1910 + // Old MSVC requires that the type parameters actually be "used" to trigger SFINAE at caller. + // This was resolved by CWG issue 1558 + typename _just_void_::type; +#else + void; +#endif /** * @brief Alias for integral_constant @@ -76,6 +104,10 @@ using bool_constant = std::integral_constant; template struct mp_list; +// Like std::declval, but does not generate a hard error if used. +template +extern add_rvalue_reference_t soft_declval() noexcept; + /// ## Implementation of the C++11 detection idiom namespace impl_detection { @@ -93,7 +125,7 @@ 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 +template std::false_type is_detected_f(...); // Provides the detected_or impl @@ -115,6 +147,18 @@ struct detection { using f = Oper; }; +/// Workaround: MSVC 14.0 forgets whether a type resulting from the evaluation +/// of a template-template parameter to an alias template is a reference. +template +struct vc140_detection { + using type = Dflt; +}; + +template +struct vc140_detection>, Oper, Args...> { + using type = Oper; +}; + } // namespace impl_detection /** @@ -147,8 +191,14 @@ struct is_detected * @tparam Args The arguments to give to the Oper metafunction */ template -using detected_or = typename impl_detection::detection< - is_detected::value>::template f; +using detected_or = +#if defined(_MSC_VER) && _MSC_VER < 1910 + typename impl_detection::vc140_detection::type +#else + typename impl_detection::detection< + is_detected::value>::template f +#endif + ; /** * @brief If Oper evaluates to a type, yields that type. Otherwise, yields @@ -343,31 +393,25 @@ 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)...)); + BSONCXX_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)...)); + BSONCXX_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); + constexpr static auto apply(F&& fun, Self&& self) + BSONCXX_RETURNS(static_cast(self).*fun); }; } // namespace impl_invoke @@ -382,13 +426,11 @@ static constexpr struct invoke_fn { 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)...)); + BSONCXX_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. * @@ -419,6 +461,18 @@ struct is_invocable : is_detected { template struct is_alike : std::is_same, remove_cvref_t> {}; +/** + * @brief Tag type for creating ranked overloads to force disambiguation. + * + * @tparam N The ranking of the overload. A higher value is ranked greater than + * lower values. + */ +template +struct rank : rank {}; + +template <> +struct rank<0> {}; + } // namespace detail } // namespace bsoncxx diff --git a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp index b55a50b359..8a9133c89c 100644 --- a/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp +++ b/src/bsoncxx/include/bsoncxx/v_noabi/bsoncxx/types.hpp @@ -31,13 +31,8 @@ #pragma push_macro("BSONCXX_ENUM") #undef BSONCXX_ENUM -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wfloat-equal" -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif +BSONCXX_PUSH_WARNINGS(); +BSONCXX_DISABLE_WARNING(GNU("-Wfloat-equal")); namespace bsoncxx { namespace v_noabi { @@ -679,6 +674,8 @@ BSONCXX_INLINE bool operator==(const b_maxkey&, const b_maxkey&) { } // namespace v_noabi } // namespace bsoncxx +BSONCXX_POP_WARNINGS(); + namespace bsoncxx { using ::bsoncxx::v_noabi::to_string; @@ -696,12 +693,6 @@ using ::bsoncxx::v_noabi::types::operator!=; } // namespace types } // namespace bsoncxx -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif - #ifdef BSONCXX_ENUM static_assert(false, "BSONCXX_ENUM must be undef'ed"); #endif diff --git a/src/bsoncxx/test/CMakeLists.txt b/src/bsoncxx/test/CMakeLists.txt index f3c224b02e..1c483b37d2 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 + string_view.test.cpp type_traits.test.cpp ) @@ -93,6 +94,26 @@ if(ENABLE_MACRO_GUARD_TESTS) DEFINE_NO_DEPRECATED BSONCXX_NO_DEPRECATED BSONCXX_UNREACHABLE # prelude.hpp + _bsoncxxDisableWarningImpl_for_GCC + _bsoncxxDisableWarningImpl_for_GNU + _bsoncxxDisableWarningImpl_for_MSVC + _bsoncxxDisableWarningImpl_for_Clang + BSONCXX_PUSH_WARNINGS + BSONCXX_POP_WARNINGS + BSONCXX_DISABLE_WARNING + BSONCXX_CONCAT + BSONCXX_CONCAT_IMPL + bsoncxx_cxx14_constexpr + BSONCXX_FORCE_SEMICOLON + BSONCXX_PRAGMA + _bsoncxxPragma + BSONCXX_STRINGIFY + BSONCXX_STRINGIFY_IMPL + BSONCXX_IF_MSVC + BSONCXX_IF_GCC + BSONCXX_IF_CLANG + BSONCXX_IF_GNU_LIKE + BSONCXX_RETURNS INCLUDE_PATTERNS "include/*.hpp" # Public headers. "lib/*.hh" # Private headers. @@ -121,6 +142,7 @@ 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 + string_view.test.cpp + type_traits.test.cpp ) diff --git a/src/bsoncxx/test/string_view.test.cpp b/src/bsoncxx/test/string_view.test.cpp new file mode 100644 index 0000000000..9c14d773c4 --- /dev/null +++ b/src/bsoncxx/test/string_view.test.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include + +#ifdef __has_include +#if __has_include() +#include +#endif +#endif + +#if __cpp_lib_string_view +#include +#endif + +#include +#include +#include +#include + +namespace stdx = bsoncxx::stdx; +using stdx::string_view; + +static_assert(!std::is_constructible>::value, "fail"); +static_assert(!std::is_constructible::value, "fail"); +static_assert(std::is_constructible::value, "fail"); +static_assert(std::is_convertible::value, "fail"); +static_assert(std::is_constructible::value, "fail"); + +TEST_CASE("string_view: Default constructor") { + (void)string_view(); + string_view s; + CHECK(s.size() == 0); + CHECK(s.length() == 0); + CHECK(s.empty()); + CHECK(s.data() == nullptr); + CHECK(s.begin() == s.end()); + CHECK(s == ""); +} + +static const char HELLO[] = "Hello, string_view!"; +TEST_CASE("string_view: Pointer+size construct") { + auto s = string_view(HELLO, sizeof HELLO - 1); + CHECK(s.length() == s.size()); + CHECK(s.data() == HELLO + 0); + CHECK(s[0] == 'H'); + CHECK(s == "Hello, string_view!"); + CHECK(s == HELLO); + + s = string_view(HELLO + 7, sizeof HELLO - 8); + CHECK(s.size() == sizeof HELLO - 8); + CHECK(s == "string_view!"); + s = string_view(HELLO + 7, 11); + CHECK(s == "string_view"); + CHECK_FALSE(s != "string_view"); +} + +TEST_CASE("string_view: Pointer construct") { + string_view s = "hello"; + CHECK(s.size() == 5); + CHECK(s[4] == 'o'); + CHECK(s == "hello"); + CHECK(s == s); + CHECK_FALSE(s != s); + CHECK("hello" == s); + CHECK_FALSE(s < s); +} + +TEST_CASE("string_view: Reversal") { + string_view string = "Hello!"; + auto rit = string.rbegin(); + CHECK(*rit++ == '!'); + CHECK(*rit++ == 'o'); + CHECK(*rit++ == 'l'); + CHECK(*rit++ == 'l'); + CHECK(*rit++ == 'e'); + CHECK(*rit++ == 'H'); + CHECK(rit == string.rend()); +} + +TEST_CASE("string_view: Get chars") { + string_view string = "Hello, world"; + CHECK(string[0] == 'H'); + CHECK(string[0] == string.at(0)); + CHECK(string[1] == 'e'); + CHECK(string[1] == string.at(1)); + CHECK(string[11] == 'd'); + CHECK(string[11] == string.at(11)); + CHECK_THROWS_AS(string.at(12), std::out_of_range); + CHECK(string.front() == string[0]); + CHECK(string.back() == string[11]); +} + +TEST_CASE("string_view: Substrings") { + string_view str = "0123456789"; + CHECK(str.substr(0) == str); + CHECK(str.substr(1) == "123456789"); + // Remove an empty prefix + auto dup = str; + dup.remove_prefix(0); + CHECK(str == dup); + // Remove one + dup.remove_prefix(1); + CHECK(dup == str.substr(1)); + // Remove from the end + dup = str; + dup.remove_suffix(1); + CHECK(dup == "012345678"); + // Remove all + dup = str; + dup.remove_prefix(10); + CHECK(dup.empty()); + dup = str; + dup.remove_suffix(10); + CHECK(dup.empty()); + + dup = str.substr(10); + CHECK(dup.empty()); + CHECK(dup.data() == str.data() + 10); + + dup = str.substr(0, 0); + CHECK(dup.empty()); + CHECK(dup.data() == str.data()); + + CHECK_THROWS_AS(dup.substr(500), std::out_of_range); + + // Substr of empty + dup = ""; + dup = dup.substr(0); + CHECK(dup.empty()); + dup = dup.substr(0, 1000); + CHECK(dup.empty()); +} + +TEST_CASE("string_view: Compare") { + string_view str = "abc"; + CHECK(str == "abc"); + CHECK_FALSE(str < "abc"); + CHECK(str < "abcd"); + CHECK(str.compare("abc") == 0); + CHECK(str.compare("abcd") < 0); +} + +TEST_CASE("string_view: Overloading safety") { + std::vector vec; + CHECK(vec == vec); +} + +TEST_CASE("string_view: find") { + string_view sv = "abc123abc123"; + std::string str{sv}; + auto pos = sv.find("abc"); + CHECK(pos == 0); + pos = sv.find("bc1"); + CHECK(pos == 1); + pos = sv.find("bc1", 1); + CHECK(pos == 1); + pos = sv.find("bc1", 2); + CHECK(pos == 7); + pos = sv.find("", 4); + CHECK(pos == 4); + CHECK(sv.find("") == str.find("")); + CHECK(sv.find("", 5000) == str.find("", 5000)); + CHECK(sv.find("nowhere") == sv.npos); + CHECK(sv.rfind("nowhere") == str.rfind("nowhere")); + CHECK(sv.find("123") == str.find("123")); + CHECK(sv.find("123", 88888) == str.find("123", 88888)); + CHECK(sv.rfind("123") == str.rfind("123")); + CHECK(sv.rfind("abc", 8) == str.rfind("abc", 8)); + CHECK(sv.rfind("abc", 888888) == str.rfind("abc", 888888)); + CHECK(sv.rfind("") == str.rfind("")); + CHECK(sv.rfind("", 5000) == str.rfind("", 5000)); + + CHECK(string_view("").find("") == std::string("").find("")); + CHECK(string_view("").rfind("") == std::string("").rfind("")); + + CHECK(sv.find_first_of("54321") == str.find_first_of("54321")); + CHECK(sv.find_first_of("nope") == str.find_first_of("nope")); + CHECK(sv.find_last_of("fedcba") == str.find_last_of("fedcba")); + CHECK(sv.find_last_of("nope") == str.find_last_of("nope")); + + CHECK(sv.find_first_of("54321", 5) == str.find_first_of("54321", 5)); + CHECK(sv.find_first_of("nope", 5) == str.find_first_of("nope", 5)); + CHECK(sv.find_last_of("fedcba", 5) == str.find_last_of("fedcba", 5)); + CHECK(sv.find_last_of("fedcba", 2) == str.find_last_of("fedcba", 2)); + CHECK(sv.find_last_of("nope", 5) == str.find_last_of("nope", 5)); + + CHECK(sv.find_first_not_of("abcdef") == str.find_first_not_of("abcdef")); + CHECK(sv.find_first_not_of("123456") == str.find_first_not_of("123456")); + CHECK(sv.find_last_not_of("abcdef") == str.find_last_not_of("abcdef")); + CHECK(sv.find_last_not_of("123456") == str.find_last_not_of("123456")); + CHECK(sv.find_first_not_of("abcdef123456") == str.find_first_not_of("abcdef123456")); + CHECK(sv.find_last_not_of("abcdef123456") == str.find_last_not_of("abcdef123456")); + + CHECK(sv.find_first_not_of("abcdef", 5) == str.find_first_not_of("abcdef", 5)); + CHECK(sv.find_first_not_of("123456", 5) == str.find_first_not_of("123456", 5)); + CHECK(sv.find_last_not_of("abcdef", 5) == str.find_last_not_of("abcdef", 5)); + CHECK(sv.find_last_not_of("123456", 5) == str.find_last_not_of("123456", 5)); + CHECK(sv.find_first_not_of("abcdef123456", 5) == str.find_first_not_of("abcdef123456", 5)); + CHECK(sv.find_last_not_of("abcdef123456", 5) == str.find_last_not_of("abcdef123456", 5)); +} + +#ifdef __cpp_lib_string_view +static_assert(std::is_constructible::value, "fail"); + +TEST_CASE("Convert to/from std::string_view") { + std::string std_str = "Hello!"; + string_view bson_sv = std_str; + std::string_view std_sv = std::string_view(bson_sv); + CHECK(bson_sv == std_str); + CHECK(std_sv == bson_sv); + CHECK(std_sv == std_str); + bson_sv = std_sv; + CHECK(bson_sv == std_sv); +} +#endif \ No newline at end of file diff --git a/src/bsoncxx/test/type_traits.test.cpp b/src/bsoncxx/test/type_traits.test.cpp index c871930e0c..f44aa80bff 100644 --- a/src/bsoncxx/test/type_traits.test.cpp +++ b/src/bsoncxx/test/type_traits.test.cpp @@ -4,11 +4,10 @@ #include #include -#if __GNUC__ +#include + // We declare variables that are only used for compilation checking -// (applies to Clang as well) -#pragma GCC diagnostic ignored "-Wunused" -#endif +BSONCXX_DISABLE_WARNING(GNU("-Wunused")); namespace { @@ -180,4 +179,21 @@ static_assert( value, "fail"); +struct rank_test { + template + constexpr int val(T x, bsoncxx::detail::rank<0>) const { + return x.never_instantiated(); + } + template + constexpr int val(T x, bsoncxx::detail::rank<1>) const { + return x + 30; + } + template + constexpr int operator()(T v) const { + return this->val(v, bsoncxx::detail::rank<20>{}); + } +}; + +static_assert(rank_test{}(12) == 42, "fail"); + } // namespace