On Sunday, 15 May 2016 at 11:15:38 UTC, Joseph Rushton Wakeling wrote:
On Sunday, 15 May 2016 at 10:43:55 UTC, Joseph Rushton Wakeling wrote:
Probably the best way to handle this is to handle the take-the-address side of things by a @trusted wrapper that uses `return ref` to guarantee the pointer remains valid for the lifetime of the wrapper itself.

Note, I've been mulling over this myself for a while, so I'll probably put something together in a future dxorshift release (and probably try to get it in Phobos ASAP, as it will be very helpful in avoiding the worst cases of the existing RNG functionality).

Sneak preview to try out, before I tidy this up for actual inclusion in the library (needs docs, etc):

/////////////////////////////////////////////////////////////////////

module dxorshift.wrapper;

import std.random : isUniformRNG;

public struct SafeUniformRNG (BaseRNG)
    if (isUniformRNG!BaseRNG)
{
  public:
    this (return ref BaseRNG generator) @trusted
    {
        this.gen = &generator;
    }

    enum isUniformRandom = BaseRNG.isUniformRandom;

    bool empty() @property
    {
        return this.gen.empty;
    }

    auto front() @property
    {
        return this.gen.front;
    }

    auto popFront()
    {
        this.gen.popFront();
    }

    auto seed(Seed...)(Seed s)
    {
        this.gen.seed(s);
    }

  private:
    BaseRNG* gen;

    invariant()
    {
        assert(this.gen !is null);
    }
}

public auto uniformRNG(BaseRNG)(return ref BaseRNG generator)
{
    return SafeUniformRNG!BaseRNG(generator);
}

// test `uniformRNG`'s behavior with phobos RNGs
@safe unittest
{
    import std.array : array;
    import std.random : isUniformRNG, isSeedable, PseudoRngTypes;
    import std.range.primitives : isInputRange, isForwardRange;
    import std.range : take;

    foreach (RNG; PseudoRngTypes)
    {
        alias SafeRNG = SafeUniformRNG!RNG;

        static assert(isUniformRNG!SafeRNG);
        static assert(isSeedable!SafeRNG == isSeedable!RNG);

        static assert(isInputRange!SafeRNG);
        static assert(!isForwardRange!SafeRNG);

        // assuming RNG is seedable, we validate
        // expected differences between phobos
        // RNGs' normal behaviour and how they
        // behave when wrapped by `uniformRNG`
        static if (isSeedable!RNG)
        {
            RNG gen;
            gen.seed(123456);

            // if we pass any normal phobos RNG
            // directly into a range chain, it
            // will (sadly) be copied by value
            auto take1 = gen.take(10).array;
            auto take2 = gen.take(10).array;
            assert(take1 == take2);

            gen.seed(123456);

            // if however we wrap it with `uniformRNG`
            // it will be passed by reference
            auto safeGen = uniformRNG(gen);
            auto take3 = safeGen.take(10).array;
            auto take4 = safeGen.take(10).array;
assert(take3 == take1); // because we start from the same seed
            assert(take3 != take4);

            // validate we can however re-seed the
            // safely wrapped generator and get
            // the same results once again
            safeGen.seed(123456);
            auto take5 = safeGen.take(10).array;
            auto take6 = safeGen.take(10).array;
            assert(take5 == take3);
            assert(take6 == take4);
        }
    }
}

// validate it works with dxorshift RNGs
// and allows them to work in @safe code
@safe nothrow pure unittest
{
    import std.array : array;
    import std.meta : AliasSeq;
    import std.random : isUniformRNG, isSeedable;
    import std.range.primitives : isInputRange, isForwardRange;
    import std.range : take;

import dxorshift : SplitMix64, Xoroshiro128plus, Xorshift1024star;

foreach (RNG; AliasSeq!(SplitMix64, Xoroshiro128plus, Xorshift1024star))
    {
        alias SafeRNG = SafeUniformRNG!RNG;

        static assert(isUniformRNG!SafeRNG);
        static assert(isSeedable!SafeRNG);
        static assert(isInputRange!SafeRNG);
        static assert(!isForwardRange!SafeRNG);

        // dxorshift generators must be constructed
        // with a seed
        auto gen = RNG(123456);

        // we can't copy dxorshift RNGs by value,
        // and it's not safe to just take the
        // pointer address, so let's just jump
        // to wrapping them in `uniformRNG`
        auto safeGen = uniformRNG(gen);
        auto take1 = safeGen.take(10).array;
        auto take2 = safeGen.take(10).array;
        assert(take1 != take2);

        // re-seeding should give us the same
        // results once over
        gen.seed(123456);
        auto take3 = safeGen.take(10).array;
        auto take4 = safeGen.take(10).array;
        assert(take3 == take1);
        assert(take4 == take2);

        // re-seeding via the safe wrapper
        // should produce the same results
        safeGen.seed(123456);
        auto take5 = safeGen.take(10).array;
        auto take6 = safeGen.take(10).array;
        assert(take5 == take1);
        assert(take6 == take2);
    }
}
  • dxorshift: random numbe... Joseph Rushton Wakeling via Digitalmars-d-announce
    • Re: dxorshift: ran... Basile B. via Digitalmars-d-announce
      • Re: dxorshift:... Joseph Rushton Wakeling via Digitalmars-d-announce
        • Re: dxorsh... Joseph Rushton Wakeling via Digitalmars-d-announce
          • Re: dx... Joseph Rushton Wakeling via Digitalmars-d-announce
            • R... Basile B. via Digitalmars-d-announce
              • ... Joseph Rushton Wakeling via Digitalmars-d-announce
                • ... Basile B. via Digitalmars-d-announce
                • ... Joseph Rushton Wakeling via Digitalmars-d-announce
                • ... Joseph Rushton Wakeling via Digitalmars-d-announce
                • ... ag0aep6g via Digitalmars-d-announce
          • Re: dx... Joseph Rushton Wakeling via Digitalmars-d-announce
            • R... jmh530 via Digitalmars-d-announce
              • ... Kagamin via Digitalmars-d-announce
              • ... Joseph Rushton Wakeling via Digitalmars-d-announce

Reply via email to