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

Conversation

eramongodb
Copy link
Contributor

@eramongodb eramongodb commented May 28, 2025

Resolves CXX-3232. Followup to #1318, #1401, and #1402.

This is 2 out of an estimated 7 major PRs which in total are expected to resolve CXX-2745.

This PR introduces the very first set of v1 ABI exported symbols. 🎉

image

... only because MSVC complains otherwise. The rest will come as planned in CXX-3233 (impls).


Due to the volume of changes relative to the equivalent v_noabi interfaces, the bulk of relative changelog (v_noabi -> v1) and rationale for design decisions are documented inline as GitHub comments (coincidentally, exactly 100 comments in total).

In summary, notable changes include:

  • General Notes
    • Nearly all preconditions are now documented and well-defined to instead throw an exception or yield an "invalid" state.
    • Narrowing casts and discarded return values are now thoroughly accounted for as new error codes.
    • All potentially-thrown exceptions are now documented and include the list of error codes which may be thrown.
  • v1::error
    • All exceptions thrown by library components have a corresponding error code enumeration declared in v1::error with corresponding error category declared in v1::error::category.
    • Error conditions source and type are provided as the primary user-facing error code query mechanism when component-specific error codes are not necessary.
  • v1::types::b_*
    • All types are now consistently non-aggregate for list-initialization consistency.
    • All "single underlying value" types now consistently support implicit conversion to said value.
    • Multi-argument constructors are no longer explicit (always favored over value).
  • v1::types::view
    • This type is now semitrivial (no user-provided non-default special member functions).
    • Conversion to this type is now favored over conversion to value when applicable.
  • v1::types::value
    • All (non-default) constructors are now explicit (b_* is always favored) so either b_* or view (cheap) is always preferred to value (expensive) when applicable.
    • v1::types::id constructors are removed in favor of b_* and converting constructors.
    • "Inline PIMPL" avoids redundant allocation while preserving encapsulation.
  • v1::element::view
    • The "invalid" state is now clarified and thoroughly documented.
    • "Inline PIMPL" leaves room for an alternative bool-based approach to "missing field" diagnostics (CXX-2120).
    • Although not semitrivial, the class is still nothrow copyable.
    • Equality comparison is defined as "same field in same BSON binary data". Relational comparison is also possible but deferred for now.
  • v1::document::view
    • Now supports an "invalid" state without underlying BSON binary data.
  • v1::array::view
    • Now (nearly) completely defined in terms of v1::document::view.
    • API is extended for better parity with v1::document::view (CXX-2118).
  • v1::document::value
    • Now default-constructible (CXX-939), equivalent to an "invalid" v1::document::view.
    • deleter_type is now a std::function<T> instead of a function pointer.
    • Customization points for to_bson and from_bson are now properly constrained.
  • v1::array::value
    • Now (nearly) completely defined in terms of v1::document::value.
    • API is extended for better parity with v1::document::value (CXX-2118).

@eramongodb eramongodb self-assigned this May 28, 2025
@eramongodb eramongodb force-pushed the cxx-abi-v1-decls branch 2 times, most recently from b52a537 to 07f460e Compare May 30, 2025 17:52
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.

std::is_trivially_copy_constructible<T>,
std::is_trivially_copy_assignable<T>> {};
struct is_semitrivial
// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/libstdc++/manual/manual/status.html
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, GCC 4.X (used by RHEL 7) does not support the std::is_trivially_* type traits. Special-case this compiler version by simply assuming this property to be true (we have plenty of true coverage with other compilers and compiler versions on other platforms).

Comment on lines +29 to +31
static_assert(
is_nothrow_moveable<view::const_iterator>::value,
"bsoncxx::v1::document::view::const_iterator must be nothrow moveable");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Despite v1::document::view being semitrivial, its iterator is only nothrow moveable due to its internal v1::element::view data member.

Comment on lines +29 to +31
static_assert(
is_nothrow_moveable<view::const_iterator>::value,
"bsoncxx::v1::array::view::const_iterator must be nothrow moveable");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Despite v1::array::view being semitrivial, its iterator is only nothrow moveable due to its internal v1::element::view data member.

