diff options
author | Samy Lanka <samy.lanka@mongodb.com> | 2020-11-20 01:05:41 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-11-23 22:56:55 +0000 |
commit | a2c49f836faedb6ef9e6d43459f6d6949525e0b1 (patch) | |
tree | 99649396eae188385786d268e1011c848d8392a6 | |
parent | 7654bb8401386c2ae2a6e482e973227ce6c0e98b (diff) | |
download | mongo-a2c49f836faedb6ef9e6d43459f6d6949525e0b1.tar.gz |
SERVER-51862 Generate a C++ base class for each V1 command
-rw-r--r-- | buildscripts/idl/idl/ast.py | 3 | ||||
-rw-r--r-- | buildscripts/idl/idl/binder.py | 4 | ||||
-rw-r--r-- | buildscripts/idl/idl/generator.py | 90 | ||||
-rw-r--r-- | buildscripts/idl/idl/syntax.py | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/generic.cpp | 21 |
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 { |