@@ -255,6 +255,35 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
255
255
_writeClassConstructor (root, indent, classDefinition, orderedFields,
256
256
'Constructs an object setting all fields.' );
257
257
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
+
258
287
for (final NamedType field in orderedFields) {
259
288
addDocumentationComments (
260
289
indent, field.documentationComments, _docCommentSpec);
@@ -313,7 +342,7 @@ class CppHeaderGenerator extends StructuredGenerator<CppOptions> {
313
342
final HostDatatype hostDatatype =
314
343
getFieldHostDatatype (field, _baseCppTypeForBuiltinDartType);
315
344
indent.writeln (
316
- '${_valueType (hostDatatype )} ${_makeInstanceVariableName (field )};' );
345
+ '${_fieldType (hostDatatype )} ${_makeInstanceVariableName (field )};' );
317
346
}
318
347
});
319
348
}, nestCount: 0 );
@@ -693,6 +722,13 @@ class CppSourceGenerator extends StructuredGenerator<CppOptions> {
693
722
// All-field constructor.
694
723
_writeClassConstructor (root, indent, classDefinition, orderedFields);
695
724
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
+
696
732
// Getters and setters.
697
733
for (final NamedType field in orderedFields) {
698
734
_writeCppSourceClassField (
@@ -1169,15 +1205,69 @@ return EncodableValue(EncodableList{
1169
1205
initializers: initializerStrings);
1170
1206
}
1171
1207
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
+
1172
1252
void _writeCppSourceClassField (CppOptions generatorOptions, Root root,
1173
1253
Indent indent, Class classDefinition, NamedType field) {
1174
1254
final HostDatatype hostDatatype =
1175
1255
getFieldHostDatatype (field, _shortBaseCppTypeForBuiltinDartType);
1176
1256
final String instanceVariableName = _makeInstanceVariableName (field);
1177
1257
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
+ }
1181
1271
1182
1272
// Writes a setter treating the type as [type], to allow generating multiple
1183
1273
// setter variants.
@@ -1220,10 +1310,20 @@ return EncodableValue(EncodableList{
1220
1310
/// Returns the value to use when setting a field of the given type from
1221
1311
/// an argument of that type.
1222
1312
///
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
+ }
1227
1327
return type.isNullable
1228
1328
? '$variable ? ${_valueType (type )}(*$variable ) : std::nullopt'
1229
1329
: variable;
@@ -1309,7 +1409,8 @@ ${prefix}reply(EncodableValue(std::move(wrapped)));''';
1309
1409
if (! hostType.isBuiltin &&
1310
1410
root.classes.any ((Class c) => c.name == dartType.baseName)) {
1311
1411
if (preSerializeClasses) {
1312
- final String operator = hostType.isNullable ? '->' : '.' ;
1412
+ final String operator =
1413
+ hostType.isNullable || _isPointerField (hostType) ? '->' : '.' ;
1313
1414
encodableValue =
1314
1415
'EncodableValue($variableName ${operator }ToEncodableList())' ;
1315
1416
} else {
@@ -1547,6 +1648,23 @@ String _valueType(HostDatatype type) {
1547
1648
return type.isNullable ? 'std::optional<$baseType >' : baseType;
1548
1649
}
1549
1650
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
+
1550
1668
/// Returns the C++ type to use in an argument context without ownership
1551
1669
/// transfer for the given base type.
1552
1670
String _unownedArgumentType (HostDatatype type) {
@@ -1723,17 +1841,21 @@ void _writeFunctionDeclaration(
1723
1841
bool isStatic = false ,
1724
1842
bool isVirtual = false ,
1725
1843
bool isConstructor = false ,
1844
+ bool isCopy = false ,
1726
1845
bool isPureVirtual = false ,
1727
1846
bool isConst = false ,
1728
1847
bool isOverride = false ,
1729
1848
bool deleted = false ,
1849
+ bool defaultImpl = false ,
1730
1850
bool inlineNoop = false ,
1851
+ bool noexcept = false ,
1731
1852
void Function ()? inlineBody,
1732
1853
}) {
1733
1854
assert (! (isVirtual && isOverride), 'virtual is redundant with override' );
1734
1855
assert (isVirtual || ! isPureVirtual, 'pure virtual methods must be virtual' );
1735
1856
assert (returnType == null || ! isConstructor,
1736
1857
'constructors cannot have return types' );
1858
+ assert (! (deleted && defaultImpl), 'a function cannot be deleted and default' );
1737
1859
_writeFunction (
1738
1860
indent,
1739
1861
inlineNoop || (inlineBody != null )
@@ -1746,12 +1868,14 @@ void _writeFunctionDeclaration(
1746
1868
if (inlineBody != null ) 'inline' ,
1747
1869
if (isStatic) 'static' ,
1748
1870
if (isVirtual) 'virtual' ,
1749
- if (isConstructor && parameters.isNotEmpty) 'explicit'
1871
+ if (isConstructor && parameters.isNotEmpty && ! isCopy ) 'explicit'
1750
1872
],
1751
1873
trailingAnnotations: < String > [
1752
1874
if (isConst) 'const' ,
1875
+ if (noexcept) 'noexcept' ,
1753
1876
if (isOverride) 'override' ,
1754
1877
if (deleted) '= delete' ,
1878
+ if (defaultImpl) '= default' ,
1755
1879
if (isPureVirtual) '= 0' ,
1756
1880
],
1757
1881
body: inlineBody,
0 commit comments