namespace bsoncxx {
namespace v1 {

exception::~exception() = default;
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 key function for the bsoncxx::v1::exception class and the first v1 ABI symbol (along with the vtable) to be exported by the bsoncxx library. 🎉

Although this is meant a declarations-only PR, this definition is required to support successful builds with MSVC, as otherwise:

error LNK2019: unresolved external symbol "public: virtual __cdecl bsoncxx::v1::exception::~exception(void)" (??1exception@v1@bsoncxx@@UEAA@XZ) referenced in function "public: virtual void * __cdecl bsoncxx::v1::exception::`vector deleting destructor'(unsigned int)" (??_Eexception@v1@bsoncxx@@UEAAPEAXI@Z)

Comment on lines +276 to +278
deleter_type const& get_deleter() const {
return _data.get_deleter();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A non-const .get_deleter() is deliberately not provided to prevent "desync" of the deleter from the associated BSON binary data. Read-only access is nevertheless essential to support inspection of the type of underlying deleter, such as for v_noabi compatibility (e.g. "Is this deleter compatible with v_noabi::document::value::deleter_type?"). This will be explored in CXX-3234 (v_noabi refactor).

Comment on lines +314 to +319
///
/// Implicitly convert to `this->view()`.
///
/* explicit(false) */ operator v1::document::view() const {
return this->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.

Asymmetry: value -> view is implicit, but view -> value is explicit.

Comment on lines +317 to +322
///
/// Implicitly convert to `this->view()`.
///
/* explicit(false) */ operator v1::types::view() const {
return this->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.

Asymmetry: value -> view is implicit, but view -> value is explicit.

Comment on lines +307 to +312
///
/// Return a view of the BSON binary data as a document.
///
v1::document::view view() const {
return {_data.get(), _length};
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All member functions below are defined in terms of v1::document::view, including error codes and Doxygen documentation.

Important

The new "invalid" state of a v1::document::view allows this conversion to be well-defined even when a value is default-initialized (_data.get() == nullptr).

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.

@eramongodb eramongodb marked this pull request as ready for review May 30, 2025 20:31
@eramongodb eramongodb requested a review from a team as a code owner May 30, 2025 20:31
@eramongodb eramongodb requested review from rcsanchez97, kevinAlbs and vector-of-bool and removed request for rcsanchez97 May 30, 2025 20:32
@eramongodb eramongodb changed the title CXX-3232 add v1 interface declarations CXX-3232 add bsoncxx v1 declarations May 30, 2025
Comment on lines +248 to +250
/// @note This iterator almost satisfies Cpp17ForwardIterator, but `std::iterator_traits<T>::reference` is defined as
/// `value_type`, similar to `std::vector<bool>::iterator` and `std::istreambuf_iterator<T>`. Therefore, this iterator
/// only fully satisfies Cpp17InputIterator.
Copy link
Contributor Author

@eramongodb eramongodb Jun 2, 2025

Choose a reason for hiding this comment

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

Update: renamed Legacy -> Cpp17 for consistency with standard spec for C++20 and newer (by P0896R4; cppreference still uses "Legacy", a leftover from when the C++20 spec was still being drafted) + restoring this comment due to the associated changes of the prior comment being lost by GitHub.


The v1 ABI interfaces address and clarify several issues with iterators that were identified during work on CXX-3082:

  • Missing const qualifiers on multiple member functions, e.g. auto const iter = doc.begin(); use(*iter); is ill-formed (?!).
  • iterator_category = std::forward_iterator_tag despite not actually satisfying the requirements of LegacyForwardIterator:
    • e.g. does not satisfy it: const_iterator -> *it -> T const&.
    • e.g. does not satisfy a == b -> std::addressof(*a) == std::addressof(*b).
  • Operations on an end iterator behavior, such as deference yielding an invalid element and increment yielding another end iterator, are underdocumented not documented at all.

v1::document::view::const_iterator (and v1::array::view::const_iterator) addresses all these issues by ensuring it properly satisfies and declares itself to be a LegacyInputIterator instead (even though it may provide some features of a LegacyForwardIterator).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant