Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Changeset 1737

Show
Ignore:
Timestamp:
07/08/10 10:07:56 (14 years ago)
Author:
rsinfu
Message:

[devel] std.typecons.Any for ad-hoc polymorphism on duck-typed objects

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/devel/stdio-native-codeset/phobos/std/typecons.d

    r1725 r1737  
    21972197            return another; 
    21982198        } 
    21992199    } 
    22002200    auto a = A(4); 
    22012201    auto b = a.copy(); 
    22022202    if (a.x._refCountedStore._count != 2) { 
    22032203        stderr.writeln("*** BUG 4356 still unfixed"); 
    22042204    } 
    22052205} 
    22062206 
     2207 
     2208/*-* 
     2209 * Provides ad-hoc polymorphism on a specific group of objects that 
     2210 * have the same interface (duck-typed). 
     2211 */ 
     2212package struct Any(Ducks...) 
     2213{ 
     2214    /** 
     2215     * Invokes the method $(D op) on the active object. 
     2216     * 
     2217     * The allowed operation $(D op) is the intersection of the allowed 
     2218     * operations on all $(D Ducks). 
     2219     * 
     2220     * Throws: 
     2221     * $(UL 
     2222     *   $(LI $(D Error) if this $(D Any) object is empty) 
     2223     * ) 
     2224     */ 
     2225    @system auto ref opDispatch(string op, Args...)(auto ref Args args) 
     2226        if (_canDispatch!(op, Args)) 
     2227    { 
     2228        if (_which == size_t.max) 
     2229            throw new Error("dispatching " ~ op ~ Args.stringof 
     2230                    ~ " on an empty " ~ typeof(this).stringof); 
     2231 
     2232        mixin (_onActiveDuck!( 
     2233            q{ 
     2234                return mixin("_storageAs!Duck()." 
     2235                        ~ (args.length == 0 ? op : op ~ "(args)")); 
     2236            })); 
     2237        assert(0); 
     2238    } 
     2239 
     2240 
     2241    /** 
     2242     * If $(D T) is one of the $(D Ducks), alters the contained object 
     2243     * with $(D rhs); otherwise assigns $(D rhs) on the active object. 
     2244     */ 
     2245    @system void opAssign(T)(T rhs) 
     2246        if (_canAssign!(T)) 
     2247    { 
     2248        if (_which == size_t.max) 
     2249            _grab(rhs); 
     2250        else 
     2251            _assign(rhs); 
     2252    } 
     2253 
     2254    @trusted void opAssign(T)(T rhs) 
     2255        if (is(T == typeof(this))) 
     2256    { 
     2257        swap(this, rhs); 
     2258    } 
     2259 
     2260    // @@@BUG4424@@@ workaround 
     2261    private template _workaround4424() 
     2262        { @disable void opAssign(...) { assert(0); } } 
     2263    mixin _workaround4424 _workaround4424_; 
     2264 
     2265 
     2266    //----------------------------------------------------------------// 
     2267 
     2268    // operator overloads 
     2269 
     2270    @system auto ref opUnary(string op)() 
     2271    { 
     2272        if (_which == size_t.max) 
     2273            throw new Error("unary " ~ op ~ " on an empty " 
     2274                    ~ typeof(this).stringof); 
     2275 
     2276        mixin (_onActiveDuck!( 
     2277            q{ 
     2278                return mixin(op ~ "_storageAs!Duck"); 
     2279            })); 
     2280        assert(0); 
     2281    } 
     2282 
     2283    @system auto ref opIndexUnary(string op, Indices...)(Indices indices) 
     2284    { 
     2285        if (_which == size_t.max) 
     2286            throw new Error("unary " ~ to!string(Indices.length) 
     2287                    ~ "-indexing " ~ op ~ " on an empty " 
     2288                    ~ typeof(this).stringof); 
     2289 
     2290        mixin (_onActiveDuck!( 
     2291            q{ 
     2292                return mixin(op ~ "_storageAs!Duck[" ~ 
     2293                        _commaExpand!("indices", Indices.length) ~ "]"); 
     2294            })); 
     2295        assert(0); 
     2296    } 
     2297 
     2298    @system auto ref opSliceUnary(string op, I, J)(I i, J j) 
     2299    { 
     2300        if (_which == size_t.max) 
     2301            throw new Error("unary slicing " ~ op ~ " on an empty " 
     2302                    ~ typeof(this).stringof); 
     2303 
     2304        mixin (_onActiveDuck!( 
     2305            q{ 
     2306                return mixin(op ~ "_storageAs!Duck[i .. j]"); 
     2307            })); 
     2308        assert(0); 
     2309    } 
     2310 
     2311    @system auto ref opSliceUnary(string op)() 
     2312    { 
     2313        if (_which == size_t.max) 
     2314            throw new Error("unary slicing " ~ op ~ " on an empty " 
     2315                    ~ typeof(this).stringof); 
     2316 
     2317        mixin (_onActiveDuck!( 
     2318            q{ 
     2319                return mixin(op ~ "_storageAs!Duck[]"); 
     2320            })); 
     2321        assert(0); 
     2322    } 
     2323 
     2324    @system auto ref opCast(T)() 
     2325    { 
     2326        if (_which == size_t.max) 
     2327            throw new Error("casting an empty " ~ typeof(this).stringof 
     2328                    ~ " to " ~ T.stringof); 
     2329 
     2330        mixin (_onActiveDuck!( 
     2331            q{ 
     2332                static if (is(T == Duck)) 
     2333                    return         _storageAs!Duck; 
     2334                else 
     2335                    return cast(T) _storageAs!Duck; 
     2336            })); 
     2337        assert(0); 
     2338    } 
     2339 
     2340    @system auto ref opBinary(string op, RHS)(RHS rhs) 
     2341    { 
     2342        if (_which == size_t.max) 
     2343            throw new Error("binary " ~ op ~ " on an empty LHS " 
     2344                    ~ typeof(this).stringof ~ " and RHS " ~ RHS.stringof); 
     2345 
     2346        mixin (_onActiveDuck!( 
     2347            q{ 
     2348                return mixin("_storageAs!Duck() " ~ op ~ " rhs"); 
     2349            })); 
     2350        assert(0); 
     2351    } 
     2352 
     2353    @system auto ref opBinaryRight(string op, LHS)(LHS lhs) 
     2354    { 
     2355        if (_which == size_t.max) 
     2356            throw new Error("binary " ~ op ~ " on LHS " ~ LHS.stringof 
     2357                    ~ "and an empty RHS " ~ typeof(this).stringof); 
     2358 
     2359        mixin (_onActiveDuck!( 
     2360            q{ 
     2361                return mixin("lhs " ~ op ~ "_storageAs!Duck"); 
     2362            })); 
     2363        assert(0); 
     2364    } 
     2365 
     2366    @system bool opEquals(RHS)(auto ref RHS rhs) const 
     2367    { 
     2368        if (_which == size_t.max) 
     2369            throw new Error("comparing an empty " ~ typeof(this).stringof 
     2370                    ~ " with " ~ RHS.stringof); 
     2371 
     2372        mixin (_onActiveDuck!( 
     2373            q{ 
     2374                return _storageAs_const!Duck() == rhs; 
     2375            })); 
     2376        assert(0); 
     2377    } 
     2378 
     2379    @system bool opEquals(RHS : typeof(this))(ref const RHS rhs) const 
     2380    { 
     2381        if (_which != size_t.max && _which == rhs._which) 
     2382        { 
     2383            mixin (_onActiveDuck!( 
     2384                q{ 
     2385                    return _storageAs_const!Duck() == 
     2386                            rhs._storageAs_const!Duck; 
     2387                })); 
     2388            assert(0); 
     2389        } 
     2390        else 
     2391        { 
     2392            return false; 
     2393        } 
     2394    } 
     2395 
     2396    @system int opCmp(RHS)(auto ref RHS rhs) const 
     2397    { 
     2398        if (_which == size_t.max) 
     2399            throw new Error("comparing an empty " ~ typeof(this).stringof 
     2400                    ~ " with " ~ RHS.stringof); 
     2401 
     2402        mixin (_onActiveDuck!( 
     2403            q{ 
     2404                return (_storageAs_const!Duck < rhs) ? -1 : 
     2405                       (_storageAs_const!Duck > rhs) ?  1 : 0; 
     2406            })); 
     2407        assert(0); 
     2408    } 
     2409 
     2410    @system int opCmp(RHS : typeof(this))(ref const RHS rhs) const 
     2411    { 
     2412        if (_which == size_t.max || rhs._which == size_t.max) 
     2413            throw new Error("comparing empty " ~ typeof(this).stringof 
     2414                    ~ " objects"); 
     2415 
     2416        mixin (_onActiveDuck!( 
     2417            q{ 
     2418                return -rhs.opCmp(_storageAs_const!Duck); 
     2419            })); 
     2420        assert(0); 
     2421    } 
     2422 
     2423    @system auto ref opCall(Args...)(auto ref Args args) 
     2424    { 
     2425        if (_which == size_t.max) 
     2426            throw new Error("calling an empty " ~ typeof(this).stringof 
     2427                    ~ " object"); 
     2428 
     2429        mixin (_onActiveDuck!( 
     2430            q{ 
     2431                return _storageAs!Duck()(args); 
     2432            })); 
     2433        assert(0); 
     2434    } 
     2435 
     2436    @system auto ref opOpAssign(string op, RHS)(RHS rhs) 
     2437    { 
     2438        mixin (_onActiveDuck!( 
     2439            q{ 
     2440                return mixin("_storageAs!Duck() " ~ op ~ "= rhs"); 
     2441            })); 
     2442        assert(0); 
     2443    } 
     2444 
     2445    @system auto ref opIndexOpAssign(string op, RHS, Indices...) 
     2446        (RHS rhs, Indices indices) 
     2447    { 
     2448        mixin (_onActiveDuck!( 
     2449            q{ 
     2450                return mixin("_storageAs!Duck[" ~ 
     2451                        _commaExpand!("indices", Indices.length) ~ 
     2452                    "] " ~ op ~ "= rhs"); 
     2453            })); 
     2454        assert(0); 
     2455    } 
     2456 
     2457    @system auto ref opSliceOpAssign(string op, RHS, I, J)(RHS rhs, I i, J j) 
     2458    { 
     2459        mixin (_onActiveDuck!( 
     2460            q{ 
     2461                return mixin("_storageAs!Duck[i .. j] " ~ op ~ "= rhs"); 
     2462            })); 
     2463        assert(0); 
     2464    } 
     2465 
     2466    @system auto ref opSliceOpAssign(string op, RHS)(RHS rhs) 
     2467    { 
     2468        mixin (_onActiveDuck!( 
     2469            q{ 
     2470                return mixin("_storageAs!Duck[] " ~ op ~ "= rhs"); 
     2471            })); 
     2472        assert(0); 
     2473    } 
     2474 
     2475    @system auto ref opIndex(Indices...)(Indices indices) 
     2476    { 
     2477        mixin (_onActiveDuck!( 
     2478            q{ 
     2479                return mixin("_storageAs!Duck[" ~ 
     2480                    _commaExpand!("indices", Indices.length) ~ "]"); 
     2481            })); 
     2482        assert(0); 
     2483    } 
     2484 
     2485    @system auto ref opSlice(I, J)(I i, J j) 
     2486    { 
     2487        mixin (_onActiveDuck!( 
     2488            q{ 
     2489                return _storageAs!Duck[i .. j]; 
     2490            })); 
     2491        assert(0); 
     2492    } 
     2493 
     2494    @system auto ref opSlice(_Dummy = void)() 
     2495    { 
     2496        mixin (_onActiveDuck!( 
     2497            q{ 
     2498                return _storageAs!Duck[]; 
     2499            })); 
     2500        assert(0); 
     2501    } 
     2502 
     2503    /+ 
     2504    // @@@ cannot coexist with input range primitives 
     2505    @system int opApply(Args...)(int delegate(ref Args) dg) 
     2506    { 
     2507        mixin (_onActiveDuck!( 
     2508            q{ 
     2509                return _storageAs!Duck().opApply(dg); 
     2510            })); 
     2511    } 
     2512    +/ 
     2513 
     2514 
     2515    //----------------------------------------------------------------// 
     2516 
     2517    /** 
     2518     * Invokes the copy constructor on the active object if any. 
     2519     */ 
     2520    @system this(this) 
     2521    { 
     2522        if (_which != size_t.max) 
     2523            _postblit(); 
     2524    } 
     2525 
     2526 
     2527    /** 
     2528     * Invokes the destructor on the active object if any. 
     2529     */ 
     2530    @system ~this() 
     2531    { 
     2532        if (_which != size_t.max) 
     2533            _dispose(); 
     2534    } 
     2535 
     2536 
     2537    /** 
     2538     * The $(D Any) namespace provides various 'meta' methods 
     2539     * for operating on this $(D Any) object itself, not a 
     2540     * contained object. 
     2541     * 
     2542     * Example: 
     2543-------------------- 
     2544Any!(A, B) ab; 
     2545 
     2546assert(ab.Any.empty); 
     2547assert(ab.Any.allows!A); 
     2548 
     2549ab = A(); 
     2550assert(ab.Any.isActive!A); 
     2551 
     2552A a = ab.Any.instance!A; 
     2553-------------------- 
     2554     */ 
     2555    template _Any() 
     2556    { 
     2557        /** 
     2558         * A tuple of types that are considered homogeneous in this 
     2559         * object, i.e. the $(D Ducks). 
     2560         */ 
     2561        alias Ducks Types; 
     2562 
     2563 
     2564        /** 
     2565         * Returns $(D true) if type $(D T) is listed in the type 
     2566         * list $(D Types). 
     2567         */ 
     2568        template allows(T) // FIXME the name 
     2569        { 
     2570            enum bool allows = _homogenizes!T; 
     2571        } 
     2572 
     2573 
     2574        /** 
     2575         * Returns $(D true) if this $(D Any) object contains 
     2576         * nothing. 
     2577         */ 
     2578        @safe @property bool empty() const nothrow 
     2579        { 
     2580            return _which == size_t.max; 
     2581        } 
     2582 
     2583 
     2584        /** 
     2585         * Returns $(D true) if the type of the active object is $(D T). 
     2586         */ 
     2587        @safe bool isActive(T)() const nothrow 
     2588        { 
     2589            return _which != size_t.max && _which == _duckID!T; 
     2590        } 
     2591 
     2592 
     2593        /** 
     2594         * Touch the active object directly (by ref).  The type $(D T) 
     2595         * must be the active one, i.e. $(D isActive!T == true). 
     2596         * 
     2597         * Throws: 
     2598         * $(UL 
     2599         *   $(LI $(D assertion) fails if the $(D Any) object is 
     2600         *        empty or $(D T) is not active) 
     2601         * ) 
     2602         */ 
     2603        @trusted @property ref T instance(T)() nothrow 
     2604            if (allows!(T)) 
     2605        in 
     2606        { 
     2607            assert(_which == _duckID!T); 
     2608        } 
     2609        body 
     2610        { 
     2611            return _storageAs!T; 
     2612        } 
     2613        /+ // @@@BUG3748@@@ 
     2614        @trusted @property ref inout(T) instance(T)() inout nothrow 
     2615        +/ 
     2616    } 
     2617 
     2618    /// Ditto 
     2619    alias _Any!() Any; 
     2620 
     2621 
     2622    //----------------------------------------------------------------// 
     2623private: 
     2624    // The internal symbols are prefixed with an underscore so that 
     2625    // opDispatch will not be blocked. @@@ 
     2626 
     2627    /* 
     2628     * Determines if the operation $(D op) is supported on all the ducks 
     2629     * and the return types are compatible. 
     2630     */ 
     2631    template _canDispatch(string op, Args...) 
     2632    { 
     2633        enum bool _canDispatch = __traits(compiles, 
     2634                function(Args args) 
     2635                { 
     2636                    // The operation must be supported by all ducks, and 
     2637                    // the return types must be compatible. 
     2638                    foreach (Duck; Ducks) 
     2639                    { 
     2640                        Duck duck; 
     2641                        static if (Args.length == 0) 
     2642                            return mixin("duck." ~ op); 
     2643                        else 
     2644                            return mixin("duck." ~ op ~ "(args)"); 
     2645                    } 
     2646                    assert(0); 
     2647                }); 
     2648    } 
     2649 
     2650 
     2651    /* 
     2652     * Determines if a value of $(D T) can be assigned to at least one 
     2653     * of the ducks. 
     2654     */ 
     2655    template _canAssign(T, size_t i = 0) 
     2656    { 
     2657        static if (i < Ducks.length) 
     2658            enum bool _canAssign = 
     2659                is(Ducks[i] == T) || 
     2660                __traits(compiles, 
     2661                    function(ref Ducks[i] duck, T rhs) 
     2662                    { 
     2663                        duck = rhs; 
     2664                    }) 
     2665                || _canAssign!(T, i + 1); 
     2666        else 
     2667            enum bool _canAssign = false; 
     2668    } 
     2669 
     2670    unittest 
     2671    { 
     2672        foreach (Duck; Ducks) 
     2673            assert(_canAssign!(Duck)); 
     2674        struct Unknown {} 
     2675        assert(!_canAssign!(Unknown)); 
     2676    } 
     2677 
     2678 
     2679    /* 
     2680     * Returns $(D true) if the type $(D T) is in the set of types 
     2681     * $(D Ducks). 
     2682     */ 
     2683    template _homogenizes(T) 
     2684    { 
     2685        enum bool _homogenizes = (_duckID!T != size_t.max); 
     2686    } 
     2687 
     2688    unittest 
     2689    { 
     2690        foreach (Duck; Ducks) 
     2691            assert(_homogenizes!(Duck)); 
     2692        struct Unknown {} 
     2693        assert(!_homogenizes!(Unknown)); 
     2694    } 
     2695 
     2696 
     2697    /* 
     2698     * Returns the ID of the duck of type $(D T), or $(D size_t.max) if 
     2699     * $(D T) is not in the set of the types. 
     2700     */ 
     2701    template _duckID(T, size_t id = 0) 
     2702    { 
     2703        static if (id < Ducks.length) 
     2704        { 
     2705            static if (is(T == Ducks[id])) 
     2706                enum size_t _duckID = id; 
     2707            else 
     2708                enum size_t _duckID = _duckID!(T, id + 1); 
     2709        } 
     2710        else 
     2711        { 
     2712            enum size_t _duckID = size_t.max; 
     2713        } 
     2714    } 
     2715 
     2716 
     2717    /* 
     2718     * Generates code for operating on the active duck. 
     2719     */ 
     2720    template _onActiveDuck(string stmt) 
     2721    { 
     2722        enum string _onActiveDuck = 
     2723                "assert(_which != size_t.max);" ~ 
     2724            "L_chooseActive:" ~ 
     2725                "final switch (_which) {" ~ 
     2726                    "foreach (Duck; Ducks) {" ~ 
     2727                        "case _duckID!Duck:" ~ 
     2728                            stmt ~ 
     2729                            "break L_chooseActive;" ~ 
     2730                    "}" ~ 
     2731                "}"; 
     2732    } 
     2733 
     2734 
     2735    /* 
     2736     * Set $(D rhs) in the storage. 
     2737     */ 
     2738    @trusted void _grab(T)(ref T rhs) 
     2739    in 
     2740    { 
     2741        assert(_which == size_t.max); 
     2742    } 
     2743    body 
     2744    { 
     2745        static if (_homogenizes!(T)) 
     2746        { 
     2747            // Simple blit. 
     2748            _init(_storageAs!T); 
     2749            swap(_storageAs!T, rhs); 
     2750            _which = _duckID!T; 
     2751        } 
     2752        else 
     2753        { 
     2754            // Use the first-matching opAssign. 
     2755            foreach (Duck; Ducks) 
     2756            { 
     2757                static if (__traits(compiles, _storageAs!Duck() = rhs)) 
     2758                { 
     2759                    _init(_storageAs!Duck); 
     2760                    _storageAs!Duck() = rhs; 
     2761                    _which = _duckID!Duck; 
     2762                    break; 
     2763                } 
     2764            } 
     2765        } 
     2766    } 
     2767 
     2768 
     2769    /* 
     2770     * Assigns $(D rhs) to the existing active object. 
     2771     */ 
     2772    @trusted void _assign(T)(ref T rhs) 
     2773    in 
     2774    { 
     2775        assert(_which != size_t.max); 
     2776    } 
     2777    body 
     2778    { 
     2779        mixin (_onActiveDuck!( 
     2780            q{ 
     2781                static if (__traits(compiles, _storageAs!Duck() = rhs)) 
     2782                    return _storageAs!Duck() = rhs; 
     2783            })); 
     2784 
     2785        // Or, alter the content with rhs. 
     2786        _dispose(); 
     2787        _grab(rhs); 
     2788    } 
     2789 
     2790 
     2791    /* 
     2792     * Returns a reference to the holded object as an instance of 
     2793     * type $(D T).  This does not validate the type. 
     2794     */ 
     2795    @system ref T _storageAs(T)() nothrow 
     2796        if (_homogenizes!(T)) 
     2797    { 
     2798        foreach (Duck; Ducks) 
     2799        { 
     2800            static if (_duckID!T == _duckID!Duck) 
     2801                return *cast(Duck*) _storage.ptr; 
     2802        } 
     2803        assert(0); 
     2804    } 
     2805    /+ // @@@BUG3748@@@ 
     2806    @system ref inout(T) storageAs(T)() inout nothrow 
     2807    +/ 
     2808 
     2809    @system ref const(T) _storageAs_const(T)() const nothrow 
     2810        if (_homogenizes!(T)) 
     2811    { 
     2812        foreach (Duck; Ducks) 
     2813        { 
     2814            static if (_duckID!T == _duckID!Duck) 
     2815                return *cast(const Duck*) _storage.ptr; 
     2816        } 
     2817        assert(0); 
     2818    } 
     2819 
     2820 
     2821 
     2822    /* 
     2823     * Runs the copy constructor on the active object. 
     2824     */ 
     2825    @trusted void _postblit() 
     2826    in 
     2827    { 
     2828        assert(_which != size_t.max); 
     2829    } 
     2830    body 
     2831    { 
     2832        mixin (_onActiveDuck!( 
     2833            q{ 
     2834                static if (__traits(compiles, _storageAs!Duck().__postblit())) 
     2835                    _storageAs!Duck().__postblit(); 
     2836                return; 
     2837            })); 
     2838        assert(0); 
     2839    } 
     2840 
     2841 
     2842    /* 
     2843     * Destroys the active object (if it's a struct) and markes this 
     2844     * $(D Any) object empty. 
     2845     */ 
     2846    @trusted void _dispose() 
     2847    in 
     2848    { 
     2849        assert(_which != size_t.max); 
     2850    } 
     2851    out 
     2852    { 
     2853        assert(_which == size_t.max); 
     2854    } 
     2855    body 
     2856    { 
     2857        mixin (_onActiveDuck!( 
     2858            q{ 
     2859                static if (__traits(compiles, _storageAs!Duck.__dtor())) 
     2860                    _storageAs!Duck.__dtor(); 
     2861                _which = size_t.max; 
     2862                return; 
     2863            })); 
     2864        assert(0); 
     2865    } 
     2866 
     2867 
     2868    //----------------------------------------------------------------// 
     2869 
     2870    // @@@ workaround 
     2871    static if (_canDispatch!("front") && _canDispatch!("empty") && 
     2872            _canDispatch!("popFront")) 
     2873    public @system 
     2874    { 
     2875        @property bool empty() 
     2876        { 
     2877            return opDispatch!("empty")(); 
     2878        } 
     2879        @property auto ref front() 
     2880        { 
     2881            return opDispatch!("front")(); 
     2882        } 
     2883        void popFront() 
     2884        { 
     2885            opDispatch!("popFront")(); 
     2886        } 
     2887    } 
     2888 
     2889 
     2890    //----------------------------------------------------------------// 
     2891private: 
     2892    size_t _which = size_t.max;     // ID of the 'active' Duck 
     2893    union 
     2894    { 
     2895        ubyte[_maxSize!(0, Ducks)]                  _storage; 
     2896        void*[_maxSize!(0, Ducks) / (void*).sizeof] _mark; 
     2897    } 
     2898} 
     2899 
     2900// These templates are prefixed by _ because templates are always public 
     2901// even if they have the private attribute. 
     2902 
     2903private template _maxSize(size_t max, TT...) 
     2904{ 
     2905    static if (TT.length > 0) 
     2906    { 
     2907        static if (max < TT[0].sizeof) 
     2908            enum _maxSize = _maxSize!(TT[0].sizeof, TT[1 .. $]); 
     2909        else 
     2910            enum _maxSize = _maxSize!(         max, TT[1 .. $]); 
     2911    } 
     2912    else 
     2913    { 
     2914        enum _maxSize = max; 
     2915    } 
     2916} 
     2917 
     2918private @trusted void _init(T)(ref T obj) 
     2919{ 
     2920    static if (is(T == struct)) 
     2921    { 
     2922        auto buf  = (cast(void*) &obj   )[0 .. T.sizeof]; 
     2923        auto init = (cast(void*) &T.init)[0 .. T.sizeof]; 
     2924        buf[] = init[]; 
     2925    } 
     2926    else 
     2927    { 
     2928        obj = T.init; 
     2929    } 
     2930} 
     2931 
     2932private template _commaExpand(string array, size_t N) 
     2933    if (N >= 1) 
     2934{ 
     2935    static if (N == 1) 
     2936        enum string _commaExpand = array ~ "[0]"; 
     2937    else 
     2938        enum string _commaExpand = _commaExpand!(array, N - 1) 
     2939            ~ ", " ~ array ~ "[" ~ N.stringof ~ " - 1]"; 
     2940} 
     2941 
     2942 
     2943version (unittest) private 
     2944{ 
     2945    bool eq(S)(S a, S b) 
     2946    { 
     2947        foreach (i, _; a.tupleof) 
     2948        { 
     2949            if (a.tupleof[i] != b.tupleof[i]) 
     2950                return false; 
     2951        } 
     2952        return true; 
     2953    } 
     2954 
     2955    bool fails(lazy void expr) 
     2956    { 
     2957        try { expr; } catch (Error e) { return true; } 
     2958        return false; 
     2959    } 
     2960} 
     2961 
     2962unittest 
     2963{ 
     2964    // copy constructor & destructor 
     2965    struct Counter 
     2966    { 
     2967        int* copies; 
     2968        this(this) { copies && ++*copies; } 
     2969        ~this()    { copies && --*copies; } 
     2970    } 
     2971    Any!(Counter) a; 
     2972    a = Counter(new int); 
     2973    assert(*a.copies == 0); 
     2974    { 
     2975        auto b = a; 
     2976        assert(*a.copies == 1); 
     2977        { 
     2978            auto c = a; 
     2979            assert(*a.copies == 2); 
     2980        } 
     2981        assert(*a.copies == 1); 
     2982    } 
     2983    assert(*a.copies == 0); 
     2984} 
     2985 
     2986unittest 
     2987{ 
     2988    // basic use 
     2989    int afoo, bfoo; 
     2990    struct A { 
     2991        int inc() { return ++afoo; } 
     2992        int dec() { return --afoo; } 
     2993    } 
     2994    struct B { 
     2995        int inc() { return --bfoo; } 
     2996        int dec() { return ++bfoo; } 
     2997    } 
     2998    Any!(A, B) ab; 
     2999    ab = A(); 
     3000    assert(ab.inc() == 1 && afoo == 1); 
     3001    assert(ab.dec() == 0 && afoo == 0); 
     3002    ab = B(); 
     3003    assert(ab.inc() == -1 && bfoo == -1); 
     3004    assert(ab.dec() ==  0 && bfoo ==  0); 
     3005} 
     3006 
     3007unittest 
     3008{ 
     3009    // ref argument & ref return 
     3010    struct K { 
     3011        ref int foo(ref int a, ref int b) { ++b; return a; } 
     3012    } 
     3013    Any!(K) k; 
     3014    int v, w; 
     3015    k = K(); 
     3016    assert(&(k.foo(v, w)) == &v); 
     3017    assert(w == 1); 
     3018} 
     3019 
     3020unittest 
     3021{ 
     3022    // meta interface 
     3023    Any!(int, real) a; 
     3024 
     3025    assert(is(a.Any.Types == TypeTuple!(int, real))); 
     3026    assert(a.Any.allows!int); 
     3027    assert(a.Any.allows!real); 
     3028    assert(!a.Any.allows!string); 
     3029 
     3030    assert(a.Any.empty); 
     3031 
     3032    a = 42; 
     3033    assert(!a.Any.empty); 
     3034    assert( a.Any.isActive!int); 
     3035    assert(!a.Any.isActive!real); 
     3036    assert(!a.Any.isActive!string); 
     3037    int* i = &(a.Any.instance!int()); 
     3038    assert(*i == 42); 
     3039 
     3040    a = -21.0L; 
     3041    assert(!a.Any.empty); 
     3042    assert(!a.Any.isActive!int); 
     3043    assert( a.Any.isActive!real); 
     3044    assert(!a.Any.isActive!string); 
     3045    real* r = &(a.Any.instance!real()); 
     3046    assert(*r == -21.0L); 
     3047 
     3048    a = a.init; 
     3049    assert(a.Any.empty); 
     3050} 
     3051 
     3052unittest 
     3053{ 
     3054    // implicit convertion 
     3055    Any!(real, const(char)[]) a; 
     3056    a = 42; 
     3057    assert(a.Any.isActive!real); 
     3058    a = "abc"; 
     3059    assert(a.Any.isActive!(const(char)[])); 
     3060} 
     3061 
     3062unittest 
     3063{ 
     3064    // foreach over input range 
     3065    Any!(string, wstring) str; 
     3066 
     3067    str = cast(string) "a"; 
     3068    assert(str.length == 1); 
     3069    assert(str.front == 'a'); 
     3070    str.popFront; 
     3071    assert(str.empty); 
     3072 
     3073    str = cast(wstring) "bc"; 
     3074    assert(str.length == 2); 
     3075    str.popFront; 
     3076    assert(str.front == 'c'); 
     3077    assert(!str.empty); 
     3078 
     3079    size_t i; 
     3080    str = "\u3067\u3043\u30fc"; 
     3081    foreach (e; str) 
     3082        assert(e == "\u3067\u3043\u30fc"d[i++]); 
     3083} 
     3084 
     3085unittest 
     3086{ 
     3087    // null dereference 
     3088    Any!(short[], int[]) x; 
     3089 
     3090    assert(fails(x.length)); 
     3091 
     3092    x = new int[4]; 
     3093    assert(x.length == 4); 
     3094 
     3095    x = typeof(x).init; 
     3096    assert(fails(x.length)); 
     3097} 
     3098 
     3099// operator overloads 
     3100 
     3101unittest 
     3102{ 
     3103    // opDispatch 
     3104    struct Tag { string op; int n; } 
     3105    struct OpEcho { 
     3106        Tag opDispatch(string op)(int n) { 
     3107            return Tag(op, n); 
     3108        } 
     3109    } 
     3110    Any!(OpEcho) obj; 
     3111 
     3112    obj = OpEcho(); 
     3113    auto r = obj.foo(42); 
     3114    assert(eq( r, Tag("foo", 42) )); 
     3115} 
     3116 
     3117unittest 
     3118{ 
     3119    // opAssign 
     3120    struct Tag { string op; int n; } 
     3121    struct OpEcho { 
     3122        int n; 
     3123        void opAssign(int n) { 
     3124            this.n = n; 
     3125        } 
     3126    } 
     3127    Any!(OpEcho) obj; 
     3128 
     3129    obj = OpEcho(); 
     3130    obj = 42; 
     3131    assert(obj.n == 42); 
     3132} 
     3133 
     3134unittest 
     3135{ 
     3136    // opUnary 
     3137    struct Tag { string op; } 
     3138    struct OpEcho { 
     3139        Tag opUnary(string op)() { 
     3140            return Tag(op); 
     3141        } 
     3142    } 
     3143    Any!(OpEcho) obj; 
     3144 
     3145    obj = OpEcho(); 
     3146    foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) 
     3147    { 
     3148        auto r = mixin(op ~ "obj"); 
     3149        assert(eq( r, Tag(op) )); 
     3150    } 
     3151} 
     3152 
     3153unittest 
     3154{ 
     3155    // opIndexUnary 
     3156    struct Tag { string op; int x; real y; } 
     3157    struct OpEcho { 
     3158        Tag opIndexUnary(string op)(int x, real y) { 
     3159            return Tag(op, x, y); 
     3160        } 
     3161    } 
     3162    Any!(OpEcho) obj; 
     3163 
     3164    obj = OpEcho(); 
     3165    foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) 
     3166    { 
     3167        auto r = mixin(op ~ "obj[4, 2.5]"); 
     3168        assert(eq( r, Tag(op, 4, 2.5) )); 
     3169    } 
     3170} 
     3171 
     3172unittest 
     3173{ 
     3174    // opSliceUnary 
     3175    struct Tag { string op; int x; real y; } 
     3176    struct OpEcho { 
     3177        Tag opSliceUnary(string op, int k = 2)(int x, real y) { 
     3178            return Tag(op, x, y); 
     3179        } 
     3180        Tag opSliceUnary(string op, int k = 0)() { 
     3181            return Tag(op, -1, -1); 
     3182        } 
     3183    } 
     3184    Any!(OpEcho) obj; 
     3185 
     3186    obj = OpEcho(); 
     3187    foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) 
     3188    { 
     3189        auto r = mixin(op ~ "obj[4 .. 5.5]"); 
     3190        assert(eq( r, Tag(op, 4, 5.5) )); 
     3191 
     3192        auto s = mixin(op ~ "obj[]"); 
     3193        assert(eq( s, Tag(op, -1, -1) )); 
     3194    } 
     3195} 
     3196 
     3197unittest 
     3198{ 
     3199    // opCast 
     3200    struct OpEcho { 
     3201        T opCast(T)() { return T.init; } 
     3202    } 
     3203    Any!(OpEcho) obj; 
     3204 
     3205    obj = OpEcho(); 
     3206    foreach (T; TypeTuple!(int, string, OpEcho)) 
     3207    { 
     3208        auto r = cast(T) obj; 
     3209        assert(is(typeof(r) == T)); 
     3210    } 
     3211} 
     3212 
     3213unittest 
     3214{ 
     3215    // opBinary, opBinaryRight 
     3216    struct LTag { string op; int v; } 
     3217    struct RTag { string op; int v; } 
     3218    struct OpEcho { 
     3219        LTag opBinary(string op)(int v) { 
     3220            return LTag(op, v); 
     3221        } 
     3222        RTag opBinaryRight(string op)(int v) { 
     3223            return RTag(op, v); 
     3224        } 
     3225    } 
     3226    Any!(OpEcho) obj; 
     3227 
     3228    obj = OpEcho(); 
     3229    foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", 
     3230                "|", "^", "<<", ">>", ">>>", "~"/+, "in" @@@BUG ??@@@+/)) 
     3231    { 
     3232        auto r = mixin("obj " ~ op ~ " 42"); 
     3233        assert(eq( r, LTag(op, 42) )); 
     3234        auto s = mixin("76 " ~ op ~ " obj"); 
     3235        assert(eq( s, RTag(op, 76) )); 
     3236    } 
     3237} 
     3238 
     3239unittest 
     3240{ 
     3241    // opEquals (forward) 
     3242    struct Dummy { 
     3243        bool opEquals(int v) const { 
     3244            return v > 0; 
     3245        } 
     3246        bool opEquals(ref const Dummy) const { assert(0); } 
     3247    } 
     3248    Any!(Dummy) obj; 
     3249 
     3250    obj = Dummy(); 
     3251    assert(obj ==  1); 
     3252    assert(obj !=  0); 
     3253    assert(obj != -1); 
     3254} 
     3255 
     3256unittest 
     3257{ 
     3258    // opEquals (meta) 
     3259    struct Dummy(int k) { 
     3260        bool opEquals(int kk)(ref const Dummy!kk rhs) const { 
     3261            return k == kk; 
     3262        } 
     3263    } 
     3264    Any!(Dummy!0, Dummy!1) a, b; 
     3265 
     3266    a = Dummy!0(); 
     3267    b = Dummy!1(); 
     3268    assert(a == a); 
     3269    assert(a != b); 
     3270    assert(b == b); 
     3271} 
     3272 
     3273unittest 
     3274{ 
     3275    // opCmp (forward) 
     3276    struct Dummy { 
     3277        int opCmp(int v) const { 
     3278            return 0 - v; 
     3279        } 
     3280        int opCmp(ref const Dummy) const { assert(0); } 
     3281    } 
     3282    Any!(Dummy) a; 
     3283 
     3284    a = Dummy(); 
     3285    assert(a >= 0); 
     3286    assert(a > -1); 
     3287    assert(a < 1); 
     3288} 
     3289 
     3290unittest 
     3291{ 
     3292    // opCmp (meta) 
     3293    struct Dummy(int k) { 
     3294        int opCmp(int kk)(ref const Dummy!kk r) const { 
     3295            return k - kk; 
     3296        } 
     3297    } 
     3298    Any!(Dummy!0, Dummy!1) a, b; 
     3299 
     3300    a = Dummy!0(); 
     3301    b = Dummy!1(); 
     3302    assert(a >= a); 
     3303    assert(a <= b); 
     3304    assert(a < b); 
     3305    assert(b >= a); 
     3306    assert(b <= b); 
     3307    assert(b > a); 
     3308} 
     3309 
     3310unittest 
     3311{ 
     3312    // opCall 
     3313    struct Tag { int x; real y; } 
     3314    struct OpEcho { 
     3315        Tag opCall(int x, real y) { 
     3316            return Tag(x, y); 
     3317        } 
     3318    } 
     3319    Any!(OpEcho) obj; 
     3320 
     3321//  obj = OpEcho(); // @@@BUG@@@ 
     3322    obj = OpEcho.init; 
     3323    auto r = obj(4, 8.5); 
     3324    assert(r == Tag(4, 8.5)); 
     3325} 
     3326 
     3327unittest 
     3328{ 
     3329    // opOpAssign 
     3330    struct Tag { string op; int v; } 
     3331    struct OpEcho { 
     3332        Tag opOpAssign(string op)(int v) { 
     3333            return Tag(op, v); 
     3334        } 
     3335    } 
     3336    Any!(OpEcho) obj; 
     3337 
     3338    obj = OpEcho(); 
     3339    foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", 
     3340                "|", "^", "<<", ">>", ">>>", "~")) 
     3341    { 
     3342        auto r = mixin("obj " ~ op ~ "= 97"); 
     3343        assert(eq( r, Tag(op, 97) )); 
     3344    } 
     3345} 
     3346 
     3347unittest 
     3348{ 
     3349    // opIndexOpAssign 
     3350    struct Tag { string op; int v; int x; real y; } 
     3351    struct OpEcho { 
     3352        Tag opIndexOpAssign(string op)(int v, int x, real y) { 
     3353            return Tag(op, v, x, y); 
     3354        } 
     3355    } 
     3356    Any!(OpEcho) obj; 
     3357 
     3358    obj = OpEcho(); 
     3359    foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", 
     3360                "|", "^", "<<", ">>", ">>>", "~")) 
     3361    { 
     3362        auto r = mixin("obj[4, 7.5] " ~ op ~ "= 42"); 
     3363        assert(eq( r, Tag(op, 42, 4, 7.5) )); 
     3364    } 
     3365} 
     3366 
     3367unittest 
     3368{ 
     3369    // opSliceOpAssign 
     3370    struct Tag { string op; int v; int i; real j; } 
     3371    struct OpEcho { 
     3372        Tag opSliceOpAssign(string op, int k = 2)(int v, int i, real j) { 
     3373            return Tag(op, v, i, j); 
     3374        } 
     3375        Tag opSliceOpAssign(string op, int k = 0)(int v) { 
     3376            return Tag(op, v, -1, -1); 
     3377        } 
     3378    } 
     3379    Any!(OpEcho) obj; 
     3380 
     3381    obj = OpEcho(); 
     3382    foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", 
     3383                "|", "^", "<<", ">>", ">>>", "~")) 
     3384    { 
     3385        auto r = mixin("obj[4 .. 7.5] " ~ op ~ "= 42"); 
     3386        assert(eq( r, Tag(op, 42, 4, 7.5) )); 
     3387 
     3388        auto s = mixin("obj[] " ~ op ~ "= 42"); 
     3389        assert(eq( s, Tag(op, 42, -1, -1) )); 
     3390    } 
     3391} 
     3392 
     3393unittest 
     3394{ 
     3395    // opIndex 
     3396    struct Tag { int i; real j; } 
     3397    struct OpEcho { 
     3398        Tag opIndex(int i, real j) { 
     3399            return Tag(i, j); 
     3400        } 
     3401    } 
     3402    Any!(OpEcho) obj; 
     3403 
     3404    obj = OpEcho(); 
     3405    auto r = obj[4, 9.5]; 
     3406    assert(eq( r, Tag(4, 9.5) )); 
     3407} 
     3408 
     3409unittest 
     3410{ 
     3411    // opSlice 
     3412    struct Tag { int i; real j; } 
     3413    struct OpEcho { 
     3414        Tag opSlice(int i, real j) { 
     3415            return Tag(i, j); 
     3416        } 
     3417        Tag opSlice() { 
     3418            return Tag(-1, -1); 
     3419        } 
     3420    } 
     3421    Any!(OpEcho) obj; 
     3422 
     3423    obj = OpEcho(); 
     3424    auto r = obj[4 .. 9.5]; 
     3425    assert(eq( r, Tag(4, 9.5) )); 
     3426 
     3427    auto s = obj[]; 
     3428    assert(eq( s, Tag(-1, -1) )); 
     3429} 
     3430 
     3431/+ 
     3432unittest 
     3433{ 
     3434    // opApply 
     3435    struct OpEcho { 
     3436        int opApply(int delegate(ref size_t, ref real) dg) 
     3437        { 
     3438            foreach (i, ref e; [ 1.L, 2.5L, 5.5L ]) 
     3439                if (auto r = dg(i, e)) 
     3440                    return r; 
     3441            return 0; 
     3442        } 
     3443    } 
     3444    Any!(OpEcho) obj; 
     3445 
     3446    obj = OpEcho(); 
     3447    foreach (size_t i, ref real e; obj) 
     3448        assert(e == [ 1.L, 2.5L, 5.5L ][i]); 
     3449} 
     3450+/ 
     3451