Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
February 25, 2017 trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
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 Re: trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
Posted in reply to ikod | 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 Re: trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
Posted in reply to ikod | 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 Re: trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
Posted in reply to Eugene Wissner | 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 Re: trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | 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 Re: trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | 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 Re: trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
Posted in reply to ikod | 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 Re: trick to make throwing method @nogc | ||||
---|---|---|---|---|
| ||||
Posted in reply to ikod | 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. |
Copyright © 1999-2021 by the D Language Foundation