@@ -255,6 +255,35 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
255255 _writeClassConstructor (root, indent, classDefinition, orderedFields,
256256 'Constructs an object setting all fields.' );
257257
258+ // If any fields are pointer type, then the class requires a custom
259+ // copy constructor, so declare the rule-of-five group of functions.
260+ if (orderedFields.any ((NamedType field) => _isPointerField (
261+ getFieldHostDatatype (field, _baseCppTypeForBuiltinDartType)))) {
262+ final String className = classDefinition.name;
263+ // Add the default destructor, since unique_ptr destroys itself.
264+ _writeFunctionDeclaration (indent, '~$className ' , defaultImpl: true );
265+ // Declare custom copy/assign to deep-copy the pointer.
266+ _writeFunctionDeclaration (indent, className,
267+ isConstructor: true ,
268+ isCopy: true ,
269+ parameters: < String > ['const $className & other' ]);
270+ _writeFunctionDeclaration (indent, 'operator=' ,
271+ returnType: '$className &' ,
272+ parameters: < String > ['const $className & other' ]);
273+ // Re-add the default move operations, since they work fine with
274+ // unique_ptr.
275+ _writeFunctionDeclaration (indent, className,
276+ isConstructor: true ,
277+ isCopy: true ,
278+ parameters: < String > ['$className && other' ],
279+ defaultImpl: true );
280+ _writeFunctionDeclaration (indent, 'operator=' ,
281+ returnType: '$className &' ,
282+ parameters: < String > ['$className && other' ],
283+ defaultImpl: true ,
284+ noexcept: true );
285+ }
286+
258287 for (final NamedType field in orderedFields) {
259288 addDocumentationComments (
260289 indent, field.documentationComments, _docCommentSpec);
@@ -313,7 +342,7 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
313342 final HostDatatype hostDatatype =
314343 getFieldHostDatatype (field, _baseCppTypeForBuiltinDartType);
315344 indent.writeln (
316- '${_valueType (hostDatatype )} ${_makeInstanceVariableName (field )};' );
345+ '${_fieldType (hostDatatype )} ${_makeInstanceVariableName (field )};' );
317346 }
318347 });
319348 }, nestCount: 0 );
@@ -693,6 +722,13 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
693722 // All-field constructor.
694723 _writeClassConstructor (root, indent, classDefinition, orderedFields);
695724
725+ // Custom copy/assign to handle pointer fields, if necessary.
726+ if (orderedFields.any ((NamedType field) => _isPointerField (
727+ getFieldHostDatatype (field, _baseCppTypeForBuiltinDartType)))) {
728+ _writeCopyConstructor (root, indent, classDefinition, orderedFields);
729+ _writeAssignmentOperator (root, indent, classDefinition, orderedFields);
730+ }
731+
696732 // Getters and setters.
697733 for (final NamedType field in orderedFields) {
698734 _writeCppSourceClassField (
@@ -1169,15 +1205,69 @@ return EncodableValue(EncodableList{
11691205 initializers: initializerStrings);
11701206 }
11711207
1208+ void _writeCopyConstructor (Root root, Indent indent, Class classDefinition,
1209+ Iterable <NamedType > fields) {
1210+ final List <String > initializerStrings = fields.map ((NamedType param) {
1211+ final String fieldName = _makeInstanceVariableName (param);
1212+ final HostDatatype hostType = getFieldHostDatatype (
1213+ param,
1214+ _shortBaseCppTypeForBuiltinDartType,
1215+ );
1216+ return '$fieldName (${_fieldValueExpression (hostType , 'other.$fieldName ' , sourceIsField : true )})' ;
1217+ }).toList ();
1218+ _writeFunctionDefinition (indent, classDefinition.name,
1219+ scope: classDefinition.name,
1220+ parameters: < String > ['const ${classDefinition .name }& other' ],
1221+ initializers: initializerStrings);
1222+ }
1223+
1224+ void _writeAssignmentOperator (Root root, Indent indent, Class classDefinition,
1225+ Iterable <NamedType > fields) {
1226+ _writeFunctionDefinition (indent, 'operator=' ,
1227+ scope: classDefinition.name,
1228+ returnType: '${classDefinition .name }&' ,
1229+ parameters: < String > ['const ${classDefinition .name }& other' ], body: () {
1230+ for (final NamedType field in fields) {
1231+ final HostDatatype hostDatatype =
1232+ getFieldHostDatatype (field, _shortBaseCppTypeForBuiltinDartType);
1233+
1234+ final String ivarName = _makeInstanceVariableName (field);
1235+ final String otherIvar = 'other.$ivarName ' ;
1236+ final String valueExpression;
1237+ if (_isPointerField (hostDatatype)) {
1238+ final String constructor =
1239+ 'std::make_unique<${hostDatatype .datatype }>(*$otherIvar )' ;
1240+ valueExpression = hostDatatype.isNullable
1241+ ? '$otherIvar ? $constructor : nullptr'
1242+ : constructor;
1243+ } else {
1244+ valueExpression = otherIvar;
1245+ }
1246+ indent.writeln ('$ivarName = $valueExpression ;' );
1247+ }
1248+ indent.writeln ('return *this;' );
1249+ });
1250+ }
1251+
11721252 void _writeCppSourceClassField (CppOptions generatorOptions, Root root,
11731253 Indent indent, Class classDefinition, NamedType field) {
11741254 final HostDatatype hostDatatype =
11751255 getFieldHostDatatype (field, _shortBaseCppTypeForBuiltinDartType);
11761256 final String instanceVariableName = _makeInstanceVariableName (field);
11771257 final String setterName = _makeSetterName (field);
1178- final String returnExpression = hostDatatype.isNullable
1179- ? '$instanceVariableName ? &(*$instanceVariableName ) : nullptr'
1180- : instanceVariableName;
1258+ final String returnExpression;
1259+ if (_isPointerField (hostDatatype)) {
1260+ // Convert std::unique_ptr<T> to either T* or const T&.
1261+ returnExpression = hostDatatype.isNullable
1262+ ? '$instanceVariableName .get()'
1263+ : '*$instanceVariableName ' ;
1264+ } else if (hostDatatype.isNullable) {
1265+ // Convert std::optional<T> to T*.
1266+ returnExpression =
1267+ '$instanceVariableName ? &(*$instanceVariableName ) : nullptr' ;
1268+ } else {
1269+ returnExpression = instanceVariableName;
1270+ }
11811271
11821272 // Writes a setter treating the type as [type], to allow generating multiple
11831273 // setter variants.
@@ -1220,10 +1310,20 @@ return EncodableValue(EncodableList{
12201310 /// Returns the value to use when setting a field of the given type from
12211311 /// an argument of that type.
12221312 ///
1223- /// For non-nullable values this is just the variable itself, but for nullable
1224- /// values this handles the conversion between an argument type (a pointer)
1225- /// and the field type (a std::optional).
1226- String _fieldValueExpression (HostDatatype type, String variable) {
1313+ /// For non-nullable and non-custom-class values this is just the variable
1314+ /// itself, but for other values this handles the conversion between an
1315+ /// argument type (a pointer or value/reference) and the field type
1316+ /// (a std::optional or std::unique_ptr).
1317+ String _fieldValueExpression (HostDatatype type, String variable,
1318+ {bool sourceIsField = false }) {
1319+ if (_isPointerField (type)) {
1320+ final String constructor = 'std::make_unique<${type .datatype }>' ;
1321+ // If the source is a pointer field, it always needs dereferencing.
1322+ final String maybeDereference = sourceIsField ? '*' : '' ;
1323+ return type.isNullable
1324+ ? '$variable ? $constructor (*$variable ) : nullptr'
1325+ : '$constructor ($maybeDereference $variable )' ;
1326+ }
12271327 return type.isNullable
12281328 ? '$variable ? ${_valueType (type )}(*$variable ) : std::nullopt'
12291329 : variable;
@@ -1309,7 +1409,8 @@ ${prefix}reply(EncodableValue(std::move(wrapped)));''';
13091409 if (! hostType.isBuiltin &&
13101410 root.classes.any ((Class c) => c.name == dartType.baseName)) {
13111411 if (preSerializeClasses) {
1312- final String operator = hostType.isNullable ? '->' : '.' ;
1412+ final String operator =
1413+ hostType.isNullable || _isPointerField (hostType) ? '->' : '.' ;
13131414 encodableValue =
13141415 'EncodableValue($variableName ${operator }ToEncodableList())' ;
13151416 } else {
@@ -1547,6 +1648,23 @@ String _valueType(HostDatatype type) {
15471648 return type.isNullable ? 'std::optional<$baseType >' : baseType;
15481649}
15491650
1651+ /// Returns the C++ type to use when declaring a data class field for the
1652+ /// given type.
1653+ String _fieldType (HostDatatype type) {
1654+ return _isPointerField (type)
1655+ ? 'std::unique_ptr<${type .datatype }>'
1656+ : _valueType (type);
1657+ }
1658+
1659+ /// Returns true if [type] should be stored as a pointer, rather than a
1660+ /// value type, in a data class.
1661+ bool _isPointerField (HostDatatype type) {
1662+ // Custom class types are stored as `unique_ptr`s since they can have
1663+ // arbitrary size, and can also be arbitrarily (including recursively)
1664+ // nested, so must be stored as pointers.
1665+ return ! type.isBuiltin && ! type.isEnum;
1666+ }
1667+
15501668/// Returns the C++ type to use in an argument context without ownership
15511669/// transfer for the given base type.
15521670String _unownedArgumentType (HostDatatype type) {
@@ -1723,17 +1841,21 @@ void _writeFunctionDeclaration(
17231841 bool isStatic = false ,
17241842 bool isVirtual = false ,
17251843 bool isConstructor = false ,
1844+ bool isCopy = false ,
17261845 bool isPureVirtual = false ,
17271846 bool isConst = false ,
17281847 bool isOverride = false ,
17291848 bool deleted = false ,
1849+ bool defaultImpl = false ,
17301850 bool inlineNoop = false ,
1851+ bool noexcept = false ,
17311852 void Function ()? inlineBody,
17321853}) {
17331854 assert (! (isVirtual && isOverride), 'virtual is redundant with override' );
17341855 assert (isVirtual || ! isPureVirtual, 'pure virtual methods must be virtual' );
17351856 assert (returnType == null || ! isConstructor,
17361857 'constructors cannot have return types' );
1858+ assert (! (deleted && defaultImpl), 'a function cannot be deleted and default' );
17371859 _writeFunction (
17381860 indent,
17391861 inlineNoop || (inlineBody != null )
@@ -1746,12 +1868,14 @@ void _writeFunctionDeclaration(
17461868 if (inlineBody != null ) 'inline' ,
17471869 if (isStatic) 'static' ,
17481870 if (isVirtual) 'virtual' ,
1749- if (isConstructor && parameters.isNotEmpty) 'explicit'
1871+ if (isConstructor && parameters.isNotEmpty && ! isCopy ) 'explicit'
17501872 ],
17511873 trailingAnnotations: < String > [
17521874 if (isConst) 'const' ,
1875+ if (noexcept) 'noexcept' ,
17531876 if (isOverride) 'override' ,
17541877 if (deleted) '= delete' ,
1878+ if (defaultImpl) '= default' ,
17551879 if (isPureVirtual) '= 0' ,
17561880 ],
17571881 body: inlineBody,
0 commit comments