Thread overview
Supporting inout haystack in array-overload of findSplitBefore without template-bloat
Oct 27, 2019
Per Nordlöw
Oct 27, 2019
Per Nordlöw
Oct 27, 2019
Per Nordlöw
October 27, 2019
Is it possible to make this array-overload of findSplitBefore support `inout`-qualified `haystack` parameter and return type without using a templated `Result`?


auto findSplitBefore(T)(scope const T[] haystack, // TODO support inout?
                        scope const T needle)
{
    struct Result
    {
        private alias Haystack = typeof(haystack);
        private Haystack _haystack;
        private size_t _offset;

        inout(Haystack) pre() inout @trusted
        {
            return _haystack.ptr[0 .. _offset];
        }

        inout(Haystack) post() inout @trusted
        {
            if (_isMiss) { return _haystack[$ .. $]; }
            return _haystack.ptr[_offset .. _haystack.length];
        }

        bool opCast(T : bool)() const
        {
            return !_isMiss;
        }

        private bool _isMiss() const
        {
            return _haystack.length == _offset;
        }
    }

    foreach (const offset, const ref e; haystack)
    {
        if (e == needle)
        {
            return Result(haystack, offset);
        }
    }

    return Result(haystack, haystack.length);
}

///
@safe pure nothrow @nogc unittest
{
    const r = "a*b".findSplitBefore('*');
    assert(r);
    assert(r.pre == "a");
    assert(r.post == "*b");
}


This is my attempt so far


auto findSplitAfter(T)(scope inout(T)[] haystack, // TODO support inout?
                       scope const T needle) @trusted
{
    struct Result
    {
        private T[] _haystack;
        private size_t _offset;

        inout(T)[] pre() inout @trusted
        {
            if (_isMiss) { return _haystack[$ .. $]; }
            return _haystack.ptr[0 .. _offset + 1];
        }

        inout(T)[] post() inout @trusted
        {
            if (_isMiss) { return _haystack[0 .. $]; }
            return _haystack.ptr[_offset + 1 .. _haystack.length];
        }

        bool opCast(T : bool)() const @trusted
        {
            return !_isMiss;
        }

        private bool _isMiss() const @trusted
        {
            return _haystack.length == _offset;
        }
    }

    foreach (const offset, const ref e; haystack)
    {
        if (e == needle)
        {
            return inout(Result)(haystack, offset);
        }
    }

    return inout(Result)(haystack, haystack.length);
}

///
@safe pure nothrow @nogc unittest
{
    const r = "a*b".findSplitAfter('*');
    assert(r);
    assert(r.pre == "a*");
    assert(r.post == "b");
}


which results in the following interesting compiler error:


array_algorithm.d(506,12): Error: modify `inout` to `immutable` is not allowed inside `inout` function
    assert(r.pre == "a*");
           ^
array_algorithm.d(507,12): Error: modify `inout` to `immutable` is not allowed inside `inout` function
    assert(r.post == "b");
           ^
array_algorithm.d(514,5): Error: static assert:  `is(typeof(r.pre()) == const(char)[])` is false
    static assert(is(typeof(r.pre()) == const(char)[]));

October 27, 2019
On Sunday, 27 October 2019 at 14:29:01 UTC, Per Nordlöw wrote:
> which results in the following interesting compiler error:
>
>
> array_algorithm.d(506,12): Error: modify `inout` to `immutable` is not allowed inside `inout` function
>     assert(r.pre == "a*");
>            ^
> array_algorithm.d(507,12): Error: modify `inout` to `immutable` is not allowed inside `inout` function
>     assert(r.post == "b");
>            ^
> array_algorithm.d(514,5): Error: static assert:  `is(typeof(r.pre()) == const(char)[])` is false
>     static assert(is(typeof(r.pre()) == const(char)[]));


A non-templated and less bloated but still suboptimal solution is to defined inlined overloads for the member functions, this case `pre()`:


