Thread overview
Range non-emptyness assertions and opIndex
Jul 01, 2016
Nordlöw
Jul 01, 2016
ag0aep6g
Jul 01, 2016
Nordlöw
Jul 01, 2016
Rene Zwanenburg
July 01, 2016
If I have a typical range definition like the following

    struct Range
    {
        @safe pure @nogc:

        bool empty() const nothrow
        {
            return _i == _j;
        }

        size_t length() const nothrow
        {
            return _j - _i;
        }

        bool front() const
        {
            assert(!empty);     // TODO use enforce when it's @nogc
            return _store[_i];
        }

        bool back() const
        {
            assert(!empty);     // TODO use enforce when it's @nogc
            return _store[_j - 1];
        }

        void popFront()
        {
            assert(!empty);
            ++_i;
        }

        void popBack()
        {
            assert(!empty);
            ++_i;
        }

    private:
        BitSet _store;          // copy of store
        size_t _i = 0;         // iterator into _store
        size_t _j = _store.length;
    }

What's the preferred way of reacting to emptyness in the members front, back, popFront, popBack --- using assert, enforce, throw, or simply relying on range-checking in the _store?

And what about using

    debug assert()

instead of

    assert()

typically when `_store`s `opIndex` already performs range-checking?

I think this is a important issue since asserts are not optimized away in release mode and D is very much about performance.
July 01, 2016
On 07/01/2016 12:35 PM, Nordlöw wrote:
> What's the preferred way of reacting to emptyness in the members front,
> back, popFront, popBack --- using assert, enforce, throw, or simply
> relying on range-checking in the _store?

I think assert is the most common way of checking. Simply ignoring the possibility is also common, as it's considered a programming error to call front/popFront/etc on an empty range.

For the same reason, throwing an exception (via throw or via enforce) is less common. Exceptions are more for input/environment errors, not so much for programming errors.

> And what about using
>
>      debug assert()
>
> instead of
>
>      assert()
>
> typically when `_store`s `opIndex` already performs range-checking?
>
> I think this is a important issue since asserts are not optimized away
> in release mode and D is very much about performance.

Huh? Asserts are ignored with -release. The only exception is assert(false) which terminates the program immediately, even with -release.

A little example program:

----
void main()
{
    import std.stdio;
    int x = 1;
    assert(x != 1);
    writeln("hi");
}
----

When compiled without -release, the program throws an AssertError. With -release it prints "hi".
July 01, 2016
On Friday, 1 July 2016 at 10:35:04 UTC, Nordlöw wrote:
> I think this is a important issue since asserts are not optimized away in release mode and D is very much about performance.

Asserts are removed in release mode, enforce isn't. Here's an example:

=====
void main(string[] args)
{
	assert(args.length >= 1);
}
=====

dmd testfile.d

This calls the assert function as expected:
=====
_Dmain:
  0000000000000000: 55                 push        rbp
  0000000000000001: 48 8B EC           mov         rbp,rsp
  0000000000000004: 48 83 39 01        cmp         qword ptr [rcx],1
  0000000000000008: 73 0E              jae         0000000000000018
  000000000000000A: B9 03 00 00 00     mov         ecx,3
  000000000000000F: 48 83 EC 20        sub         rsp,20h
  0000000000000013: E8 00 00 00 00     call        _D5test28__assertFiZv
  0000000000000018: 31 C0              xor         eax,eax
  000000000000001A: 5D                 pop         rbp
  000000000000001B: C3                 ret
=====

Now compiling in release mode:
dmd -release testfile.d

No assert in sight:
=====
_Dmain:
  0000000000000000: 55                 push        rbp
  0000000000000001: 48 8B EC           mov         rbp,rsp
  0000000000000004: 31 C0              xor         eax,eax
  0000000000000006: 5D                 pop         rbp
  0000000000000007: C3                 ret
=====
July 01, 2016
On Friday, 1 July 2016 at 11:35:40 UTC, ag0aep6g wrote:
> Huh? Asserts are ignored with -release. The only exception is assert(false) which terminates the program immediately, even with -release.

Aha, that's what I was testing against so therefore the confusion.