Skip to content

CXX-3232 add bsoncxx v1 declarations #1412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .evergreen/scripts/abidiff-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ echo "---" >>cxx-noabi/mongocxx.txt

# Allow task to upload the diff reports despite failed status.
echo "Comparing stable ABI for bsoncxx..."
if ! abidiff "${abi_flags[@]:?}" install/old/lib/libbsoncxx.so install/new/lib/libbsoncxx.so >>cxx-abi/bsoncxx.txt; then
abidiff "${abi_flags[@]:?}" install/old/lib/libbsoncxx.so install/new/lib/libbsoncxx.so >>cxx-abi/bsoncxx.txt && ret="$?" || ret="$?"
if (("$ret" & 0x03)); then # ABIDIFF_ERROR (1) | ABIDIFF_USAGE_ERROR (2)
echo "abidiff error" >&2
exit 1
elif (("$ret" & 0x08)); then # ABIDIFF_ABI_INCOMPATIBLE_CHANGE (8).
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per abidiff docs:

The exit code of the abidiff command is either 0 if the ABI of the binaries being compared are equal, or non-zero if they differ or if the tool encountered an error. In the later case, the exit code is a 8-bits-wide bit field in which each bit has a specific meaning.

This PR introduces the first exported v1 ABI symbol: bsoncxx::v1::exception::~exception(). This new symbol triggered a non-zero exit code of ABIDIFF_ABI_CHANGE (4), which was incorrectly handled as incompatibility/failure. The script is updated to distinguish the individual error codes appropriately: we only care about incompatible changes. Compatible changes (such as this) will still be included in the resulting abidiff report.

declare status
status='{"status":"failed", "type":"test", "should_continue":true, "desc":"abidiff returned an error for bsoncxx (stable)"}'
curl -sS -d "${status:?}" -H "Content-Type: application/json" -X POST localhost:2285/task_status || true
Expand All @@ -63,7 +67,11 @@ echo "Comparing stable ABI for bsoncxx... done."

# Allow task to upload the diff reports despite failed status.
echo "Comparing stable ABI for mongocxx..."
if ! abidiff "${abi_flags[@]:?}" install/old/lib/libmongocxx.so install/new/lib/libmongocxx.so >>cxx-abi/mongocxx.txt; then
abidiff "${abi_flags[@]:?}" install/old/lib/libmongocxx.so install/new/lib/libmongocxx.so >>cxx-abi/mongocxx.txt && ret="$?" || ret="$?"
if (("$ret" & 0x03)); then # ABIDIFF_ERROR (1) | ABIDIFF_USAGE_ERROR (2)
echo "abidiff error" >&2
exit 1
elif (("$ret" & 0x08)); then # ABIDIFF_ABI_INCOMPATIBLE_CHANGE (8)
declare status
status='{"status":"failed", "type":"test", "should_continue":true, "desc":"abidiff returned an error for mongocxx (stable)"}'
curl -sS -d "${status:?}" -H "Content-Type: application/json" -X POST localhost:2285/task_status || true
Expand Down
176 changes: 175 additions & 1 deletion src/bsoncxx/include/bsoncxx/v1/array/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@

#include <bsoncxx/v1/detail/prelude.hpp>

#include <bsoncxx/v1/array/view.hpp>
#include <bsoncxx/v1/config/export.hpp>
#include <bsoncxx/v1/document/value.hpp>

#include <cstdint>
#include <cstring>
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>

