Thread overview
trick to make throwing method @nogc
Feb 25, 2017
ikod
Feb 25, 2017
ikod
Feb 25, 2017
Eugene Wissner
Feb 25, 2017
Adam D. Ruppe
Feb 25, 2017
Eugene Wissner
Feb 25, 2017
ikod
Feb 25, 2017
Profile Anaysis
Feb 27, 2017
Guest
February 25, 2017
Hello,

I have a method for range:

struct Range {
    immutable(ubyte[]) _buffer;
    size_t             _pos;

    @property void popFront() pure @safe {
        enforce(_pos < _buffer.length, "popFront from empty buffer");
        _pos++;
    }
}

I'd like to have @nogc here, but I can't because enforce() is non-@nogc.
I have a trick but not sure if it is valid, especially I don't know if optimization will preserve code, used for throwing:

import std.string;

struct Range {
    immutable(ubyte[]) _buffer;
    size_t  _pos;

    this(immutable(ubyte[]) s) {
        _buffer = s;
    }
    @property void popFront() pure @safe @nogc {
        if (_pos >= _buffer.length ) {
            auto _ = _buffer[$]; // throws RangeError
        }
        _pos++;
    }
}

void main() {
	auto r = Range("1".representation);
	r.popFront();
	r.popFront(); // throws
}

Is it ok to use it? Is there any better solution?

Thanks!

February 25, 2017
On Saturday, 25 February 2017 at 19:59:29 UTC, ikod wrote:
> Hello,
>
> I have a method for range:
>
> struct Range {
>     immutable(ubyte[]) _buffer;
>     size_t             _pos;
>
>     @property void popFront() pure @safe {
>         enforce(_pos < _buffer.length, "popFront from empty buffer");
>         _pos++;
>     }
> }
>
> I'd like to have @nogc here, but I can't because enforce() is non-@nogc.
> I have a trick but not sure if it is valid, especially I don't know if optimization will preserve code, used for throwing:
>
> import std.string;
>
> struct Range {
>     immutable(ubyte[]) _buffer;
>     size_t  _pos;
>
>     this(immutable(ubyte[]) s) {
>         _buffer = s;
>     }
>     @property void popFront() pure @safe @nogc {
>         if (_pos >= _buffer.length ) {
>             auto _ = _buffer[$]; // throws RangeError
>         }
>         _pos++;
>     }
> }
>
> void main() {
> 	auto r = Range("1".representation);
> 	r.popFront();
> 	r.popFront(); // throws
> }
>
> Is it ok to use it? Is there any better solution?
>
> Thanks!

Found that I can use

    @property void popFront() pure @safe @nogc {
        if (_pos >= _buffer.length ) {
			assert(0, "popFront for empty range");
        }
        _pos++;
    }

which is both descriptive and can't be optimized out.

February 25, 2017
On Saturday, 25 February 2017 at 20:02:56 UTC, ikod wrote:
> On Saturday, 25 February 2017 at 19:59:29 UTC, ikod wrote:
>> Hello,
>>
>> I have a method for range:
>>
>> struct Range {
>>     immutable(ubyte[]) _buffer;
>>     size_t             _pos;
>>
>>     @property void popFront() pure @safe {
>>         enforce(_pos < _buffer.length, "popFront from empty buffer");
>>         _pos++;
>>     }
>> }
>>
>> I'd like to have @nogc here, but I can't because enforce() is non-@nogc.
>> I have a trick but not sure if it is valid, especially I don't know if optimization will preserve code, used for throwing:
>>
>> import std.string;
>>
>> struct Range {
>>     immutable(ubyte[]) _buffer;
>>     size_t  _pos;
>>
>>     this(immutable(ubyte[]) s) {
>>         _buffer = s;
>>     }
>>     @property void popFront() pure @safe @nogc {
>>         if (_pos >= _buffer.length ) {
>>             auto _ = _buffer[$]; // throws RangeError
>>         }
>>         _pos++;
>>     }
>> }
>>
>> void main() {
>> 	auto r = Range("1".representation);
>> 	r.popFront();
>> 	r.popFront(); // throws
>> }
>>
>> Is it ok to use it? Is there any better solution?
>>
>> Thanks!
>
> Found that I can use
>
>     @property void popFront() pure @safe @nogc {
>         if (_pos >= _buffer.length ) {
> 			assert(0, "popFront for empty range");
>         }
>         _pos++;
>     }
>
> which is both descriptive and can't be optimized out.

I made a test:

void main()
{
  assert(0);
}

it builds and doesn't throw if I compile with:
dmd -release

though it causes a segfault, what is probably a dmd bug. So I suppose it can be optimized out. And it isn't very discriptive, you probably throws the same AssertError for all errors. You can throw in @nogc code, you only have to allocate the exception not on the GC heap and free it after catching. I'm writing myself a library that is complete @nogc and I use exceptions this way:
- Allocate the exception
- throw
- catch
- free

