Re: Drawbacks of exceptions being globally allocated
On Sunday, 15 August 2021 at 03:45:07 UTC, Mathias LANG wrote: On Sunday, 15 August 2021 at 02:09:08 UTC, Tejas wrote: [...] You can't really have `@nogc` allocated Exception without circumventing the type system. Personally I gave up on `@nogc` just because of how inconvenient it is. [...] You think I should bother with ```gc_stats``` or stick to ```DRT-gcopt=verbose:2```? I'm trying to transpile the C++98 standard library so that we finally have a true `@nogc` standard alternative. C++98 and not C++11/14/17 because DIP 1040 is stuck in limbo. That is why I'm insisting on pure @nogc stuff
Re: Drawbacks of exceptions being globally allocated
On Sunday, 15 August 2021 at 02:09:08 UTC, Tejas wrote: On Sunday, 15 August 2021 at 00:15:32 UTC, Ali Çehreli wrote: On 8/14/21 4:41 AM, Tejas wrote: > [...] exception > [...] So, there would be many exception objects one for each place that an exception can be thrown. Functions like enforce() would have to take a reference to the exception object that is associated with that local scope. [...] I just want ```@nogc``` exceptions ;_; Please tell me that the ```GC.stats``` thing I posted is irrelevant so that I can go back to using ```emplace```. I wanted to allocate a class on the heap without being forced to use templates... but I guess that simply isn't possible(I know I can use ```mixin```, but that's even worse). You can't really have `@nogc` allocated Exception without circumventing the type system. Personally I gave up on `@nogc` just because of how inconvenient it is. Regarding the broader topic, at Sociomantic, we had pre-allocated Exception. After years working there, I grew to see the `throw new Exception` you see everywhere as an anti-pattern. Exceptions aren't convenient to use. At the very least, we should have a way to print a formatted message, however nothing currently offers this. A simple way to achieve this is the following: https://github.com/bosagora/agora/blob/113c89bd63048a7b98b8e9a2a664bd0eb08ebc84/source/agora/common/Ensure.d The gist of it is a statically allocated Exception (via module ctor) that is thrown by our `ensure` method. This is not `pure` unfortunately (because accessing a global mutable is not pure), and not `@nogc` (because `snformat` / `formattedWrite` aren't `@nogc`), but it doesn't allocate. As Ali mentioned, having a single Exception in the program breaks Exception chaining. But Exception chaining is one of the lowest ROI feature of D: It took a lot of effort to implement correctly, and is barely, if at all, used. There have even been talks (involving Walter) of deprecating it. If your goal is to never *link* in the GC, then you can barely use Phobos. If your goal is to never use the GC, I would say, why ? Using the GC for long-lived object and/or infrequent allocation doesn't hurt. GC collections are only triggered when you call new, so as long as you keep your critical path free of allocations, you're in the clear. Another little piece of code you might find interesting: https://github.com/sociomantic-tsunami/ocean/blob/adb31c84baa2061d07aaa0cb7a7d14c3cc98309b/src/ocean/core/Test.d#L305-L340 This uses a built-in method (`gc_stats`) which predates `GC.stats` and doesn't work in D2, but I'm sure you'll easily figure out how to adapt it :)
Re: Drawbacks of exceptions being globally allocated
On Sunday, 15 August 2021 at 02:09:08 UTC, Tejas wrote: On Sunday, 15 August 2021 at 00:15:32 UTC, Ali Çehreli wrote: On 8/14/21 4:41 AM, Tejas wrote: > [...] exception > [...] So, there would be many exception objects one for each place that an exception can be thrown. Functions like enforce() would have to take a reference to the exception object that is associated with that local scope. [...] I just want ```@nogc``` exceptions ;_; Please tell me that the ```GC.stats``` thing I posted is irrelevant so that I can go back to using ```emplace```. I wanted to allocate a class on the heap without being forced to use templates... but I guess that simply isn't possible(I know I can use ```mixin```, but that's even worse). In @nogc code, of course, like how for stack allocation there's simple ```scope c = new C(ctor args...)```, but to allocate on heap is ```auto c = heapAlloc!(A, ctor args...)```. I wanted to remove the ```!```
Re: Drawbacks of exceptions being globally allocated
On Sunday, 15 August 2021 at 00:15:32 UTC, Ali Çehreli wrote: On 8/14/21 4:41 AM, Tejas wrote: > [...] exception > [...] So, there would be many exception objects one for each place that an exception can be thrown. Functions like enforce() would have to take a reference to the exception object that is associated with that local scope. [...] I just want ```@nogc``` exceptions ;_; Please tell me that the ```GC.stats``` thing I posted is irrelevant so that I can go back to using ```emplace```. I wanted to allocate a class on the heap without being forced to use templates... but I guess that simply isn't possible(I know I can use ```mixin```, but that's even worse).
Re: Drawbacks of exceptions being globally allocated
On Saturday, 14 August 2021 at 23:14:51 UTC, Paul Backus wrote: On Saturday, 14 August 2021 at 15:58:17 UTC, Tejas wrote: If you're willing to help further, would you please tell me why there is a GC allocation in the code below that uses ```emplace```? Will such code truly work if GC is never linked in the program? https://run.dlang.io/is/XEc2WJ ``` onlineapp.d(26): Error: `@nogc` function `D main` cannot call non-@nogc function `core.memory.GC.stats` ``` Looks like `GC.stats` uses the GC. Then why is it (0,0,0) the first time it gets used? I actually stole this from [here](https://github.com/dlang/dmd/pull/8508#issuecomment-538310708) and assumed the author knew what he was doing. FWIW, using ```DRT-gcopt=verbose:2``` gives grand total GC time as 0 so maybe there is something up with ```GC.stats``` only.
Re: Drawbacks of exceptions being globally allocated
On 8/14/21 4:41 AM, Tejas wrote: > What is the drawback of the following "simple" ```@nogc``` exception > creation technique? > > ```d > import std; > void main()@nogc > { > try{ > __gshared a = new Exception("help"); > scope b = a; > throw b; > } > catch(Exception e){ > printf("caught"); > } > } > ``` So, there would be many exception objects one for each place that an exception can be thrown. Functions like enforce() would have to take a reference to the exception object that is associated with that local scope. I don't have a clear idea on whether it would work or whether it would be cumbersome to use or not. I wrote the following by misunderstanding you. I thought you you were proposing just one exception object for the whole program. I am still posting it because it is something I realized relatively recently. Even though this feature is probably never used, in D, multiple exception objects are chained. For example, you can throw e.g. in a destructor when there is an active exception in flight and that second object gets attached to the first one in linked list fashion. This may be useful in some cases but in general, these colatteral exceptions don't carry much information and I don't think anybody looks at them. Usually, the first one is the one that explains the error case. All such collateral exceptions are accessible through the Throwable.next function. However, even if D did not have such a feature and it had only a single exception that could be thrown (like in C++), the reality is, there can be infinite number of exceptions objects alive. This fact is true for C++ as well and this fact is one of the main reasons why exceptions are not allowed in safety-critical systems: When you can't limit the number of exception objects, you can't guarantee that the system will not run out of memory. Here is how even in C++ there can be infine exception objects. (Note: Yes, there is only one in flight but there is no limit on the number of caught exception objects that are alive.) try { foo(); } catch (Exception exc) { // Note: We caught the exception; so it's not in flight anymore bar(); // For the following to work, exc must be alive even after bar() writeln(exc.msg); } Now imagine bar() had a try-catch of its own where it caught another exception. During the execution of bar's catch clause, there are two exception objects alive. So, the problem with your proposal is, there is no room for the exception that was throw during bar's execution. Ali
Re: Drawbacks of exceptions being globally allocated
On Saturday, 14 August 2021 at 15:58:17 UTC, Tejas wrote: If you're willing to help further, would you please tell me why there is a GC allocation in the code below that uses ```emplace```? Will such code truly work if GC is never linked in the program? https://run.dlang.io/is/XEc2WJ ``` onlineapp.d(26): Error: `@nogc` function `D main` cannot call non-@nogc function `core.memory.GC.stats` ``` Looks like `GC.stats` uses the GC.
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 14:04:47 UTC, kdevel wrote: ~~~ char [7] d7 = "x"; // okay string s = "x"; char [7] c7 = s; // throws RangeError ~~~ What justifies that the compiler behaves differently on two terms ('s', '"x"') which are of equal size, type, length and value? Literals in D can have different types in different contexts. For example: ```d byte b = 16; // 16 is treated as a byte literal int n = 16; // 16 is treated as an int literal b = n; // Error: cannot convert int to byte ``` Similarly, the string literal `"x"` can be treated either as a `string` (a dynamic array of `immutable(char)`) or as a static array of `char`, depending on the type of variable it's assigned to.
Re: Anyway to achieve the following
``` struct S { int x = 1234; } void main() { import std.stdio; S s; //construction of a using &(s.x) auto a = Ref!(int)(); writeln(a); //displays 1234 s.x += 1; writeln(a); //displays 1235 a += 1; writeln(s.x); //displays 1236 } struct Ref(T) { T* ptr; this(T* p) { ptr = p; } string toString() { import std.conv; return to!string(*ptr); } ref T var() { return *ptr; } alias var this; } ```
Re: Looping over Template Types ... possible?
On Saturday, 14 August 2021 at 20:20:01 UTC, Stefan Koch wrote: On Saturday, 14 August 2021 at 20:07:21 UTC, james.p.leblanc wrote: mes it is possible look for `AliasSeq` in `std.meta` foreach(T; AliasSeq!(float, double)) { ... } Stefan, Thanks very much for your help here ... I had not understood that AliasSeq worked in this manner. I definitely need read the AliasSeq and try to understand how to use this. Best Regards, James
Re: Looping over Template Types ... possible?
On Saturday, 14 August 2021 at 20:07:21 UTC, james.p.leblanc wrote: Good Evening/Day, Suppose I have a number of function templates that each take two types, say S and T. I would like to exercise my routines over the combinations of types: set of all S: ( double[], float[], Complex!double[], Complex!float[]) set of all T: ( double, float) Is something along the line of the following sketch possible? foreach( s in S){ foreach( t in T){ foo!(S,T)( T x, S y); biz!(S,T)( T x, S y); } } I have done some searching for hints about this, but I perhaps my search terms are not very good. All hint and pointers thankfully received. Best Regards, James it is possible look for `AliasSeq` in `std.meta` foreach(T; AliasSeq!(float, double)) { ... }
Looping over Template Types ... possible?
Good Evening/Day, Suppose I have a number of function templates that each take two types, say S and T. I would like to exercise my routines over the combinations of types: set of all S: ( double[], float[], Complex!double[], Complex!float[]) set of all T: ( double, float) Is something along the line of the following sketch possible? foreach( s in S){ foreach( t in T){ foo!(S,T)( T x, S y); biz!(S,T)( T x, S y); } } I have done some searching for hints about this, but I perhaps my search terms are not very good. All hint and pointers thankfully received. Best Regards, James
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 14:04:47 UTC, kdevel wrote: On Saturday, 14 August 2021 at 13:01:13 UTC, frame wrote: I would say case [...] 3 is not [a bug]. It's just the element type conversion and mismatched lengths of the ranges. ~~~ char [7] d7 = "x"; // okay string s = "x"; char [7] c7 = s; // throws RangeError ~~~ What justifies that the compiler behaves differently on two terms ('s', '"x"') which are of equal size, type, length and value? One is pure initialization, CTFE magic for strings or something like that. The other one is an assignment of an already created range where length check kicks in. The compiler is fine with that as it compiles. It's the bound checker at runtime level that bails out.
Re: Drawbacks of exceptions being globally allocated
On Saturday, 14 August 2021 at 15:28:36 UTC, Alexandru Ermicioi wrote: On Saturday, 14 August 2021 at 13:24:22 UTC, Tejas wrote: ... I don't think there are any gotchas here. The problem with this technique, is when your exceptions aren't just simple labels but also carry some additional data, say for example specific error type, and subject that, caused this. In such cases you can't have a gloablly shared instance. Let's say it's doable but has lot's of drawbacks. Regards, Alexandru. Thank you. If you're willing to help further, would you please tell me why there is a GC allocation in the code below that uses ```emplace```? Will such code truly work if GC is never linked in the program? ```d import core.lifetime:emplace; import core.stdc.stdlib:malloc; int foo(int)@nogc { auto mem = cast(Exception)malloc(__traits(classInstanceSize, Exception)); auto memo = emplace!(Exception,string)(mem, "HOHOH"); //scope b = a; throw memo; } void test() @nogc { try { foo(1); } catch(Exception e) { } } void main() { import std.stdio; import core.memory; auto stats1 = GC.stats(); test(); auto stats2 = GC.stats(); writeln(stats1); writeln(stats2); } Output: Stats(0, 0, 0) Stats(1376, 1047200, 1360) ```
Re: How to extend the string class to return this inside the square bracket?
On Saturday, 14 August 2021 at 08:24:41 UTC, user1234 wrote: On Friday, 13 August 2021 at 23:33:05 UTC, Paul Backus wrote: On Friday, 13 August 2021 at 23:23:55 UTC, Marcone wrote: writeln("Hello World!"[x.indexOf("e")..x.indexOf("r")]); indexOf()is just a simple example, not the goal. I want handle literal inside [] like it bellow, but in literal: string x = "Hello World!"; writeln(x[x.indexOf("e")..x.indexOf("r")]); You can use the `pipe` function to bind an arbitrary expression to a variable: ```d import std.functional: pipe; import std.algorithm: countUntil; import std.stdio: writeln; "Hello world!" .pipe!(s => s[s.countUntil('e') .. s.countUntil('r')]) .writeln; ``` nice, that's the best alternative. Very Good!!! This pipe!() is just what I am looking for. Thank you very much
Re: Drawbacks of exceptions being globally allocated
On Saturday, 14 August 2021 at 13:24:22 UTC, Tejas wrote: ... I don't think there are any gotchas here. The problem with this technique, is when your exceptions aren't just simple labels but also carry some additional data, say for example specific error type, and subject that, caused this. In such cases you can't have a gloablly shared instance. Let's say it's doable but has lot's of drawbacks. Regards, Alexandru.
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 14:33:42 UTC, kdevel wrote: On Saturday, 14 August 2021 at 14:18:57 UTC, Tejas wrote: On Saturday, 14 August 2021 at 14:04:47 UTC, kdevel wrote: On Saturday, 14 August 2021 at 13:01:13 UTC, frame wrote: I would say case [...] 3 is not [a bug]. It's just the element type conversion and mismatched lengths of the ranges. ~~~ char [7] d7 = "x"; // okay string s = "x"; char [7] c7 = s; // throws RangeError ~~~ What justifies that the compiler behaves differently on two terms ('s', '"x"') which are of equal size, type, length and value? They are not. assert(string.sizeof != char.sizeof); The terms are s and "x": ~~~r.d void main () { string s = "x"; assert (s.sizeof == "x".sizeof); } ~~~ It did implicit conversion on the literal but not the variable. Basically, ```d char[7] a = cast(char[7])"x"; // works string s = "x"; char[7] b = cast(char[7])s; //fails ``` That's my best guess. Hopefully someone more experienced chimes in.
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 14:18:57 UTC, Tejas wrote: On Saturday, 14 August 2021 at 14:04:47 UTC, kdevel wrote: On Saturday, 14 August 2021 at 13:01:13 UTC, frame wrote: I would say case [...] 3 is not [a bug]. It's just the element type conversion and mismatched lengths of the ranges. ~~~ char [7] d7 = "x"; // okay string s = "x"; char [7] c7 = s; // throws RangeError ~~~ What justifies that the compiler behaves differently on two terms ('s', '"x"') which are of equal size, type, length and value? They are not. assert(string.sizeof != char.sizeof); The terms are s and "x": ~~~r.d void main () { string s = "x"; assert (s.sizeof == "x".sizeof); } ~~~
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 14:04:47 UTC, kdevel wrote: On Saturday, 14 August 2021 at 13:01:13 UTC, frame wrote: I would say case [...] 3 is not [a bug]. It's just the element type conversion and mismatched lengths of the ranges. ~~~ char [7] d7 = "x"; // okay string s = "x"; char [7] c7 = s; // throws RangeError ~~~ What justifies that the compiler behaves differently on two terms ('s', '"x"') which are of equal size, type, length and value? They are not. assert(string.sizeof != char.sizeof);
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 13:01:13 UTC, frame wrote: I would say case [...] 3 is not [a bug]. It's just the element type conversion and mismatched lengths of the ranges. ~~~ char [7] d7 = "x"; // okay string s = "x"; char [7] c7 = s; // throws RangeError ~~~ What justifies that the compiler behaves differently on two terms ('s', '"x"') which are of equal size, type, length and value?
Re: Drawbacks of exceptions being globally allocated
On Saturday, 14 August 2021 at 11:41:36 UTC, Tejas wrote: What is the drawback of the following "simple" ```@nogc``` exception creation technique? ```d import std; void main()@nogc { try{ __gshared a = new Exception("help"); scope b = a; throw b; } catch(Exception e){ printf("caught"); } } ``` I mean, there has to be a gotcha, some weird multi-threading case where this catastrophically breaks and is therefore not recommended, right? Otherwise, why can't we just use this instead of fretting with ```DIP 1008```? I even tried accessing the actual object in the code below, it didn't crash the program :D ```d import std; void main()@nogc { try{ __gshared a = new Exception("help"); scope b = a; throw b; } catch(Exception e){ printf("caught\n"); printf(cast(char*)e.msg);//new code } } ```
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 13:01:13 UTC, frame wrote: On Saturday, 14 August 2021 at 09:40:54 UTC, kdevel wrote: Shall I file them to bugzilla? I would say case 2 and 3 are not bugs. It's just the element type conversion and mismatched lengths of the ranges. Not sure about the first one. Yeah, I jumped the train :( Replace ```string``` with ```char``` and double quotes with single quotes then case 2 and 3 give the expected output 1 is 100% a bug, please report that.
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 09:40:54 UTC, kdevel wrote: Shall I file them to bugzilla? I would say case 2 and 3 are not bugs. It's just the element type conversion and mismatched lengths of the ranges. Not sure about the first one.
Re: partial initialization of fixed size ("static") arrays
On Saturday, 14 August 2021 at 09:40:54 UTC, kdevel wrote: Today I stumbled across three issues with partial initialization of "static" arrays: ~~~i1.d void main () { char [7] b = [ 1: 'x' ]; // okay char [7] a = [ 0: 'x' ]; // i1.d(4): Error: mismatched array lengths, 7 and 1 } ~~~ ~~~i2.d import std.stdio; void main () { char [7] c7 = [ 1: 'x' ]; writeln (cast (ubyte [7]) c7); // [255, 120, 255, 255, 255, 255, 255] okay char [7] d7 = "x"; writeln (cast (ubyte [7]) d7); // [120, 0, 0, 0, 0, 0, 0] would have expected // [120, 255, 255, 255, 255, 255, 255] } ~~~ ~~~i3.d // i3.d void main () { string s = "x"; static assert (is (typeof (s) == typeof ("x"))); assert (s == "x"); char [7] c7 = s; // throws RangeError } $ dmd -g i3 $ ./i3 core.exception.RangeError@i3.d(7): Range violation ??:? _d_arrayboundsp [...] i3.d:7 _Dmain [...] ~~~ Shall I file them to bugzilla? Static arrays really don't get any love :( I also recently filed a bug: https://issues.dlang.org/show_bug.cgi?id=22198 I think you should also file these ones. If they're wrong, it'll just get ```RESOLVED INVALID```. Even better, someone here can tell why the above mentioned behaviours aren't bugs and you can just do it yourself.
Drawbacks of exceptions being globally allocated
What is the drawback of the following "simple" ```@nogc``` exception creation technique? ```d import std; void main()@nogc { try{ __gshared a = new Exception("help"); scope b = a; throw b; } catch(Exception e){ printf("caught"); } } ```
Re: Create alias of same name in inner scope, bug or feature?
On Saturday, 14 August 2021 at 08:23:20 UTC, user1234 wrote: On Saturday, 14 August 2021 at 04:09:34 UTC, Tejas wrote: [...] Oh right, the ```.``` operator will reference variable in the _module_ scope, not just the _immediate outer scope_, you can use the module name to disambiguate as well. To extend Mike answer, the general rule is that if you can distinguish two names there's no shadowing. Understood
partial initialization of fixed size ("static") arrays
Today I stumbled across three issues with partial initialization of "static" arrays: ~~~i1.d void main () { char [7] b = [ 1: 'x' ]; // okay char [7] a = [ 0: 'x' ]; // i1.d(4): Error: mismatched array lengths, 7 and 1 } ~~~ ~~~i2.d import std.stdio; void main () { char [7] c7 = [ 1: 'x' ]; writeln (cast (ubyte [7]) c7); // [255, 120, 255, 255, 255, 255, 255] okay char [7] d7 = "x"; writeln (cast (ubyte [7]) d7); // [120, 0, 0, 0, 0, 0, 0] would have expected // [120, 255, 255, 255, 255, 255, 255] } ~~~ ~~~i3.d // i3.d void main () { string s = "x"; static assert (is (typeof (s) == typeof ("x"))); assert (s == "x"); char [7] c7 = s; // throws RangeError } $ dmd -g i3 $ ./i3 core.exception.RangeError@i3.d(7): Range violation ??:? _d_arrayboundsp [...] i3.d:7 _Dmain [...] ~~~ Shall I file them to bugzilla?
Re: Create alias of same name in inner scope, bug or feature?
On Saturday, 14 August 2021 at 04:09:34 UTC, Tejas wrote: [...] Oh right, the ```.``` operator will reference variable in the _module_ scope, not just the _immediate outer scope_, you can use the module name to disambiguate as well. To extend Mike answer, the general rule is that if you can distinguish two names there's no shadowing.
Re: How to extend the string class to return this inside the square bracket?
On Friday, 13 August 2021 at 23:33:05 UTC, Paul Backus wrote: On Friday, 13 August 2021 at 23:23:55 UTC, Marcone wrote: writeln("Hello World!"[x.indexOf("e")..x.indexOf("r")]); indexOf()is just a simple example, not the goal. I want handle literal inside [] like it bellow, but in literal: string x = "Hello World!"; writeln(x[x.indexOf("e")..x.indexOf("r")]); You can use the `pipe` function to bind an arbitrary expression to a variable: ```d import std.functional: pipe; import std.algorithm: countUntil; import std.stdio: writeln; "Hello world!" .pipe!(s => s[s.countUntil('e') .. s.countUntil('r')]) .writeln; ``` nice, that's the best alternative.