Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#pragma once

#include <mongocxx/v1/server_api-fwd.hpp>

#include <mongocxx/config/prelude.hpp>

namespace mongocxx {
Expand All @@ -29,7 +31,7 @@ class server_api;
namespace mongocxx {
namespace options {

using ::mongocxx::v_noabi::options::server_api;
using v_noabi::options::server_api;

} // namespace options
} // namespace mongocxx
Expand All @@ -40,3 +42,6 @@ using ::mongocxx::v_noabi::options::server_api;
/// @file
/// Declares @ref mongocxx::v_noabi::options::server_api.
///
/// @par Includes
/// - @ref mongocxx/v1/server_api-fwd.hpp
///
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@

#pragma once

#include <mongocxx/options/server_api-fwd.hpp> // IWYU pragma: export

//

#include <mongocxx/v1/server_api.hpp> // IWYU pragma: export

#include <string>

#include <mongocxx/client-fwd.hpp>
#include <mongocxx/options/server_api-fwd.hpp> // IWYU pragma: export
#include <mongocxx/pool-fwd.hpp>
#include <mongocxx/client-fwd.hpp> // IWYU pragma: keep: backward compatibility, to be removed.
#include <mongocxx/pool-fwd.hpp> // IWYU pragma: keep: backward compatibility, to be removed.

#include <bsoncxx/stdx/optional.hpp>
#include <bsoncxx/stdx/string_view.hpp>
Expand All @@ -40,9 +45,7 @@ class server_api {
///
/// Enum representing the possible values for server API version.
///
enum class version {
k_version_1, ///< Stable API Version 1.
};
using version = v1::server_api::version;

///
/// Constructs a new server_api object.
Expand All @@ -55,7 +58,19 @@ class server_api {
/// @param version
/// The server api version to send to the server.
///
MONGOCXX_ABI_EXPORT_CDECL() server_api(version version);
/* explicit(false) */ server_api(version version) : _version{version} {}

///
/// Construct with the @ref mongocxx::v1 equivalent.
///
/* explicit(false) */ server_api(v1::server_api const& opts) : server_api{opts.get_version()} {}

///
/// Convert to the @ref mongocxx::v1 equivalent.
///
explicit operator v1::server_api() const {
return v1::server_api{_version};
}

///
/// Converts a version enum value to its string value.
Expand Down Expand Up @@ -93,15 +108,20 @@ class server_api {
/// @return
/// A reference to this object to facilitate method chaining.
///
MONGOCXX_ABI_EXPORT_CDECL(server_api&) strict(bool strict);
server_api& strict(bool strict) {
_strict = strict;
return *this;
}

///
/// Gets the current value of the strict option.
///
/// @return
/// The optional value of the strict option.
///
MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional<bool> const&) strict() const;
bsoncxx::v_noabi::stdx::optional<bool> const& strict() const {
return _strict;
}

///
/// Sets the deprecation errors option, specifying whether the server should
Expand All @@ -113,29 +133,32 @@ class server_api {
/// @return
/// A reference to this object to facilitate method chaining.
///
MONGOCXX_ABI_EXPORT_CDECL(server_api&) deprecation_errors(bool deprecation_errors);
server_api& deprecation_errors(bool deprecation_errors) {
_deprecation_errors = deprecation_errors;
return *this;
}

///
/// Gets the current value of the deprecation errors option.
///
/// @return
/// The optional value of the deprecation errors option.
///
MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional<bool> const&)
deprecation_errors() const;
bsoncxx::v_noabi::stdx::optional<bool> const& deprecation_errors() const {
return _deprecation_errors;
}

///
/// Gets the declared server api version.
///
/// @return
/// The version enum value specifying the declared server api version.
///
MONGOCXX_ABI_EXPORT_CDECL(version) get_version() const;
version get_version() const {
return _version;
}

private:
friend ::mongocxx::v_noabi::client;
friend ::mongocxx::v_noabi::pool;

version _version;
bsoncxx::v_noabi::stdx::optional<bool> _strict;
bsoncxx::v_noabi::stdx::optional<bool> _deprecation_errors;
Expand All @@ -145,9 +168,32 @@ class server_api {
} // namespace v_noabi
} // namespace mongocxx

namespace mongocxx {
namespace v_noabi {

///
/// Convert to the @ref mongocxx::v_noabi equivalent of `v`.
///
inline v_noabi::options::server_api from_v1(v1::server_api const& v) {
return {v};
}

///
/// Convert to the @ref mongocxx::v1 equivalent of `v`.
///
inline v1::server_api to_v1(v_noabi::options::server_api const& v) {
return v1::server_api{v};
}

} // namespace v_noabi
} // namespace mongocxx

#include <mongocxx/config/postlude.hpp>

///
/// @file
/// Provides @ref mongocxx::v_noabi::options::server_api.
///
/// @par Includes
/// - @ref mongocxx/v1/server_api.hpp
///
193 changes: 193 additions & 0 deletions src/mongocxx/lib/mongocxx/v1/server_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,196 @@
// limitations under the License.

#include <mongocxx/v1/server_api.hpp>

//

#include <bsoncxx/v1/stdx/optional.hpp>
#include <bsoncxx/v1/stdx/string_view.hpp>

#include <mongocxx/v1/exception.hh>

#include <string>
#include <system_error>

#include <bsoncxx/private/immortal.hh>

#include <mongocxx/private/mongoc.hh>
#include <mongocxx/private/utility.hh>

namespace mongocxx {
namespace v1 {

using code = server_api::errc;

namespace {

static_assert(
static_cast<int>(server_api::version::k_version_1) ==
static_cast<int>(mongoc_server_api_version_t::MONGOC_SERVER_API_V1),
"");

server_api::version from_mongoc(mongoc_server_api_version_t v) {
return static_cast<server_api::version>(v);
}

} // namespace

class server_api::impl {
public:
version _version;
bsoncxx::v1::stdx::optional<bool> _strict;
bsoncxx::v1::stdx::optional<bool> _deprecation_errors;

explicit impl(version v) : _version{v} {}

static impl const& with(server_api const& self) {
return *static_cast<impl*>(self._impl);
}

static impl const* with(server_api const* self) {
return static_cast<impl*>(self->_impl);
}

static impl& with(server_api& self) {
return *static_cast<impl*>(self._impl);
}

static impl* with(server_api* self) {
return static_cast<impl*>(self->_impl);
}

static impl* with(void* ptr) {
return static_cast<impl*>(ptr);
}
};

// NOLINTBEGIN(cppcoreguidelines-owning-memory): owning void* for ABI stability.

server_api::~server_api() {
delete impl::with(this);
}

server_api::server_api(server_api&& other) noexcept : _impl{exchange(other._impl, nullptr)} {}

server_api& server_api::operator=(server_api&& other) noexcept {
if (this != &other) {
delete impl::with(exchange(_impl, exchange(other._impl, nullptr)));
}

return *this;
}

server_api::server_api(server_api const& other) : _impl{new impl{impl::with(other)}} {}

server_api& server_api::operator=(server_api const& other) {
if (this != &other) {
delete impl::with(exchange(_impl, new impl{impl::with(other)}));
}

return *this;
}

server_api::server_api(version v) : _impl{new impl{v}} {}

// NOLINTEND(cppcoreguidelines-owning-memory)

std::string server_api::version_to_string(version v) {
switch (v) {
case version::k_version_1:
return "1";
default:
throw v1::exception::internal::make(code::invalid_version);
}
}

server_api::version server_api::version_from_string(bsoncxx::v1::stdx::string_view v) {
mongoc_server_api_version_t ver = {};

if (libmongoc::server_api_version_from_string(std::string{v}.c_str(), &ver)) {
return from_mongoc(ver);
}

throw v1::exception::internal::make(code::invalid_version);
}

server_api& server_api::strict(bool strict) {
impl::with(this)->_strict = strict;
return *this;
}

bsoncxx::v1::stdx::optional<bool> server_api::strict() const {
return impl::with(this)->_strict;
}

server_api& server_api::deprecation_errors(bool v) {
impl::with(this)->_deprecation_errors = v;
return *this;
}

bsoncxx::v1::stdx::optional<bool> server_api::deprecation_errors() const {
return impl::with(this)->_deprecation_errors;
}

server_api::version server_api::get_version() const {
return impl::with(this)->_version;
}

std::error_category const& server_api::error_category() {
class type final : public std::error_category {
char const* name() const noexcept override {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a particular reason you chose to use the final specifier for inheritance, but the override specifier for the virtual member functions?

I imagine this is of little consequence, but I am curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Admittedly, I hadn't thought too deeply about this. If I had to give a reason, it'd be that final classes are not unusual or uncommon, but final virtual member functions are unusual and uncommon. The final specifier on the class effectively applies final to all the virtual member functions by construction. The use of override for virtual member functions is for consistency with the usual "I expect this member function to override a base class' virtual function" practice, regardless whether the class is final or not. Quoting CppCoreGuidelines C.128:

Note: On a class defined as final, each individual virtual function should use either override or final; there is no semantic difference in this case.

Note: Use final on functions sparingly. It does not necessarily lead to optimization, and it precludes further overriding.

Therefore, I would reserve the use of final on a virtual member function for cases where the class is not also final to further highlight that such a situation is unusual and uncommon. If you asked me to give an example of when such a situation may be justified, I don't have an answer. 😅

return "mongocxx::v1::server_api";
}

std::string message(int v) const noexcept override {
switch (static_cast<code>(v)) {
case code::zero:
return "zero";
case code::invalid_version:
return "invalid server API version";
default:
return std::string(this->name()) + ':' + std::to_string(v);
}
}

bool equivalent(int v, std::error_condition const& ec) const noexcept override {
if (ec.category() == v1::source_error_category()) {
using condition = v1::source_errc;

auto const source = static_cast<condition>(ec.value());

switch (static_cast<code>(v)) {
case code::invalid_version:
return source == condition::mongocxx;

case code::zero:
default:
return false;
}
}

if (ec.category() == v1::type_error_category()) {
using condition = v1::type_errc;

auto const type = static_cast<condition>(ec.value());

switch (static_cast<code>(v)) {
case code::invalid_version:
return type == condition::invalid_argument;

case code::zero:
default:
return false;
}
}

return false;
}
};

static bsoncxx::immortal<type> const instance;

return instance.value();
}

} // namespace v1
} // namespace mongocxx
Loading