A wrapper that unifies these 4 steps like enforce is pretty easy to implement.
February 25, 2017
On Saturday, 25 February 2017 at 20:40:26 UTC, Eugene Wissner wrote:
> it builds and doesn't throw if I compile with:
> dmd -release
> though it causes a segfault, what is probably a dmd bug.

No, that's by design. assert(0) compiles to a segfault instruction with -release.

> A wrapper that unifies these 4 steps like enforce is pretty easy to implement.

yeah easy to use exception in @nogc as long as the catch knows to free it too.
February 25, 2017
On Saturday, 25 February 2017 at 20:49:51 UTC, Adam D. Ruppe wrote:
> On Saturday, 25 February 2017 at 20:40:26 UTC, Eugene Wissner wrote:
>> it builds and doesn't throw if I compile with:
>> dmd -release
>> though it causes a segfault, what is probably a dmd bug.
>
> No, that's by design. assert(0) compiles to a segfault instruction with -release.
>
>> A wrapper that unifies these 4 steps like enforce is pretty easy to implement.
>
> yeah easy to use exception in @nogc as long as the catch knows to free it too.

But anyway segfault is not very descriptive :)
February 25, 2017
On Saturday, 25 February 2017 at 20:49:51 UTC, Adam D. Ruppe wrote:
>> A wrapper that unifies these 4 steps like enforce is pretty easy to implement.
>
> yeah easy to use exception in @nogc as long as the catch knows to free it too.

Alas, not my case. Exception can be catched not in my code.
February 25, 2017
On Saturday, 25 February 2017 at 19:59:29 UTC, ikod wrote:
> Hello,
>
> I have a method for range:
>
> struct Range {
>     immutable(ubyte[]) _buffer;
>     size_t             _pos;
>
>     @property void popFront() pure @safe {
>         enforce(_pos < _buffer.length, "popFront from empty buffer");
>         _pos++;
>     }
> }
>
> I'd like to have @nogc here, but I can't because enforce() is non-@nogc.
> I have a trick but not sure if it is valid, especially I don't know if optimization will preserve code, used for throwing:
>
> import std.string;
>
> struct Range {
>     immutable(ubyte[]) _buffer;
>     size_t  _pos;
>
>     this(immutable(ubyte[]) s) {
>         _buffer = s;
>     }
>     @property void popFront() pure @safe @nogc {
>         if (_pos >= _buffer.length ) {
>             auto _ = _buffer[$]; // throws RangeError
>         }
>         _pos++;
>     }
> }
>
> void main() {
> 	auto r = Range("1".representation);
> 	r.popFront();
> 	r.popFront(); // throws
> }
>
> Is it ok to use it? Is there any better solution?
>
> Thanks!

You can wrap a gc function in a nogc call using a function pointer that casts it to a nogc. You do this first by casting to void* then back to the same signature as the function + @nogc. This "tricks" the compiler in to calling the gc function from a nogc function. The problem is, of course, it is not safe if the gc is turned off as it will result in a memory leak. This may or may not be an issue with enforce depending on if it allocates before or after the check.



February 27, 2017
On Saturday, 25 February 2017 at 19:59:29 UTC, ikod wrote:
> Hello,
>
> I have a method for range:
>
> struct Range {
>     immutable(ubyte[]) _buffer;
>     size_t             _pos;
>
>     @property void popFront() pure @safe {
>         enforce(_pos < _buffer.length, "popFront from empty buffer");
>         _pos++;
>     }
> }
>
> I'd like to have @nogc here, but I can't because enforce() is non-@nogc.
> I have a trick but not sure if it is valid, especially I don't know if optimization will preserve code, used for throwing:
>
> import std.string;
>
> struct Range {
>     immutable(ubyte[]) _buffer;
>     size_t  _pos;
>
>     this(immutable(ubyte[]) s) {
>         _buffer = s;
>     }
>     @property void popFront() pure @safe @nogc {
>         if (_pos >= _buffer.length ) {
>             auto _ = _buffer[$]; // throws RangeError
>         }
>         _pos++;
>     }
> }
>
> void main() {
> 	auto r = Range("1".representation);
> 	r.popFront();
> 	r.popFront(); // throws
> }
>
> Is it ok to use it? Is there any better solution?
>
> Thanks!

solution 1/
===========

You can throw a static Exception.

auto staticEx(string msg, string file = __FILE__, size_t line = __LINE__)() @nogc
{
    immutable static Exception e = new Exception(msg, file, line);
    return e;
}

void main() @nogc
{
    throw staticEx!"bla";
}

not good for the call stack display, tho.

solution 2/
===========

Throw an Error. Errors shouldn't be caugth and consequently they lead to program termination so you don't care about the leak.


import std.experimental.allocator: make;
import std.experimental.allocator.mallocator: Mallocator;

void main() @nogc
{
    throw make!Error(Mallocator.instance, "bla");
}

good when errors are not designed to be caught. not good for exceptions because might leak to death depending on how the exceptions happens (i.e in a loop ouch).

solution 3/
===========

Reference counting. Not explored so far.

To finish, using a assert(0) is bad. assert(0) throws an error, it's really not like an Exception.