summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamy Lanka <samy.lanka@mongodb.com>2020-11-20 01:05:41 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-23 22:56:55 +0000
commita2c49f836faedb6ef9e6d43459f6d6949525e0b1 (patch)
tree99649396eae188385786d268e1011c848d8392a6
parent7654bb8401386c2ae2a6e482e973227ce6c0e98b (diff)
downloadmongo-a2c49f836faedb6ef9e6d43459f6d6949525e0b1.tar.gz
SERVER-51862 Generate a C++ base class for each V1 command
-rw-r--r--buildscripts/idl/idl/ast.py3
-rw-r--r--buildscripts/idl/idl/binder.py4
-rw-r--r--buildscripts/idl/idl/generator.py90
-rw-r--r--buildscripts/idl/idl/syntax.py1
-rw-r--r--src/mongo/db/commands/generic.cpp21
5 files changed, 91 insertions, 28 deletions
diff --git a/buildscripts/idl/idl/ast.py b/buildscripts/idl/idl/ast.py
index 1d0b97a1f82..7ae0a3c7f33 100644
--- a/buildscripts/idl/idl/ast.py
+++ b/buildscripts/idl/idl/ast.py
@@ -215,6 +215,7 @@ class Command(Struct):
# type: (str, int, int) -> None
"""Construct a command."""
self.namespace = None # type: str
+ self.command_name = None # type: str
self.command_field = None # type: Field
self.reply_type = None # type: Field
self.api_version = "" # type: str
@@ -268,8 +269,6 @@ class FieldListEntry(common.SourceLocation):
self.name = None # type: str
self.forward_to_shards = False # type: bool
self.forward_from_shards = False # type: bool
-
- self.command_name = None # type: str
super(FieldListEntry, self).__init__(file_name, line, column)
def get_should_forward(self):
diff --git a/buildscripts/idl/idl/binder.py b/buildscripts/idl/idl/binder.py
index 20eebf0158c..b449c962add 100644
--- a/buildscripts/idl/idl/binder.py
+++ b/buildscripts/idl/idl/binder.py
@@ -482,6 +482,8 @@ def _bind_command_reply_type(ctxt, parsed_spec, command):
if not isinstance(syntax_symbol, syntax.Struct):
ctxt.add_reply_type_invalid_type(ast_field, command.name, command.reply_type)
+ else:
+ ast_field.struct_type = syntax_symbol.name
return ast_field
@@ -497,6 +499,8 @@ def _bind_command(ctxt, parsed_spec, command):
ast_command = ast.Command(command.file_name, command.line, command.column)
ast_command.api_version = command.api_version
+ ast_command.is_deprecated = command.is_deprecated
+ ast_command.command_name = command.command_name
# Inject special fields used for command parsing
_inject_hidden_command_fields(command)
diff --git a/buildscripts/idl/idl/generator.py b/buildscripts/idl/idl/generator.py
index 36c8976e7c6..71667f94539 100644
--- a/buildscripts/idl/idl/generator.py
+++ b/buildscripts/idl/idl/generator.py
@@ -514,6 +514,8 @@ class _CppFileWriterBase(object):
class _CppHeaderFileWriter(_CppFileWriterBase):
"""C++ .h File writer."""
+ # pylint: disable=too-many-public-methods
+
def gen_class_declaration_block(self, class_name):
# type: (str) -> writer.IndentedScopedBlock
"""Generate a class declaration block."""
@@ -715,8 +717,8 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
if isinstance(struct, ast.Command):
self._writer.write_line(
- common.template_args('static constexpr auto kCommandName = "${struct_name}"_sd;',
- struct_name=struct.name))
+ common.template_args('static constexpr auto kCommandName = "${command_name}"_sd;',
+ command_name=struct.command_name))
def gen_enum_functions(self, idl_enum):
# type: (ast.Enum) -> None
@@ -910,6 +912,76 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
self.write_empty_line()
+ def gen_template_declaration(self):
+ # type: () -> None
+ """Generate a template declaration for a command's base class."""
+ self._writer.write_line('template <typename Derived>')
+
+ def gen_derived_class_declaration_block(self, command_name, api_version):
+ # type: (str, str) -> writer.IndentedScopedBlock
+ """Generate a command's base class declaration block."""
+ class_name = common.title_case(command_name) + "CmdVersion" + api_version + "Gen"
+ return writer.IndentedScopedBlock(
+ self._writer, 'class %s : public TypedCommand<Derived> {' % class_name, '};')
+
+ def gen_type_alias_declaration(self, new_type_name, old_type_name):
+ # type: (str, str) -> None
+ """Generate a type alias declaration."""
+ self._writer.write_line(
+ 'using %s = %s;' % (new_type_name, common.title_case(old_type_name)))
+
+ def gen_api_version_fn(self, is_api_versions, api_version):
+ # type: (bool, Union[str, bool]) -> None
+ """Generate an apiVersions or deprecatedApiVersions function for a command's base class."""
+ fn_name = "apiVersions" if is_api_versions else "deprecatedApiVersions"
+ fn_def = 'virtual const std::set<std::string>& %s() const override' % fn_name
+ value = "kApiVersions1" if api_version else "kNoApiVersions"
+ with self._block('%s {' % (fn_def), '}'):
+ self._writer.write_line('return %s;' % value)
+
+ def gen_invocation_base_class_declaration(self):
+ # type: () -> None
+ """Generate the InvocationBaseGen class for a command's base class."""
+ class_declaration = 'class InvocationBaseGen : public _TypedCommandInvocationBase {'
+ with writer.IndentedScopedBlock(self._writer, class_declaration, '};'):
+ # public requires special indentation that aligns with the class definition.
+ self._writer.unindent()
+ self._writer.write_line('public:')
+ self._writer.indent()
+
+ # Inherit base constructor.
+ self._writer.write_line(
+ 'using _TypedCommandInvocationBase::_TypedCommandInvocationBase;')
+
+ self._writer.write_line('virtual Reply typedRun(OperationContext* opCtx) = 0;')
+
+ def generate_versioned_command_base_class(self, command):
+ # type: (ast.Command) -> None
+ """Generate a command's C++ base class to a stream."""
+ self.write_empty_line()
+
+ self.gen_template_declaration()
+
+ with self.gen_derived_class_declaration_block(command.command_name, command.api_version):
+ # Write type alias for InvocationBase.
+ self.gen_type_alias_declaration('_TypedCommandInvocationBase',
+ 'typename TypedCommand<Derived>::InvocationBase')
+
+ self.write_empty_line()
+
+ self.write_unindented_line('public:')
+
+ # Write type aliases for Request and Reply.
+ self.gen_type_alias_declaration("Request", command.command_name)
+ self.gen_type_alias_declaration("Reply", command.reply_type.struct_type)
+
+ # Write apiVersions() and deprecatedApiVersions() functions.
+ self.gen_api_version_fn(True, command.api_version)
+ self.gen_api_version_fn(False, command.is_deprecated)
+
+ # Write InvocationBaseGen class.
+ self.gen_invocation_base_class_declaration()
+
def generate(self, spec):
# type: (ast.IDLAST) -> None
"""Generate the C++ header to a stream."""
@@ -961,6 +1033,11 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
header_list.append('mongo/idl/server_parameter.h')
header_list.append('mongo/idl/server_parameter_with_storage.h')
+ # Include this for TypedCommand only if a base class will be generated for a command in this
+ # file.
+ if any(command.api_version for command in spec.commands):
+ header_list.append('mongo/db/commands.h')
+
header_list.sort()
for include in header_list:
@@ -968,7 +1045,7 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
self.write_empty_line()
- # Generate namesapce
+ # Generate namespace
with self.gen_namespace_block(spec.globals.cpp_namespace):
self.write_empty_line()
@@ -1077,6 +1154,11 @@ class _CppHeaderFileWriter(_CppFileWriterBase):
self._gen_extern_declaration(opt.cpp_vartype, opt.cpp_varname, opt.condition)
self._gen_config_function_declaration(spec)
+ # Write a base class for each command in API Version 1.
+ for command in spec.commands:
+ if command.api_version:
+ self.generate_versioned_command_base_class(command)
+
class _CppSourceFileWriter(_CppFileWriterBase):
"""C++ .cpp File writer."""
@@ -2314,7 +2396,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
def generate(self, spec, header_file_name):
# type: (ast.IDLAST, str) -> None
- """Generate the C++ header to a stream."""
+ """Generate the C++ source to a stream."""
# pylint: disable=too-many-statements
diff --git a/buildscripts/idl/idl/syntax.py b/buildscripts/idl/idl/syntax.py
index a249bf235b7..fcacc571139 100644
--- a/buildscripts/idl/idl/syntax.py
+++ b/buildscripts/idl/idl/syntax.py
@@ -475,7 +475,6 @@ class FieldListEntry(common.SourceLocation):
self.name = None # type: str
self.forward_to_shards = False # type: bool
self.forward_from_shards = False # type: bool
- self.command_name = None # type: str
super(FieldListEntry, self).__init__(file_name, line, column)
diff --git a/src/mongo/db/commands/generic.cpp b/src/mongo/db/commands/generic.cpp
index 4c3bc4ca914..52ed0d5c556 100644
--- a/src/mongo/db/commands/generic.cpp
+++ b/src/mongo/db/commands/generic.cpp
@@ -52,27 +52,6 @@ using std::string;
using std::stringstream;
using std::vector;
-// TODO (SERVER-51862): generate PingCmdVersion1Gen from IDL.
-template <typename Derived>
-class PingCmdVersion1Gen : public TypedCommand<Derived> {
- using _TypedCommandInvocationBase = typename TypedCommand<Derived>::InvocationBase;
-
-public:
- using Request = Ping;
- using Reply = OkReply;
- virtual const std::set<std::string>& apiVersions() const override {
- return kApiVersions1;
- }
- virtual const std::set<std::string>& deprecatedApiVersions() const override {
- return kNoApiVersions;
- }
- class InvocationBaseGen : public _TypedCommandInvocationBase {
- public:
- using _TypedCommandInvocationBase::_TypedCommandInvocationBase;
- virtual Reply typedRun(OperationContext* opCtx) = 0;
- };
-};
-
class PingCommand : public PingCmdVersion1Gen<PingCommand> {
public:
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {