www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - trick to make throwing method nogc

reply ikod <geller.garry gmail.com> writes:
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!
Feb 25
next sibling parent reply ikod <geller.garry gmail.com> writes:
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.
Feb 25
parent reply Eugene Wissner <belka caraus.de> writes:
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.
Feb 25
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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.
Feb 25
next sibling parent Eugene Wissner <belka caraus.de> writes:
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 :)
Feb 25
prev sibling parent ikod <geller.garry gmail.com> writes:
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.
Feb 25
prev sibling next sibling parent Profile Anaysis <PA gotacha.com> writes:
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.
Feb 25
prev sibling parent Guest <esvogues nowhere.ch> writes:
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.
Feb 26