@@ -183,11 +183,24 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
183183 indent, klass.documentationComments, _docCommentSpec,
184184 generatorComments: generatedMessages);
185185
186+ final Iterable <NamedType > orderedFields =
187+ getFieldsInSerializationOrder (klass);
188+
186189 indent.write ('class ${klass .name } ' );
187190 indent.addScoped ('{' , '};' , () {
188191 indent.addScoped (' public:' , '' , () {
189- indent.writeln ('${klass .name }();' );
190- for (final NamedType field in getFieldsInSerializationOrder (klass)) {
192+ final Iterable <NamedType > requiredFields =
193+ orderedFields.where ((NamedType type) => ! type.type.isNullable);
194+ // Minimal constructor, if needed.
195+ if (requiredFields.length != orderedFields.length) {
196+ _writeClassConstructor (root, indent, klass, requiredFields,
197+ 'Constructs an object setting all non-nullable fields.' );
198+ }
199+ // All-field constructor.
200+ _writeClassConstructor (root, indent, klass, orderedFields,
201+ 'Constructs an object setting all fields.' );
202+
203+ for (final NamedType field in orderedFields) {
191204 addDocumentationComments (
192205 indent, field.documentationComments, _docCommentSpec);
193206 final HostDatatype baseDatatype = getFieldHostDatatype (
@@ -209,7 +222,8 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
209222 });
210223
211224 indent.addScoped (' private:' , '' , () {
212- indent.writeln ('${klass .name }(const flutter::EncodableList& list);' );
225+ indent.writeln (
226+ 'static ${klass .name } FromEncodableList(const flutter::EncodableList& list);' );
213227 indent.writeln ('flutter::EncodableList ToEncodableList() const;' );
214228 for (final Class friend in root.classes) {
215229 if (friend != klass &&
@@ -228,7 +242,7 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
228242 indent.writeln ('friend class $testFixtureClass ;' );
229243 }
230244
231- for (final NamedType field in getFieldsInSerializationOrder (klass) ) {
245+ for (final NamedType field in orderedFields ) {
232246 final HostDatatype hostDatatype = getFieldHostDatatype (
233247 field, root.classes, root.enums, _baseCppTypeForBuiltinDartType);
234248 indent.writeln (
@@ -366,6 +380,23 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
366380 }, nestCount: 0 );
367381 }
368382
383+ void _writeClassConstructor (Root root, Indent indent, Class klass,
384+ Iterable <NamedType > params, String docComment) {
385+ final String explicit = params.isEmpty ? '' : 'explicit ' ;
386+ String paramString = params.map ((NamedType param) {
387+ final HostDatatype hostDatatype = getFieldHostDatatype (
388+ param, root.classes, root.enums, _baseCppTypeForBuiltinDartType);
389+ return '\t ${_hostApiArgumentType (hostDatatype )} ${_makeVariableName (param )}' ;
390+ }).join (',\n ' );
391+ if (paramString.isNotEmpty) {
392+ paramString = '\n $paramString ' ;
393+ }
394+ indent.format ('''
395+ $_commentPrefix $docComment
396+ $explicit ${klass .name }($paramString );
397+ ''' );
398+ }
399+
369400 void _writeCodec (
370401 CppOptions generatorOptions, Root root, Indent indent, Api api) {
371402 assert (getCodecClasses (api, root).isNotEmpty);
@@ -423,12 +454,10 @@ class FlutterError {
423454
424455template<class T> class ErrorOr {
425456 public:
426- \t ErrorOr(const T& rhs) { new(&v_) T(rhs); }
427- \t ErrorOr(const T&& rhs) { v_ = std::move(rhs); }
428- \t ErrorOr(const FlutterError& rhs) {
429- \t\t new(&v_) FlutterError(rhs);
430- \t }
431- \t ErrorOr(const FlutterError&& rhs) { v_ = std::move(rhs); }
457+ \t ErrorOr(const T& rhs) : v_(rhs) {}
458+ \t ErrorOr(const T&& rhs) : v_(std::move(rhs)) {}
459+ \t ErrorOr(const FlutterError& rhs) : v_(rhs) {}
460+ \t ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {}
432461
433462\t bool has_error() const { return std::holds_alternative<FlutterError>(v_); }
434463\t const T& value() const { return std::get<T>(v_); };
@@ -528,19 +557,26 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
528557 indent.writeln ('$_commentPrefix ${klass .name }' );
529558 indent.newln ();
530559
560+ final Iterable <NamedType > orderedFields =
561+ getFieldsInSerializationOrder (klass);
562+ final Iterable <NamedType > requiredFields =
563+ orderedFields.where ((NamedType type) => ! type.type.isNullable);
564+ // Minimal constructor, if needed.
565+ if (requiredFields.length != orderedFields.length) {
566+ _writeClassConstructor (root, indent, klass, requiredFields);
567+ }
568+ // All-field constructor.
569+ _writeClassConstructor (root, indent, klass, orderedFields);
570+
531571 // Getters and setters.
532- for (final NamedType field in getFieldsInSerializationOrder (klass) ) {
572+ for (final NamedType field in orderedFields ) {
533573 _writeCppSourceClassField (generatorOptions, root, indent, klass, field);
534574 }
535575
536576 // Serialization.
537577 writeClassEncode (generatorOptions, root, indent, klass, customClassNames,
538578 customEnumNames);
539579
540- // Default constructor.
541- indent.writeln ('${klass .name }::${klass .name }() {}' );
542- indent.newln ();
543-
544580 // Deserialization.
545581 writeClassDecode (generatorOptions, root, indent, klass, customClassNames,
546582 customEnumNames);
@@ -581,47 +617,70 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
581617 Set <String > customClassNames,
582618 Set <String > customEnumNames,
583619 ) {
584- indent.write ('${klass .name }::${klass .name }(const EncodableList& list) ' );
620+ // Returns the expression to convert the given EncodableValue to a field
621+ // value.
622+ String getValueExpression (NamedType field, String encodable) {
623+ if (customEnumNames.contains (field.type.baseName)) {
624+ return '(${field .type .baseName })(std::get<int32_t>($encodable ))' ;
625+ } else if (field.type.baseName == 'int' ) {
626+ return '$encodable .LongValue()' ;
627+ } else {
628+ final HostDatatype hostDatatype = getFieldHostDatatype (field,
629+ root.classes, root.enums, _shortBaseCppTypeForBuiltinDartType);
630+ if (! hostDatatype.isBuiltin &&
631+ root.classes
632+ .map ((Class x) => x.name)
633+ .contains (field.type.baseName)) {
634+ return '${hostDatatype .datatype }::FromEncodableList(std::get<EncodableList>($encodable ))' ;
635+ } else {
636+ return 'std::get<${hostDatatype .datatype }>($encodable )' ;
637+ }
638+ }
639+ }
640+
641+ indent.write (
642+ '${klass .name } ${klass .name }::FromEncodableList(const EncodableList& list) ' );
585643 indent.addScoped ('{' , '}' , () {
586- enumerate (getFieldsInSerializationOrder (klass),
587- (int index, final NamedType field) {
588- final String instanceVariableName = _makeInstanceVariableName (field);
589- final String pointerFieldName =
590- '${_pointerPrefix }_${_makeVariableName (field )}' ;
644+ const String instanceVariable = 'decoded' ;
645+ final Iterable <_IndexedField > indexedFields = indexMap (
646+ getFieldsInSerializationOrder (klass),
647+ (int index, NamedType field) => _IndexedField (index, field));
648+ final Iterable <_IndexedField > nullableFields = indexedFields
649+ .where ((_IndexedField field) => field.field.type.isNullable);
650+ final Iterable <_IndexedField > nonNullableFields = indexedFields
651+ .where ((_IndexedField field) => ! field.field.type.isNullable);
652+
653+ // Non-nullable fields must be set via the constructor.
654+ String constructorArgs = nonNullableFields
655+ .map ((_IndexedField param) =>
656+ getValueExpression (param.field, 'list[${param .index }]' ))
657+ .join (',\n\t ' );
658+ if (constructorArgs.isNotEmpty) {
659+ constructorArgs = '(\n\t $constructorArgs )' ;
660+ }
661+ indent.format ('${klass .name } $instanceVariable $constructorArgs ;' );
662+
663+ // Add the nullable fields via setters, since converting the encodable
664+ // values to the pointer types that the convenience constructor uses for
665+ // nullable fields is non-trivial.
666+ for (final _IndexedField entry in nullableFields) {
667+ final NamedType field = entry.field;
668+ final String setterName = _makeSetterName (field);
591669 final String encodableFieldName =
592670 '${_encodablePrefix }_${_makeVariableName (field )}' ;
593- indent.writeln ('auto& $encodableFieldName = list[$index ];' );
594- if (customEnumNames.contains (field.type.baseName)) {
595- indent.writeln (
596- 'if (const int32_t* $pointerFieldName = std::get_if<int32_t>(&$encodableFieldName ))\t $instanceVariableName = (${field .type .baseName })*$pointerFieldName ;' );
597- } else {
598- final HostDatatype hostDatatype = getFieldHostDatatype (field,
599- root.classes, root.enums, _shortBaseCppTypeForBuiltinDartType);
600- if (field.type.baseName == 'int' ) {
601- indent.format ('''
602- if (const int32_t* $pointerFieldName = std::get_if<int32_t>(&$encodableFieldName ))
603- \t $instanceVariableName = *$pointerFieldName ;
604- else if (const int64_t* ${pointerFieldName }_64 = std::get_if<int64_t>(&$encodableFieldName ))
605- \t $instanceVariableName = *${pointerFieldName }_64;''' );
606- } else if (! hostDatatype.isBuiltin &&
607- root.classes
608- .map ((Class x) => x.name)
609- .contains (field.type.baseName)) {
610- indent.write (
611- 'if (const EncodableList* $pointerFieldName = std::get_if<EncodableList>(&$encodableFieldName )) ' );
612- indent.addScoped ('{' , '}' , () {
613- indent.writeln (
614- '$instanceVariableName = ${hostDatatype .datatype }(*$pointerFieldName );' );
615- });
616- } else {
617- indent.write (
618- 'if (const ${hostDatatype .datatype }* $pointerFieldName = std::get_if<${hostDatatype .datatype }>(&$encodableFieldName )) ' );
619- indent.addScoped ('{' , '}' , () {
620- indent.writeln ('$instanceVariableName = *$pointerFieldName ;' );
621- });
622- }
623- }
624- });
671+ indent.writeln ('auto& $encodableFieldName = list[${entry .index }];' );
672+
673+ final String valueExpression =
674+ getValueExpression (field, encodableFieldName);
675+ indent.writeScoped ('if (!$encodableFieldName .IsNull()) {' , '}' , () {
676+ indent.writeln ('$instanceVariable .$setterName ($valueExpression );' );
677+ });
678+ }
679+
680+ // This returns by value, relying on copy elision, since it makes the
681+ // usage more convenient during deserialization than it would be with
682+ // explicit transfer via unique_ptr.
683+ indent.writeln ('return $instanceVariable ;' );
625684 });
626685 }
627686
@@ -868,7 +927,7 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
868927 indent.writeln ('case ${customClass .enumeration }:' );
869928 indent.nest (1 , () {
870929 indent.writeln (
871- 'return CustomEncodableValue(${customClass .name }(std::get<EncodableList>(ReadValue(stream))));' );
930+ 'return CustomEncodableValue(${customClass .name }::FromEncodableList (std::get<EncodableList>(ReadValue(stream))));' );
872931 });
873932 }
874933 indent.writeln ('default:' );
@@ -901,6 +960,42 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
901960 indent.newln ();
902961 }
903962
963+ void _writeClassConstructor (
964+ Root root, Indent indent, Class klass, Iterable <NamedType > params) {
965+ final Iterable <_HostNamedType > hostParams = params.map ((NamedType param) {
966+ return _HostNamedType (
967+ _makeVariableName (param),
968+ getFieldHostDatatype (
969+ param,
970+ root.classes,
971+ root.enums,
972+ _shortBaseCppTypeForBuiltinDartType,
973+ ),
974+ param.type,
975+ );
976+ });
977+
978+ String paramString = hostParams
979+ .map ((_HostNamedType param) =>
980+ '\t ${_hostApiArgumentType (param .hostType )} ${param .name }' )
981+ .join (',\n ' );
982+ if (paramString.isNotEmpty) {
983+ paramString = '\n $paramString \n ' ;
984+ }
985+
986+ String initializerString = hostParams
987+ .map ((_HostNamedType param) =>
988+ '${param .name }_(${_fieldValueExpression (param .hostType , param .name )})' )
989+ .join (',\n\t\t ' );
990+ if (initializerString.isNotEmpty) {
991+ initializerString = ' : $initializerString ' ;
992+ }
993+
994+ indent.format ('''
995+ ${klass .name }::${klass .name }($paramString )$initializerString {}
996+ ''' );
997+ }
998+
904999 void _writeCppSourceClassField (CppOptions generatorOptions, Root root,
9051000 Indent indent, Class klass, NamedType field) {
9061001 final HostDatatype hostDatatype = getFieldHostDatatype (
@@ -918,11 +1013,8 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
9181013 // generating multiple setter variants.
9191014 String makeSetter (HostDatatype type) {
9201015 const String setterArgumentName = 'value_arg' ;
921- final String valueExpression = type.isNullable
922- ? '$setterArgumentName ? ${_valueType (type )}(*$setterArgumentName ) : std::nullopt'
923- : setterArgumentName;
9241016 return 'void $qualifiedSetterName (${_unownedArgumentType (type )} $setterArgumentName ) '
925- '{ $instanceVariableName = $valueExpression ; }' ;
1017+ '{ $instanceVariableName = ${ _fieldValueExpression ( type , setterArgumentName )} ; }' ;
9261018 }
9271019
9281020 indent.writeln (
@@ -938,6 +1030,18 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
9381030 indent.newln ();
9391031 }
9401032
1033+ /// Returns the value to use when setting a field of the given type from
1034+ /// an argument of that type.
1035+ ///
1036+ /// For non-nullable values this is just the variable itself, but for nullable
1037+ /// values this handles the conversion between an argument type (a pointer)
1038+ /// and the field type (a std::optional).
1039+ String _fieldValueExpression (HostDatatype type, String variable) {
1040+ return type.isNullable
1041+ ? '$variable ? ${_valueType (type )}(*$variable ) : std::nullopt'
1042+ : variable;
1043+ }
1044+
9411045 String _wrapResponse (Indent indent, Root root, TypeDeclaration returnType,
9421046 {String prefix = '' }) {
9431047 final String nonErrorPath;
@@ -1109,9 +1213,15 @@ class _HostNamedType {
11091213 final TypeDeclaration originalType;
11101214}
11111215
1216+ /// Contains a class field and its serialization index.
1217+ class _IndexedField {
1218+ const _IndexedField (this .index, this .field);
1219+ final int index;
1220+ final NamedType field;
1221+ }
1222+
11121223String _getCodecSerializerName (Api api) => '${api .name }CodecSerializer' ;
11131224
1114- const String _pointerPrefix = 'pointer' ;
11151225const String _encodablePrefix = 'encodable' ;
11161226
11171227String _getArgumentName (int count, NamedType argument) =>
0 commit comments