namespace bsoncxx {
namespace v1 {
namespace array {
Expand All @@ -29,7 +40,165 @@ namespace array {
///
/// @attention This feature is experimental! It is not ready for use!
///
class value {};
class value {
private:
v1::document::value _value;
Copy link
Contributor Author

@eramongodb eramongodb May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All behavior for this class is defined in terms of v1::document::value or v1::array::view, including error codes and Doxygen documentation.


template <typename T>
struct is_valid_deleter : std::is_constructible<v1::document::value, std::uint8_t*, std::size_t, T> {};

public:
/// @copydoc v1::document::value::deleter_type
using deleter_type = v1::document::value::deleter_type;

/// @copydoc v1::document::value::default_deleter_type
using default_deleter_type = v1::document::value::default_deleter_type;

/// @copydoc v1::document::value::unique_ptr_type
using unique_ptr_type = v1::document::value::unique_ptr_type;

/// @copydoc v1::document::view::const_iterator
using const_iterator = v1::document::view::const_iterator;

/// @copydoc v1::document::view::iterator
using iterator = const_iterator;

/// @copydoc v1::document::value::~value()
~value() = default;

/// @copydoc v1::document::value::value(v1::document::value&& other) noexcept
value(value&& other) noexcept : _value{std::move(other._value)} {}

/// @copydoc v1::document::value::operator=(v1::document::value&& other) noexcept
value& operator=(value&& other) noexcept {
_value = std::move(other._value);
return *this;
}

/// @copydoc v1::document::value::value(v1::document::value const& other)
value(value const& other) : _value(other._value) {}

/// @copydoc v1::document::value::operator=(v1::document::value const& other)
value& operator=(value const& other) {
_value = other._value;
return *this;
}

/// @copydoc v1::document::value::value()
value() = default;

/// @copydoc v1::document::value::value(std::uint8_t* data, std::size_t length, Deleter deleter)
template <typename Deleter, detail::enable_if_t<is_valid_deleter<Deleter>::value>* = nullptr>
value(std::uint8_t* data, std::size_t length, Deleter deleter) : _value{data, length, std::move(deleter)} {}

/// @copydoc v1::document::value::value(std::uint8_t* data, std::size_t length)
value(std::uint8_t* data, std::size_t length) : _value{data, length} {}

/// @copydoc v1::document::value::value(v1::document::value::unique_ptr_type ptr, std::size_t length)
value(unique_ptr_type ptr, std::size_t length) : _value{std::move(ptr), length} {}

/// @copydoc v1::document::value::value(v1::document::view const& view)
explicit value(v1::array::view view) : _value{view} {}

/// @copydoc v1::document::value::get_deleter() const
deleter_type const& get_deleter() const {
return _value.get_deleter();
}

/// @copydoc v1::document::value::release()
unique_ptr_type release() {
return _value.release();
}

/// @copydoc v1::document::value::reset(v1::document::value v)
void reset(value v) {
_value = std::move(v._value);
}

/// @copydoc v1::document::value::reset(v1::document::view const& v)
void reset(v1::array::view v) {
*this = value{v};
}

///
/// Return a view of the BSON binary data as an array.
///
v1::array::view view() const {
return {_value.data(), _value.size()};
}

///
/// Implicitly convert to `this->view()`.
///
/* explicit(false) */ operator v1::array::view() const {
return this->view();
}

/// @copydoc v1::array::view::cbegin() const
v1::array::view::const_iterator cbegin() const {
return this->view().cbegin();
}

/// @copydoc v1::array::view::cend() const
v1::array::view::const_iterator cend() const {
return this->view().cend();
}

/// @copydoc v1::array::view::begin() const
v1::array::view::const_iterator begin() const {
return this->view().begin();
}

/// @copydoc v1::array::view::end() const
v1::array::view::const_iterator end() const {
return this->view().end();
}

/// @copydoc v1::array::view::find(std::uint32_t i) const
v1::array::view::const_iterator find(std::uint32_t i) const {
return this->view().find(i);
}

/// @copydoc v1::array::view::operator[](std::uint32_t i) const
v1::element::view operator[](std::uint32_t i) const {
return this->view()[i];
}

/// @copydoc v1::array::view::data() const
std::uint8_t const* data() const {
return this->view().data();
}

/// @copydoc v1::array::view::size() const
std::size_t size() const {
return this->view().size();
}

/// @copydoc v1::array::view::length() const
std::size_t length() const {
return this->view().length();
}

/// @copydoc v1::array::view::empty() const
bool empty() const {
return this->view().empty();
}

/// @copydoc v1::array::view::operator bool() const
explicit operator bool() const {
return this->view().operator bool();
}

/// @copydoc v1::array::view::operator==(v1::array::view const& lhs, v1::array::view const& rhs)
friend bool operator==(value const& lhs, value const& rhs) {
return lhs.view() == rhs.view();
}

/// @copydoc v1::array::view::operator!=(v1::array::view const& lhs, v1::array::view const& rhs)
friend bool operator!=(value const& lhs, value const& rhs) {
return !(lhs == rhs);
}
};

} // namespace array
} // namespace v1
Expand All @@ -41,3 +210,8 @@ class value {};
/// @file
/// Provides @ref bsoncxx::v1::array::value.
///
/// @par Includes
/// - @ref bsoncxx/v1/array/view.hpp
/// - @ref bsoncxx/v1/document/value.hpp
/// - @ref bsoncxx/v1/element/view.hpp
///
116 changes: 115 additions & 1 deletion src/bsoncxx/include/bsoncxx/v1/array/view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,126 @@

#include <bsoncxx/v1/detail/prelude.hpp>

#include <bsoncxx/v1/config/export.hpp>
#include <bsoncxx/v1/document/view.hpp>
#include <bsoncxx/v1/element/view.hpp>

#include <cstddef>
#include <cstdint>
#include <iterator>

namespace bsoncxx {
namespace v1 {
namespace array {

///
/// A non-owning, read-only BSON array.
///
/// An "invalid" view, as indicated by @ref operator bool() const, does not satisfy the minimum requirements of a valid
/// BSON document, which are that:
///
/// - @ref data() is not null, and
/// - @ref size() is not less than `5` (the minimum size of a BSON document).
///
/// The BSON binary data being represented is only validated as minimally required to satisfy a requested operation.
/// When an operation is not satisfiable due to invalid data, the operation will throw an @ref bsoncxx::v1::exception
/// with @ref bsoncxx::v1::error::document::view::invalid_data.
///
/// @attention This feature is experimental! It is not ready for use!
///
class view {};
class view {
public:
/// @copydoc v1::document::view::const_iterator
using const_iterator = v1::document::view::const_iterator;

/// @copydoc v1::document::view::iterator
using iterator = const_iterator;

private:
v1::document::view _view;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the similarity in behavior between v1::document::view and v1::array::view, the latter is primarily implemented in terms of the former, including error codes and Doxygen documentation.


public:
/// @copydoc v1::document::view::view()
view() = default;

/// @copydoc v1::document::view::view(std::uint8_t const* data, std::size_t length)
view(std::uint8_t const* data, std::size_t length) : _view{data, length} {}

/// @copydoc v1::document::view::data() const
std::uint8_t const* data() const {
return _view.data();
}

/// @copydoc v1::document::view::size() const
std::size_t size() const {
return _view.size();
}

/// @copydoc v1::document::view::length() const
std::size_t length() const {
return _view.length();
}

/// @copydoc v1::document::view::empty() const
bool empty() const {
return _view.empty();
}

/// @copydoc v1::document::view::operator bool() const
explicit operator bool() const {
return _view.operator bool();
}

/// @copydoc v1::document::view::cbegin() const
BSONCXX_ABI_EXPORT_CDECL(const_iterator) cbegin() const;

/// @copydoc v1::document::view::cend() const
const_iterator cend() const {
return {};
}

/// @copydoc v1::document::view::cbegin() const
const_iterator begin() const {
return this->cbegin();
}

/// @copydoc v1::document::view::cend() const
const_iterator end() const {
return this->cend();
}

///
/// Return a const iterator to the element within the represented BSON array at index `i`.
///
/// If this view is invalid, returns an end iterator.
///
/// @exception bsoncxx::v1::exception with @ref bsoncxx::v1::error::document::view::invalid_data if this operation
/// failed due to invalid BSON binary data.
///
BSONCXX_ABI_EXPORT_CDECL(const_iterator) find(std::uint32_t i) const;

/// @copydoc find(std::uint32_t i) const
v1::element::view operator[](std::uint32_t i) const {
return *(this->find(i));
}
Comment on lines +111 to +124
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only discernable difference between v1::document::view and v1::array::view: support for index-based access of elements instead of key-based access.


///
/// Implicitly convert to a @ref bsoncxx::v1::document::view.
///
/* explicit(false) */ operator v1::document::view() const {
return _view;
}

/// @copydoc v1::document::view::operator==(v1::document::view const& lhs, v1::document::view const& rhs)
friend bool operator==(view const& lhs, view const& rhs) {
return lhs._view == rhs._view;
}

/// @copydoc v1::document::view::operator!=(v1::document::view const& lhs, v1::document::view const& rhs)
friend bool operator!=(view const& lhs, view const& rhs) {
return !(lhs == rhs);
}
};

} // namespace array
} // namespace v1
Expand All @@ -41,3 +151,7 @@ class view {};
/// @file
/// Provides @ref bsoncxx::v1::array::view.
///
/// @par Includes
/// - @ref bsoncxx/v1/document/view.hpp
/// - @ref bsoncxx/v1/element/view.hpp
///
Loading