@@ -2490,6 +2490,390 @@ static if (haveDip1000 && __VERSION__ >= 2100)
24902490 }));
24912491}
24922492
2493+ /**
2494+ * Checks whether a `SumType` contains a value of a given type.
2495+ *
2496+ * The types must match exactly, without implicit conversions.
2497+ *
2498+ * Params:
2499+ * T = the type to check for.
2500+ */
2501+ template has (T)
2502+ {
2503+ /**
2504+ * The actual `has` function.
2505+ *
2506+ * Params:
2507+ * self = the `SumType` to check.
2508+ *
2509+ * Returns: true if `self` contains a `T`, otherwise false.
2510+ */
2511+ bool has (Self)(auto ref Self self)
2512+ if (isSumType! Self)
2513+ {
2514+ return self.match! checkType;
2515+ }
2516+
2517+ // Helper to avoid redundant template instantiations
2518+ private bool checkType (Value)(ref Value value)
2519+ {
2520+ return is (Value == T);
2521+ }
2522+ }
2523+
2524+ // / Basic usage
2525+ @safe unittest {
2526+ SumType! (string , double ) example = " hello" ;
2527+
2528+ assert ( example.has! string );
2529+ assert (! example.has! double );
2530+
2531+ // If T isn't part of the SumType, has!T will always return false
2532+ assert (! example.has! int );
2533+ }
2534+
2535+ // / With type qualifiers
2536+ @safe unittest {
2537+ alias Example = SumType! (string , double );
2538+
2539+ Example m = " mutable" ;
2540+ const Example c = " const" ;
2541+ immutable Example i = " immutable" ;
2542+
2543+ assert ( m.has! string );
2544+ assert (! m.has! (const (string )));
2545+ assert (! m.has! (immutable (string )));
2546+
2547+ assert (! c.has! string );
2548+ assert ( c.has! (const (string )));
2549+ assert (! c.has! (immutable (string )));
2550+
2551+ assert (! i.has! string );
2552+ assert (! i.has! (const (string )));
2553+ assert ( i.has! (immutable (string )));
2554+ }
2555+
2556+ // / As a predicate
2557+ version (D_BetterC ) {} else
2558+ @safe unittest {
2559+ import std.algorithm.iteration : filter;
2560+ import std.algorithm.comparison : equal;
2561+
2562+ alias Example = SumType! (string , double );
2563+
2564+ auto arr = [
2565+ Example(" foo" ),
2566+ Example(0 ),
2567+ Example(" bar" ),
2568+ Example(1 ),
2569+ Example(2 ),
2570+ Example(" baz" )
2571+ ];
2572+
2573+ auto strings = arr.filter! (has! string );
2574+ auto nums = arr.filter! (has! double );
2575+
2576+ assert (strings.equal([Example(" foo" ), Example(" bar" ), Example(" baz" )]));
2577+ assert (nums.equal([Example(0 ), Example(1 ), Example(2 )]));
2578+ }
2579+
2580+ // Non-copyable types
2581+ @safe unittest {
2582+ static struct NoCopy
2583+ {
2584+ @disable this (this );
2585+ }
2586+
2587+ SumType! NoCopy x;
2588+
2589+ assert (x.has! NoCopy);
2590+ }
2591+
2592+ /**
2593+ * Accesses a `SumType`'s value.
2594+ *
2595+ * The value must be of the specified type. Use [has] to check.
2596+ *
2597+ * Params:
2598+ * T = the type of the value being accessed.
2599+ */
2600+ template get (T)
2601+ {
2602+ /**
2603+ * The actual `get` function.
2604+ *
2605+ * Params:
2606+ * self = the `SumType` whose value is being accessed.
2607+ *
2608+ * Returns: the `SumType`'s value.
2609+ */
2610+ auto ref T get (Self)(auto ref Self self)
2611+ if (isSumType! Self)
2612+ {
2613+ import std.typecons : No;
2614+
2615+ static if (__traits(isRef, self))
2616+ return self.match! (getLvalue! (No.try_, T));
2617+ else
2618+ return self.match! (getRvalue! (No.try_, T));
2619+ }
2620+ }
2621+
2622+ // / Basic usage
2623+ @safe unittest {
2624+ SumType! (string , double ) example1 = " hello" ;
2625+ SumType! (string , double ) example2 = 3.14 ;
2626+
2627+ assert (example1.get ! string == " hello" );
2628+ assert (example2.get ! double == 3.14 );
2629+ }
2630+
2631+ // / With type qualifiers
2632+ @safe unittest {
2633+ alias Example = SumType! (string , double );
2634+
2635+ Example m = " mutable" ;
2636+ const (Example) c = " const" ;
2637+ immutable (Example) i = " immutable" ;
2638+
2639+ assert (m.get ! string == " mutable" );
2640+ assert (c.get ! (const (string )) == " const" );
2641+ assert (i.get ! (immutable (string )) == " immutable" );
2642+ }
2643+
2644+ // / As a predicate
2645+ version (D_BetterC ) {} else
2646+ @safe unittest {
2647+ import std.algorithm.iteration : map;
2648+ import std.algorithm.comparison : equal;
2649+
2650+ alias Example = SumType! (string , double );
2651+
2652+ auto arr = [Example(0 ), Example(1 ), Example(2 )];
2653+ auto values = arr.map! (get ! double );
2654+
2655+ assert (values .equal([0 , 1 , 2 ]));
2656+ }
2657+
2658+ // Non-copyable types
2659+ @safe unittest {
2660+ static struct NoCopy
2661+ {
2662+ @disable this (this );
2663+ }
2664+
2665+ SumType! NoCopy lvalue;
2666+ auto rvalue () { return SumType! NoCopy(); }
2667+
2668+ assert (lvalue.get ! NoCopy == NoCopy());
2669+ assert (rvalue.get ! NoCopy == NoCopy());
2670+ }
2671+
2672+ // Immovable rvalues
2673+ @safe unittest {
2674+ auto rvalue () { return const (SumType! string )(" hello" ); }
2675+
2676+ assert (rvalue.get ! (const (string )) == " hello" );
2677+ }
2678+
2679+ // Nontrivial rvalues at compile time
2680+ @safe unittest {
2681+ static struct ElaborateCopy
2682+ {
2683+ this (this ) {}
2684+ }
2685+
2686+ enum rvalue = SumType! ElaborateCopy();
2687+ enum ctResult = rvalue.get ! ElaborateCopy;
2688+
2689+ assert (ctResult == ElaborateCopy());
2690+ }
2691+
2692+ /**
2693+ * Attempt to access a `SumType`'s value.
2694+ *
2695+ * If the `SumType` does not contain a value of the specified type, an
2696+ * exception is thrown.
2697+ *
2698+ * Params:
2699+ * T = the type of the value being accessed.
2700+ */
2701+ version (D_Exceptions)
2702+ template tryGet (T)
2703+ {
2704+ /**
2705+ * The actual `tryGet` function.
2706+ *
2707+ * Params:
2708+ * self = the `SumType` whose value is being accessed.
2709+ *
2710+ * Throws: `MatchException` if the value does not have the expected type.
2711+ *
2712+ * Returns: the `SumType`'s value.
2713+ */
2714+ auto ref T tryGet (Self)(auto ref Self self)
2715+ if (isSumType! Self)
2716+ {
2717+ import std.typecons : Yes;
2718+
2719+ static if (__traits(isRef, self))
2720+ return self.match! (getLvalue! (Yes.try_, T));
2721+ else
2722+ return self.match! (getRvalue! (Yes.try_, T));
2723+ }
2724+ }
2725+
2726+ // / Basic usage
2727+ version (D_Exceptions)
2728+ @safe unittest {
2729+ SumType! (string , double ) example = " hello" ;
2730+
2731+ assert (example.tryGet! string == " hello" );
2732+
2733+ double result = double .nan;
2734+ try
2735+ result = example.tryGet! double ;
2736+ catch (MatchException e)
2737+ result = 0 ;
2738+
2739+ // Exception was thrown
2740+ assert (result == 0 );
2741+ }
2742+
2743+ // / With type qualifiers
2744+ version (D_Exceptions)
2745+ @safe unittest {
2746+ import std.exception : assertThrown;
2747+
2748+ const (SumType! (string , double )) example = " const" ;
2749+
2750+ // Qualifier mismatch; throws exception
2751+ assertThrown! MatchException(example.tryGet! string );
2752+ // Qualifier matches; no exception
2753+ assert (example.tryGet! (const (string )) == " const" );
2754+ }
2755+
2756+ // / As a predicate
2757+ version (D_BetterC ) {} else
2758+ @safe unittest {
2759+ import std.algorithm.iteration : map, sum;
2760+ import std.functional : pipe;
2761+ import std.exception : assertThrown;
2762+
2763+ alias Example = SumType! (string , double );
2764+
2765+ auto arr1 = [Example(0 ), Example(1 ), Example(2 )];
2766+ auto arr2 = [Example(" foo" ), Example(" bar" ), Example(" baz" )];
2767+
2768+ alias trySum = pipe! (map! (tryGet! double ), sum);
2769+
2770+ assert (trySum(arr1) == 0 + 1 + 2 );
2771+ assertThrown! MatchException(trySum(arr2));
2772+ }
2773+
2774+ // Throws if requested type is impossible
2775+ version (D_Exceptions)
2776+ @safe unittest {
2777+ import std.exception : assertThrown;
2778+
2779+ SumType! int x;
2780+
2781+ assertThrown! MatchException(x.tryGet! string );
2782+ }
2783+
2784+ // Non-copyable types
2785+ version (D_Exceptions)
2786+ @safe unittest {
2787+ static struct NoCopy
2788+ {
2789+ @disable this (this );
2790+ }
2791+
2792+ SumType! NoCopy lvalue;
2793+ auto rvalue () { return SumType! NoCopy(); }
2794+
2795+ assert (lvalue.tryGet! NoCopy == NoCopy());
2796+ assert (rvalue.tryGet! NoCopy == NoCopy());
2797+ }
2798+
2799+ // Immovable types
2800+ version (D_Exceptions)
2801+ @safe unittest {
2802+ auto rvalue () { return const (SumType! string )(" hello" ); }
2803+
2804+ assert (rvalue.tryGet! (const (string )) == " hello" );
2805+ }
2806+
2807+ // Nontrivial rvalues at compile time
2808+ version (D_Exceptions)
2809+ @safe unittest {
2810+ static struct ElaborateCopy
2811+ {
2812+ this (this ) {}
2813+ }
2814+
2815+ enum rvalue = SumType! ElaborateCopy();
2816+ enum ctResult = rvalue.tryGet! ElaborateCopy;
2817+
2818+ assert (ctResult == ElaborateCopy());
2819+ }
2820+
2821+ private template failedGetMessage (Expected, Actual)
2822+ {
2823+ static if (Expected.stringof == Actual.stringof) {
2824+ enum expectedStr = __traits(fullyQualifiedName, Expected);
2825+ enum actualStr = __traits(fullyQualifiedName, Actual);
2826+ } else {
2827+ enum expectedStr = Expected.stringof;
2828+ enum actualStr = Actual.stringof;
2829+ }
2830+
2831+ enum failedGetMessage =
2832+ " Tried to get `" ~ expectedStr ~ " `" ~
2833+ " but found `" ~ actualStr ~ " `" ;
2834+ }
2835+
2836+ private template getLvalue (Flag! " try_" try_, T)
2837+ {
2838+ ref T getLvalue (Value)(ref Value value)
2839+ {
2840+ static if (is (Value == T)) {
2841+ return value;
2842+ } else {
2843+ static if (try_)
2844+ throw new MatchException(failedGetMessage! (T, Value));
2845+ else
2846+ assert (false , failedGetMessage! (T, Value));
2847+ }
2848+ }
2849+ }
2850+
2851+ private template getRvalue (Flag! " try_" try_, T)
2852+ {
2853+ T getRvalue (Value)(ref Value value)
2854+ {
2855+ static if (is (Value == T)) {
2856+ import core.lifetime : move;
2857+
2858+ // Move if possible; otherwise fall back to copy
2859+ static if (is (typeof (move(value)))) {
2860+ static if (isCopyable! Value)
2861+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
2862+ return __ctfe ? value : move(value);
2863+ else
2864+ return move (value);
2865+ } else {
2866+ return value;
2867+ }
2868+ } else {
2869+ static if (try_)
2870+ throw new MatchException(failedGetMessage! (T, Value));
2871+ else
2872+ assert (false , failedGetMessage! (T, Value));
2873+ }
2874+ }
2875+ }
2876+
24932877private void destroyIfOwner (T)(ref T value)
24942878{
24952879 static if (hasElaborateDestructor! T) {
0 commit comments