auto findSplitAfter_inout(T)(scope inout(T)[] haystack,
                             scope const T needle) @trusted
{
    static struct Result
    {
        private T[] _haystack;
        private size_t _offset;

        auto pre() @trusted
        {
            pragma(inline, true);
            if (_isMiss) { return _haystack[$ .. $]; }
            return _haystack.ptr[0 .. _offset + 1];
        }
        auto pre() const @trusted
        {
            pragma(inline, true);
            if (_isMiss) { return _haystack[$ .. $]; }
            return _haystack.ptr[0 .. _offset + 1];
        }
        auto pre() immutable @trusted
        {
            pragma(inline, true);
            if (_isMiss) { return _haystack[$ .. $]; }
            return _haystack.ptr[0 .. _offset + 1];
        }

        auto post() const @trusted
        {
            if (_isMiss) { return _haystack[0 .. $]; }
            return _haystack.ptr[_offset + 1 .. _haystack.length];
        }

        bool opCast(T : bool)() const
        {
            return !_isMiss;
        }

        private bool _isMiss() const
        {
            return _haystack.length == _offset;
        }
    }

    foreach (const offset, const ref e; haystack)
    {
        if (e == needle)
        {
            return inout(Result)(haystack, offset);
        }
    }

    return inout(Result)(haystack, haystack.length);
}

///
@safe pure nothrow @nogc unittest
{
    auto r = "a*b".findSplitAfter_inout('*');
    static assert(is(typeof(r.pre()) == string));
    assert(r);
    assert(r.pre == "a*");
    assert(r.post == "b");
}

October 27, 2019
On Sunday, 27 October 2019 at 14:57:29 UTC, Per Nordlöw wrote:
> @safe pure nothrow @nogc unittest
> {
>     auto r = "a*b".findSplitAfter_inout('*');
>     static assert(is(typeof(r.pre()) == string));
>     assert(r);
>     assert(r.pre == "a*");
>     assert(r.post == "b");
> }

Made it work! :)


/** Array-overload for `findSplitAfter` with default predicate.
 *
 * See_Also: https://forum.dlang.org/post/dhxwgtaubzbmjaqjmnmq@forum.dlang.org
 */
auto findSplitAfter(T)(scope inout(T)[] haystack, // TODO support inout? See_Also: https://forum.dlang.org/post/jtpchtddgenhjuwhqdsq@forum.dlang.org
                       scope const T needle) @trusted
{
    static struct Result
    {
        private T[] _haystack;
        private size_t _offset;

    pragma(inline, true):

        inout(T)[] pre() @trusted inout
        {
            if (_isMiss) { return _haystack[$ .. $]; }
            return _haystack.ptr[0 .. _offset + 1];
        }

        inout(T)[] post() @trusted inout
        {
            if (_isMiss) { return _haystack[0 .. $]; }
            return _haystack.ptr[_offset + 1 .. _haystack.length];
        }

        bool opCast(T : bool)() const
        {
            return !_isMiss;
        }

        private bool _isMiss() const
        {
            return _haystack.length == _offset;
        }
    }

    foreach (const offset, const ref e; haystack)
    {
        if (e == needle)
        {
            return inout(Result)(haystack, offset);
        }
    }

    return inout(Result)(haystack, haystack.length);
}

///
@safe pure nothrow @nogc unittest
{
    char[] haystack;
    auto r = haystack.findSplitAfter('*');
    static assert(is(typeof(r.pre()) == char[]));
    static assert(is(typeof(r.post()) == char[]));
}

///
@safe pure nothrow @nogc unittest
{
    const(char)[] haystack;
    auto r = haystack.findSplitAfter('*');
    static assert(is(typeof(r.pre()) == const(char)[]));
    static assert(is(typeof(r.post()) == const(char)[]));
}

///
@safe pure nothrow @nogc unittest
{
    auto r = "a*b".findSplitAfter('*');
    static assert(is(typeof(r.pre()) == string));
    static assert(is(typeof(r.post()) == string));
    assert(r);
    assert(r.pre == "a*");
    assert(r.post == "b");
}