@@ -183,11 +183,24 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
183
183
indent, klass.documentationComments, _docCommentSpec,
184
184
generatorComments: generatedMessages);
185
185
186
+ final Iterable <NamedType > orderedFields =
187
+ getFieldsInSerializationOrder (klass);
188
+
186
189
indent.write ('class ${klass .name } ' );
187
190
indent.addScoped ('{' , '};' , () {
188
191
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) {
191
204
addDocumentationComments (
192
205
indent, field.documentationComments, _docCommentSpec);
193
206
final HostDatatype baseDatatype = getFieldHostDatatype (
@@ -209,7 +222,8 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
209
222
});
210
223
211
224
indent.addScoped (' private:' , '' , () {
212
- indent.writeln ('${klass .name }(const flutter::EncodableList& list);' );
225
+ indent.writeln (
226
+ 'static ${klass .name } FromEncodableList(const flutter::EncodableList& list);' );
213
227
indent.writeln ('flutter::EncodableList ToEncodableList() const;' );
214
228
for (final Class friend in root.classes) {
215
229
if (friend != klass &&
@@ -228,7 +242,7 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
228
242
indent.writeln ('friend class $testFixtureClass ;' );
229
243
}
230
244
231
- for (final NamedType field in getFieldsInSerializationOrder (klass) ) {
245
+ for (final NamedType field in orderedFields ) {
232
246
final HostDatatype hostDatatype = getFieldHostDatatype (
233
247
field, root.classes, root.enums, _baseCppTypeForBuiltinDartType);
234
248
indent.writeln (
@@ -366,6 +380,23 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
366
380
}, nestCount: 0 );
367
381
}
368
382
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
+
369
400
void _writeCodec (
370
401
CppOptions generatorOptions, Root root, Indent indent, Api api) {
371
402
assert (getCodecClasses (api, root).isNotEmpty);
@@ -423,12 +454,10 @@ class FlutterError {
423
454
424
455
template<class T> class ErrorOr {
425
456
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)) {}
432
461
433
462
\t bool has_error() const { return std::holds_alternative<FlutterError>(v_); }
434
463
\t const T& value() const { return std::get<T>(v_); };
@@ -528,19 +557,26 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
528
557
indent.writeln ('$_commentPrefix ${klass .name }' );
529
558
indent.newln ();
530
559
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
+
531
571
// Getters and setters.
532
- for (final NamedType field in getFieldsInSerializationOrder (klass) ) {
572
+ for (final NamedType field in orderedFields ) {
533
573
_writeCppSourceClassField (generatorOptions, root, indent, klass, field);
534
574
}
535
575
536
576
// Serialization.
537
577
writeClassEncode (generatorOptions, root, indent, klass, customClassNames,
538
578
customEnumNames);
539
579
540
- // Default constructor.
541
- indent.writeln ('${klass .name }::${klass .name }() {}' );
542
- indent.newln ();
543
-
544
580
// Deserialization.
545
581
writeClassDecode (generatorOptions, root, indent, klass, customClassNames,
546
582
customEnumNames);
@@ -581,47 +617,70 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
581
617
Set <String > customClassNames,
582
618
Set <String > customEnumNames,
583
619
) {
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) ' );
585
643
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);
591
669
final String encodableFieldName =
592
670
'${_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 ;' );
625
684
});
626
685
}
627
686
@@ -868,7 +927,7 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
868
927
indent.writeln ('case ${customClass .enumeration }:' );
869
928
indent.nest (1 , () {
870
929
indent.writeln (
871
- 'return CustomEncodableValue(${customClass .name }(std::get<EncodableList>(ReadValue(stream))));' );
930
+ 'return CustomEncodableValue(${customClass .name }::FromEncodableList (std::get<EncodableList>(ReadValue(stream))));' );
872
931
});
873
932
}
874
933
indent.writeln ('default:' );
@@ -901,6 +960,42 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
901
960
indent.newln ();
902
961
}
903
962
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
+
904
999
void _writeCppSourceClassField (CppOptions generatorOptions, Root root,
905
1000
Indent indent, Class klass, NamedType field) {
906
1001
final HostDatatype hostDatatype = getFieldHostDatatype (
@@ -918,11 +1013,8 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
918
1013
// generating multiple setter variants.
919
1014
String makeSetter (HostDatatype type) {
920
1015
const String setterArgumentName = 'value_arg' ;
921
- final String valueExpression = type.isNullable
922
- ? '$setterArgumentName ? ${_valueType (type )}(*$setterArgumentName ) : std::nullopt'
923
- : setterArgumentName;
924
1016
return 'void $qualifiedSetterName (${_unownedArgumentType (type )} $setterArgumentName ) '
925
- '{ $instanceVariableName = $valueExpression ; }' ;
1017
+ '{ $instanceVariableName = ${ _fieldValueExpression ( type , setterArgumentName )} ; }' ;
926
1018
}
927
1019
928
1020
indent.writeln (
@@ -938,6 +1030,18 @@ EncodableValue ${api.name}::WrapError(const FlutterError& error) {
938
1030
indent.newln ();
939
1031
}
940
1032
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
+
941
1045
String _wrapResponse (Indent indent, Root root, TypeDeclaration returnType,
942
1046
{String prefix = '' }) {
943
1047
final String nonErrorPath;
@@ -1109,9 +1213,15 @@ class _HostNamedType {
1109
1213
final TypeDeclaration originalType;
1110
1214
}
1111
1215
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
+
1112
1223
String _getCodecSerializerName (Api api) => '${api .name }CodecSerializer' ;
1113
1224
1114
- const String _pointerPrefix = 'pointer' ;
1115
1225
const String _encodablePrefix = 'encodable' ;
1116
1226
1117
1227
String _getArgumentName (int count, NamedType argument) =>
0 commit comments