Skip to content

Conversation

@eramongodb
Copy link
Contributor

@eramongodb eramongodb commented Dec 12, 2025

Resolves CXX-3237 and CXX-3238 for the v1::server_api component.

mongoc_server_api_t is not used as the underlying implementation due to the inability to convert v1::server_api::version (scoped enum: underlying type is int) to mongoc_server_api_version_t (unscoped enum: underlying type is "a suitably sized integer type"). Because "invalid API version" exceptions are only thrown upon request to convert to/from a string or by the make_server_api helper (only when required by client/pool ctors), validating this narrowing conversion with an exception would be a breaking change. Therefore, the v1::server_api fields continue to be handled independently by mongocxx rather than converted into mongoc values.

Because v1::server_api::version is completely equivalent to v_noabi::options::server_api::version, the latter is defined as an alias of the former (similar to v1::return_document). This avoids the need for v_noabi <-> v1 enumeration conversions.

@eramongodb eramongodb self-assigned this Dec 12, 2025
@eramongodb eramongodb requested a review from a team as a code owner December 12, 2025 16:43
Copy link
Collaborator

@connorsmacd connorsmacd left a comment

Choose a reason for hiding this comment

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

LGTM.


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. 😅

@eramongodb eramongodb merged commit 3fef6e6 into mongodb:master Dec 17, 2025
21 checks passed
@eramongodb eramongodb deleted the cxx-abi-v1-server_api branch December 17, 2025 16:43
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.

2 participants