268
268
269
269
270
270
#define CPP2_TYPEOF (x ) std::remove_cvref_t <decltype(x)>
271
+ #if __cplusplus >= 202302L && \
272
+ ( \
273
+ (defined(__clang_major__) && __clang_major__ >= 15 ) \
274
+ || (defined(__GNUC__) && __GNUC__ >= 12 ) \
275
+ )
276
+ #define CPP2_COPY (x ) auto (x)
277
+ #else
278
+ #define CPP2_COPY (x ) CPP2_TYPEOF(x)(x)
279
+ #endif
271
280
#define CPP2_FORWARD (x ) std::forward<decltype(x)>(x)
272
281
#define CPP2_PACK_EMPTY (x ) (sizeof ...(x) == 0 )
273
282
#define CPP2_CONTINUE_BREAK (NAME ) goto CONTINUE_##NAME; CONTINUE_##NAME: continue ; goto BREAK_##NAME; BREAK_##NAME: break ;
@@ -340,6 +349,10 @@ struct aligned_storage {
340
349
alignas (Align) unsigned char data[Len];
341
350
};
342
351
352
+ template <typename T>
353
+ requires requires { *std::declval<T&>(); }
354
+ using deref_t = decltype(*std::declval<T&>());
355
+
343
356
344
357
// -----------------------------------------------------------------------
345
358
//
@@ -450,13 +463,13 @@ auto inline Testing = contract_group(
450
463
451
464
452
465
// Check for invalid dereference or indirection which would result in undefined behavior.
453
- //
466
+ //
454
467
// - Null pointer
455
468
// - std::unique_ptr that owns nothing
456
469
// - std::shared_ptr with no managed object
457
470
// - std::optional with no value
458
471
// - std::expected containing an unexpected value
459
- //
472
+ //
460
473
// Note: For naming simplicity we consider all the above cases to be "null" states so that
461
474
// we can write: `*assert_not_null(object)`.
462
475
//
@@ -1112,44 +1125,39 @@ constexpr auto is( T const& ) -> bool {
1112
1125
// Types
1113
1126
//
1114
1127
template < typename C, typename X >
1115
- auto is ( X const & ) -> bool {
1116
- return false ;
1117
- }
1118
-
1119
- template < typename C, typename X >
1120
- requires std::is_same_v<C, X>
1121
- auto is ( X const & ) -> bool {
1122
- return true ;
1123
- }
1124
-
1125
- template < typename C, typename X >
1126
- requires (std::is_base_of_v<C, X> && !std::is_same_v<C,X>)
1127
- auto is ( X const & ) -> bool {
1128
- return true ;
1129
- }
1130
-
1131
- template < typename C, typename X >
1132
- requires (
1133
- ( std::is_base_of_v<X, C> ||
1134
- ( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
1135
- ) && !std::is_same_v<C,X>)
1136
- auto is ( X const & x ) -> bool {
1137
- return Dynamic_cast<C const *>(&x) != nullptr ;
1138
- }
1139
-
1140
- template < typename C, typename X >
1141
- requires (
1142
- ( std::is_base_of_v<X, C> ||
1143
- ( std::is_polymorphic_v<C> && std::is_polymorphic_v<X>)
1144
- ) && !std::is_same_v<C,X>)
1145
- auto is ( X const * x ) -> bool {
1146
- return Dynamic_cast<C const *>(x) != nullptr ;
1147
- }
1148
-
1149
- template < typename C, typename X >
1150
- requires (requires (X x) { *x; X (); } && std::is_same_v<C, empty>)
1151
1128
auto is ( X const & x ) -> bool {
1152
- return x == X ();
1129
+ if constexpr (
1130
+ std::is_same_v<C, X>
1131
+ || std::is_base_of_v<C, X>
1132
+ )
1133
+ {
1134
+ return true ;
1135
+ }
1136
+ else if constexpr (
1137
+ std::is_base_of_v<X, C>
1138
+ || (
1139
+ std::is_polymorphic_v<C>
1140
+ && std::is_polymorphic_v<X>
1141
+ )
1142
+ )
1143
+ {
1144
+ if constexpr (std::is_pointer_v<X>) {
1145
+ return Dynamic_cast<C const *>(x) != nullptr ;
1146
+ }
1147
+ else {
1148
+ return Dynamic_cast<C const *>(&x) != nullptr ;
1149
+ }
1150
+ }
1151
+ else if constexpr (
1152
+ requires { *x; X (); }
1153
+ && std::is_same_v<C, empty>
1154
+ )
1155
+ {
1156
+ return x == X ();
1157
+ }
1158
+ else {
1159
+ return false ;
1160
+ }
1153
1161
}
1154
1162
1155
1163
@@ -1217,11 +1225,6 @@ inline constexpr bool is_castable_v =
1217
1225
// As
1218
1226
//
1219
1227
1220
- template < typename C >
1221
- auto as (auto const &) -> auto {
1222
- return nonesuch;
1223
- }
1224
-
1225
1228
template < typename C, auto x >
1226
1229
requires (std::is_arithmetic_v<C> && std::is_arithmetic_v<CPP2_TYPEOF(x)>)
1227
1230
inline constexpr auto as () -> auto
@@ -1233,108 +1236,83 @@ inline constexpr auto as() -> auto
1233
1236
}
1234
1237
}
1235
1238
1236
- template < typename C >
1237
- inline constexpr auto as (auto const & x) -> auto
1238
- requires (
1239
+ template < typename C, typename X >
1240
+ auto as (X const & x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT ) -> decltype( auto ) {
1241
+ if constexpr (
1239
1242
std::is_floating_point_v<C> &&
1240
1243
std::is_floating_point_v<CPP2_TYPEOF (x)> &&
1241
1244
sizeof (CPP2_TYPEOF (x)) > sizeof (C)
1242
1245
)
1243
- {
1244
- return nonesuch;
1245
- }
1246
-
1247
- // Signed/unsigned conversions to a not-smaller type are handled as a precondition,
1248
- // and trying to cast from a value that is in the half of the value space that isn't
1249
- // representable in the target type C is flagged as a Type safety contract violation
1250
- template < typename C >
1251
- inline constexpr auto as (auto const & x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto
1252
- requires (
1246
+ {
1247
+ return CPP2_COPY (nonesuch);
1248
+ }
1249
+ // Signed/unsigned conversions to a not-smaller type are handled as a precondition,
1250
+ // and trying to cast from a value that is in the half of the value space that isn't
1251
+ // representable in the target type C is flagged as a Type safety contract violation
1252
+ else if constexpr (
1253
1253
std::is_integral_v<C> &&
1254
1254
std::is_integral_v<CPP2_TYPEOF (x)> &&
1255
1255
std::is_signed_v<CPP2_TYPEOF (x)> != std::is_signed_v<C> &&
1256
1256
sizeof (CPP2_TYPEOF (x)) <= sizeof (C)
1257
1257
)
1258
- {
1259
- const C c = static_cast <C>(x);
1260
- Type.enforce ( // precondition check: must be round-trippable => not lossy
1261
- static_cast <CPP2_TYPEOF (x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF (x){}),
1262
- " dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
1263
- );
1264
- return c;
1265
- }
1266
-
1267
- template < typename C, typename X >
1268
- requires std::is_same_v<C, X>
1269
- auto as ( X const & x ) -> decltype(auto ) {
1270
- return x;
1271
- }
1272
-
1273
- template < typename C, typename X >
1274
- requires std::is_same_v<C, X>
1275
- auto as ( X& x ) -> decltype(auto ) {
1276
- return x;
1277
- }
1278
-
1279
-
1280
- template < typename C, typename X >
1281
- auto as (X const & x) -> C
1282
- requires (std::is_same_v<C, std::string> && std::is_integral_v<X>)
1283
- {
1284
- return cpp2::to_string (x);
1285
- }
1286
-
1287
-
1288
- template < typename C, typename X >
1289
- auto as ( X const & x ) -> auto
1290
- requires (!std::is_same_v<C, X> && !std::is_base_of_v<C, X> && requires { C{x}; }
1291
- && !(std::is_same_v<C, std::string> && std::is_integral_v<X>) // exclude above case
1292
- )
1293
- {
1294
- // Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
1295
- // like std::optional, and try to prevent accidental narrowing conversions even when
1296
- // those types themselves don't defend against them
1297
- if constexpr ( requires { requires std::is_convertible_v<X, typename C::value_type>; } ) {
1298
- if constexpr ( is_narrowing_v<typename C::value_type, X>) {
1299
- return nonesuch;
1258
+ {
1259
+ const C c = static_cast <C>(x);
1260
+ Type.enforce ( // precondition check: must be round-trippable => not lossy
1261
+ static_cast <CPP2_TYPEOF (x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF (x){}),
1262
+ " dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
1263
+ );
1264
+ return CPP2_COPY (c);
1265
+ }
1266
+ else if constexpr (std::is_same_v<C, std::string> && std::is_integral_v<X>) {
1267
+ return cpp2::to_string (x);
1268
+ }
1269
+ else if constexpr (std::is_same_v<C, X>) {
1270
+ return x;
1271
+ }
1272
+ else if constexpr (std::is_base_of_v<C, X>) {
1273
+ return static_cast <C const &>(x);
1274
+ }
1275
+ else if constexpr (std::is_base_of_v<X, C>) {
1276
+ return Dynamic_cast<C const &>(x);
1277
+ }
1278
+ else if constexpr (
1279
+ std::is_pointer_v<C>
1280
+ && std::is_pointer_v<X>
1281
+ && requires { requires std::is_base_of_v<deref_t <X>, deref_t <C>>; }
1282
+ )
1283
+ {
1284
+ return Dynamic_cast<C>(x);
1285
+ }
1286
+ else if constexpr (requires { C{x}; }) {
1287
+ // Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
1288
+ // like std::optional, and try to prevent accidental narrowing conversions even when
1289
+ // those types themselves don't defend against them
1290
+ if constexpr ( requires { requires std::is_convertible_v<X, typename C::value_type>; } ) {
1291
+ if constexpr ( is_narrowing_v<typename C::value_type, X>) {
1292
+ return nonesuch;
1293
+ }
1300
1294
}
1295
+ return C{x};
1296
+ }
1297
+ else {
1298
+ return nonesuch;
1301
1299
}
1302
- return C{x};
1303
- }
1304
-
1305
- template < typename C, typename X >
1306
- requires (std::is_base_of_v<C, X> && !std::is_same_v<C, X>)
1307
- auto as ( X& x ) -> C& {
1308
- return x;
1309
- }
1310
-
1311
- template < typename C, typename X >
1312
- requires (std::is_base_of_v<C, X> && !std::is_same_v<C, X>)
1313
- auto as ( X const & x ) -> C const & {
1314
- return x;
1315
- }
1316
-
1317
- template < typename C, typename X >
1318
- requires (std::is_base_of_v<X, C> && !std::is_same_v<C,X>)
1319
- auto as ( X& x ) -> C& {
1320
- return Dynamic_cast<C&>(x);
1321
- }
1322
-
1323
- template < typename C, typename X >
1324
- requires (std::is_base_of_v<X, C> && !std::is_same_v<C,X>)
1325
- auto as ( X const & x ) -> C const & {
1326
- return Dynamic_cast<C const &>(x);
1327
1300
}
1328
1301
1329
1302
template < typename C, typename X >
1330
- requires (
1331
- std::is_pointer_v<C>
1332
- && std::is_pointer_v<X>
1333
- && std::is_base_of_v<CPP2_TYPEOF(*std::declval<X>()), CPP2_TYPEOF(*std::declval<C>())>
1334
- && !std::is_same_v<C, X>
1335
- )
1336
- auto as ( X x ) -> C {
1337
- return Dynamic_cast<C>(x);
1303
+ auto as ( X& x ) -> decltype(auto ) {
1304
+ if constexpr (std::is_same_v<C, X>) {
1305
+ return x;
1306
+ }
1307
+ else if constexpr (std::is_base_of_v<C, X>) {
1308
+ return static_cast <C&>(x);
1309
+ }
1310
+ else if constexpr (std::is_base_of_v<X, C>) {
1311
+ return Dynamic_cast<C&>(x);
1312
+ }
1313
+ else {
1314
+ return as<C>(std::as_const (x));
1315
+ }
1338
1316
}
1339
1317
1340
1318
0 commit comments