Re: foreach / mutating iterator - How to do this?
On 2018-06-25 15:29:23 +, Robert M. Münch said: I have two foreach loops where the inner should change the iterator (append new entries) of the outer. foreach(a, candidates) { foreach(b, a) { if(...) candidates ~= additionalCandidate; } } The foreach docs state that the collection must not change during iteration. So, how to best handle such a situation then? Using a plain for loop? Answering myself: If you implement an opApply using for() or while() etc. with a mutating aggregate, foreach can be used indirectly with mutating aggregates. Works without any problems. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Re: Debugging compile time memory usage
On Tuesday, 26 June 2018 at 00:59:24 UTC, Nicholas Wilson wrote: On Sunday, 24 June 2018 at 14:16:26 UTC, Kamil Koczurek wrote: [...] If you were able to compile it with LDC and not DMD, then that is most likely due to the LDC executable being 64-bit (limited to available system RAM+swaps) and the DMD executable being 32-bit (limited to 4GB). If you want to use DMD, build a 64-bit version yourself and complain on general that the releases are not 64-bit. Is there a specific reason why DMD isn't shipped as 64bit by default? Literally most machines are 64bit these days, so why do we limit ourselves to 32bit?
Re: template sequence parameters treats member functions differently?
On Monday, 25 June 2018 at 18:59:37 UTC, Steven Schveighoffer wrote: On 6/25/18 2:51 PM, aliak wrote: On Monday, 25 June 2018 at 15:06:42 UTC, Steven Schveighoffer wrote: I don't see any reason why the alias is to the function and not the contexted function. I don't see how it's any different from the ones which use inner functions. I can only agree - me no see either. And having no clue as to how the compiler is implemented, I cannot even conjecture :) Well, it's worth an enhancement request in any case. -Steve doneo: https://issues.dlang.org/show_bug.cgi?id=19026
Re: Debugging compile time memory usage
On Sunday, 24 June 2018 at 14:16:26 UTC, Kamil Koczurek wrote: I recently wrote a brainfuck compiler in D, which loads the BF source at compile time, performs some (simple) optimizations, translates everything to D and puts it into the source code with a mixin. I did manage to get some pretty good performance, but for some programs in brainfuck I have to use LDC instead of DMD because the latter runs out of memory. Is there a way for me to optimize my code in such a way that DMD will be able to compile it? D code: https://pastebin.com/fg1bqwnd BF program that works: https://github.com/erikdubbelboer/brainfuck-jit/blob/master/mandelbrot.bf BF program that makes DMD crash: https://github.com/fabianishere/brainfuck/blob/master/examples/hanoi.bf After putting BF code in code.bf and D in main.d, I compile it with the following command: dmd main.d -J./ Error msg: unable to fork: Cannot allocate memory DMD version: DMD64 D Compiler v2.080.0-dirty If you were able to compile it with LDC and not DMD, then that is most likely due to the LDC executable being 64-bit (limited to available system RAM+swaps) and the DMD executable being 32-bit (limited to 4GB). If you want to use DMD, build a 64-bit version yourself and complain on general that the releases are not 64-bit.
Re: Can I parse this kind of HTML with arsd.dom module?
On Sunday, 24 June 2018 at 03:46:09 UTC, Dr.No wrote: to make it work. But if anyone else know how to fix this, will be very welcome too! try it now. thanks to Sandman83 on github.
Re: Nullable!T with T of class type
On Monday, June 25, 2018 19:40:30 kdevel via Digitalmars-d-learn wrote: > Just stumbled over the following design: > > class S {...} > > class R { > >Nullable!S s; > > } > > s was checked in code like > > R r; > > if (r.s is null) >throw new Exception ("some error message"); > > At runtime the following was caught: > > fatal error: caught Throwable: Called `get' on null Nullable!S > > Why can't this programming error be detected at compile time? If you have a function that accepts Nullable!T, when that function is called, how on earth is it going to know whether the argument it received was null at compile time? That depends entirely on the argument, which could have come from anywhere. So, in the general case, the compiler can't possibly determine whether a variable is null or not at compile time. And as far as just within a function goes, Nullable is a library type. There's nothing special about it. The compiler has no magic knowledge about what it does or how it functions. So, how would it know that R r; r.foo(); was bad code? And honestly, this sort of problem actually gets surprisingly thorny even if you tried to bake this into the compiler for a built-in type. Sure, it would be straightforward for the compiler to see that int* i; *i = 42; is dereferencing null, but as soon as you start adding if statements, function calls, etc. it quickly becomes impossible for the compiler to accurately determine whether the pointer is null or not. Any such attempt will inevitably end up with false positives, forcing you to do stuff like assign variables values when you know that it's unnecessary - it would complicate the compiler considerably to even make the attempt. It's far simpler to just treat it as a runtime error. Even more restricted languages such as Java do that. Java does try to force you to initialize stuff (resulting in annoying false positives at times), but in general, it still can't guarantee when a variable is null or not and is forced to insert runtime null checks. > Is it possible > to "lower" the Nullable operations if T is a class type such that > there > is only one level of nullification? It's been discussed before, but doing so would make the behavior of Nullable depend subtly on what type it contained and risks bugs in generic code. Also, there _is_ code out there which depends on the fact that you can have a Nullable!MyClass where the variable has a value and that value is a null reference. If you really don't want the extra bool, then just don't use Nullable. Class references are already naturally nullable. - Jonathan M Davis
Re: foreach / mutating iterator - How to do this?
On Monday, June 25, 2018 17:29:23 Robert M. Münch via Digitalmars-d-learn wrote: > I have two foreach loops where the inner should change the iterator > (append new entries) of the outer. > > foreach(a, candidates) { > foreach(b, a) { > if(...) candidates ~= additionalCandidate; > } > } > > The foreach docs state that the collection must not change during > iteration. > > So, how to best handle such a situation then? Using a plain for loop? Either that or create a separate array containing the elements you're adding and then append that to candidates after the loop has terminated. Or if all you're really trying to do is run an operation on a list of items, and in the process, you get more items that you want to operate on but don't need to keep them around afterwards, you could just wrap the operation in a function and use recursion. e.g. foreach(a, candidates) { doStuff(a); } void func(T)(T a) { foreach(b, a) { if(...) func(additionalCandidate); } } But regardless, you can't mutate something while you're iterating over it with foreach, so you're either going to have to manually control the iteration yourself so that you can do it in a way that guarantees that it's safe to add elements while iterating, or you're going to have to adjust what you're doing so that it doesn't need to add to the list of items while iterating over it. The big issue with foreach is that if it's iterating over is a range, then it copies it, and if it's not a range, it slices it (or if it defines opApply, that gets used). So, foreach(e; range) gets lowered to foreach(auto __c = range; !__c.empty; __c.popFront()) { auto e = __c.front; } which means that range is copied, and it's then unspecified behavior as to what happens if you try to use the range after passing it to foreach (the exact behavior depends on how the range is implemented), meaning that you really shouldn't be passing a range to foreach and then still do anything with it. If foreach is given a container, then it slices it, e.g. foreach(e; container) foreach(auto __c = container[]; !__c.empty; __c.popFront()) { auto e = __c.front; } so it doesn't run into the copying problem, but it's still not a good idea to mutate the container while iterating. What happens when you try to mutate the container while iterating over a range from that container depends on the container, and foreach in general isn't supposed to be able to iterate over something while it's mutated. Dynamic and associative arrays get different lowerings than generic ranges or containers, but they're also likely to run into problems if you try to mutate them while iterating over them. So, if using a normal for loop instead of foreach fixes your problem, then there you go. Otherwise, rearrange what you're doing so that it doesn't need to add anything to the original list of items in the loop. Either way, trying to mutate what you're iterating over is going to cause bugs, albeit slightly different bugs depending on what you're iterating over. - Jonathan M Davis
Nullable!T with T of class type
Just stumbled over the following design: class S {...} class R { : Nullable!S s; : } s was checked in code like R r; : if (r.s is null) throw new Exception ("some error message"); At runtime the following was caught: fatal error: caught Throwable: Called `get' on null Nullable!S Why can't this programming error be detected at compile time? Is it possible to "lower" the Nullable operations if T is a class type such that there is only one level of nullification? BTW: https://dlang.org/library/std/typecons/nullable.html contains duplicate sections: Function nullable Function nullable Struct Nullable Struct Nullable
Re: template sequence parameters treats member functions differently?
On 6/25/18 2:51 PM, aliak wrote: On Monday, 25 June 2018 at 15:06:42 UTC, Steven Schveighoffer wrote: I don't see any reason why the alias is to the function and not the contexted function. I don't see how it's any different from the ones which use inner functions. I can only agree - me no see either. And having no clue as to how the compiler is implemented, I cannot even conjecture :) Well, it's worth an enhancement request in any case. -Steve
Re: template sequence parameters treats member functions differently?
On Monday, 25 June 2018 at 15:06:42 UTC, Steven Schveighoffer wrote: On 6/24/18 5:19 PM, aliak wrote: [...] No, because the alias is an alias to the function, not the delegate. The act of taking the address creates the delegate, where the delegate's ptr is the context pointer (i.e. s), and the funcptr is the function that accepts the pointer (i.e. S.f). When you pass in s.f to an alias, you are actually passing in S.f. It's the fact that you are looking in the *namespace* of s when you do the alias. The &s.f is special for the compiler, and can't be deferred to later. Ahh, I see. Ah well. So not really much i can do here with this then I guess. Thanks for explaining though! BUT, I'm thinking this may be fixable, as it's inconsistent with inner functions: auto foo(alias x)() { return x(); } struct S { int bar() { return 42; } // int baz() { return foo!bar; } // nope } void main() { S s; int bar() { return 42; } assert(foo!bar() == 42); // ok // assert(foo!(s.bar) == 42); // nope int baz() { return s.bar; } assert(foo!baz() == 42); // ok! } I don't see any reason why the alias is to the function and not the contexted function. I don't see how it's any different from the ones which use inner functions. -Steve I can only agree - me no see either. And having no clue as to how the compiler is implemented, I cannot even conjecture :)
Futures
Hi, I am sure std.parallelism and vibe.d both have futures/channels/executors systems as there appears not to be anything in Phobos as a futures system. Or am I wrong here? What is needed is a futures system with a single threaded executor system that can be integrated with GtkD so as to make something for D akin to what is happening in gtk-rs. -- Russel. === Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Roadm: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk signature.asc Description: This is a digitally signed message part
Re: overload .
On Monday, 25 June 2018 at 15:39:09 UTC, Mr.Bingo wrote: On Monday, 25 June 2018 at 13:58:54 UTC, aliak wrote: A.x is translated in to A.opDispatch!"x" with no args. So I guess you can overload or you can static if on a template parameter sequence: import std.stdio; struct S { auto opDispatch(string name, Args...)(Args args) { static if (!Args.length) { return 3; } else { // set something } } } void main() { S s; s.x = 3; writeln(s.x); } Cheers, - Ali Ok, for some reason using two different templated failed but combining them in to one passes: auto opDispatch(string name, T)(T a) auto opDispatch(string name)() Maybe it is a bug in the compiler that it only checks one opDispatch? Two opDispatchs as in: import std.stdio: writeln; struct S { void opDispatch(string name, T)(T t) { writeln(t); } auto opDispatch(string name)() { writeln("ret"); return 4; } } void main() { S s; s.x; s.x = 4; } ?? The above seems to work fine. Or maybe you meant something else?
Re: Making sense of recursion
On 06/25/2018 07:45 PM, zbr wrote: void mergeSort(int[] arr, int l, int r) { if (l < r) // 1 { int m = l+(r-l)/2; // 2 mergeSort(arr, l, m); // 3 mergeSort(arr, m+1, r); // 4 merge(arr, l, m, r); // 5 } // 6 } // 7 mergeSort(arr, 0, 4); When I see this, I visualize the recursion to perform this way: mergeSort(arr, 0, 4): 0 < 4 ? true: mergeSort(0, 2): 0 < 2 ? true: mergeSort(0, 1): 0 < 1 ? true: mergeSort(0, 0): 0 < 0 ? false: //reach the end of mergeSort / reach line 6 and then 7 I don't see the computer ever reaching line 4 and 5? Obviously I'm wrong but where is my mistake? You seem to think that a recursive call takes over completely, and that the caller ceases to exist. That's not so. mergeSort does call "itself", but that means there's two active calls now. And when it calls "itself" again, there's three. And so on. When an inner call returns, the outer one resumes with the next line as usual. It's not just a list of recursive calls, it's a tree: mergeSort(0, 3) mergeSort(0, 1) // line 3 mergeSort(0, 0) // line 3 mergeSort(1, 1) // line 4 merge // line 5 mergeSort(2, 3) // line 4 mergesort(2, 2) // line 3 mergesort(3, 3) // line 4 merge // line 5 merge // line 5
Re: Making sense of recursion
On Monday, 25 June 2018 at 17:45:01 UTC, zbr wrote: Hi, this question is not specifically D related but I'll just ask anyway. Consider the following snippet: [...] Your mistake is in your visualization :-) But... more like: 0 < 4 ? true : mergeSort(0,2) && mergeSort(3, 4) And so on. I.e, the it's not either or to run the second mergeSort, they both happen.
Making sense of recursion
Hi, this question is not specifically D related but I'll just ask anyway. Consider the following snippet: void mergeSort(int[] arr, int l, int r) { if (l < r) // 1 { int m = l+(r-l)/2;// 2 mergeSort(arr, l, m); // 3 mergeSort(arr, m+1, r); // 4 merge(arr, l, m, r); // 5 }// 6 } // 7 mergeSort(arr, 0, 4); When I see this, I visualize the recursion to perform this way: mergeSort(arr, 0, 4): 0 < 4 ? true: mergeSort(0, 2): 0 < 2 ? true: mergeSort(0, 1): 0 < 1 ? true: mergeSort(0, 0): 0 < 0 ? false: //reach the end of mergeSort / reach line 6 and then 7 I don't see the computer ever reaching line 4 and 5? Obviously I'm wrong but where is my mistake? Thanks.
Re: foreach / mutating iterator - How to do this?
On Mon, Jun 25, 2018 at 05:29:23PM +0200, Robert M. Münch via Digitalmars-d-learn wrote: > I have two foreach loops where the inner should change the iterator > (append new entries) of the outer. > > foreach(a, candidates) { > foreach(b, a) { > if(...) candidates ~= additionalCandidate; > } > } > > The foreach docs state that the collection must not change during > iteration. > > So, how to best handle such a situation then? Using a plain for loop? [...] Yes. T -- The fact that anyone still uses AOL shows that even the presence of options doesn't stop some people from picking the pessimal one. - Mike Ellis
Re: Code failing unknown reason out of memory, also recursive types
On Monday, 25 June 2018 at 14:41:28 UTC, rikki cattermole wrote: Let me get this straight, you decided to max out your memory address space /twice over/ before you hit run time, and think that this would be a good idea? Well, that cause was suppose to allocate a dynamic array instead of a tuple. Somehow it got reverted. Works when allocating the dynamic array. How bout the compiler predict how big a variable is going to be allocated and if it exceeds memory then give an error instead of an out of memory error. If it would have gave me a line number I would have saw the problem immediately.
Re: overload .
On Monday, 25 June 2018 at 13:58:54 UTC, aliak wrote: On Monday, 25 June 2018 at 13:37:01 UTC, Mr.Bingo wrote: One can overload assignment and dispatch so that something like A.x = ... is valid when x is not a typical member but gets resolved by the above functions. Therefore, I can create a member for assignment. How can I create a member for getting the value? A.x = 3; // Seems to get translated in to A.opDispatch!("x")(3) works but foo(A.x); // fails and the compiler says x does not exist I need something consistent with opDot. I am trying to create "virtual"(not as in function) fields and I can only get assignment but not accessor. A.x is translated in to A.opDispatch!"x" with no args. So I guess you can overload or you can static if on a template parameter sequence: import std.stdio; struct S { auto opDispatch(string name, Args...)(Args args) { static if (!Args.length) { return 3; } else { // set something } } } void main() { S s; s.x = 3; writeln(s.x); } Cheers, - Ali Ok, for some reason using two different templated failed but combining them in to one passes: auto opDispatch(string name, T)(T a) auto opDispatch(string name)() Maybe it is a bug in the compiler that it only checks one opDispatch?
foreach / mutating iterator - How to do this?
I have two foreach loops where the inner should change the iterator (append new entries) of the outer. foreach(a, candidates) { foreach(b, a) { if(...) candidates ~= additionalCandidate; } } The foreach docs state that the collection must not change during iteration. So, how to best handle such a situation then? Using a plain for loop? -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Re: template sequence parameters treats member functions differently?
On 6/24/18 5:19 PM, aliak wrote: Hi, I'm having some issues with template sequence parameters, it seems they are not typed as delegates inside a template, but are outside. I.e. template T(V...) { alias T = typeof(&V[0]); } struct S { void f() {} } S s; pragma(msg, T!(s.f)); // void function() pragma(msg, typeof(&s.f)); // void delegate() How come the output is different? Is it supposed to be the same? No, because the alias is an alias to the function, not the delegate. The act of taking the address creates the delegate, where the delegate's ptr is the context pointer (i.e. s), and the funcptr is the function that accepts the pointer (i.e. S.f). When you pass in s.f to an alias, you are actually passing in S.f. It's the fact that you are looking in the *namespace* of s when you do the alias. The &s.f is special for the compiler, and can't be deferred to later. BUT, I'm thinking this may be fixable, as it's inconsistent with inner functions: auto foo(alias x)() { return x(); } struct S { int bar() { return 42; } // int baz() { return foo!bar; } // nope } void main() { S s; int bar() { return 42; } assert(foo!bar() == 42); // ok // assert(foo!(s.bar) == 42); // nope int baz() { return s.bar; } assert(foo!baz() == 42); // ok! } I don't see any reason why the alias is to the function and not the contexted function. I don't see how it's any different from the ones which use inner functions. -Steve
Re: Code failing unknown reason out of memory, also recursive types
Let me get this straight, you decided to max out your memory address space /twice over/ before you hit run time, and think that this would be a good idea?
Code failing unknown reason out of memory, also recursive types
import std.stdio; union Vector(T, size_t N = size_t.max) { import std.range, std.typecons, std.meta, std.algorithm, std.conv, std.math; static if (N == size_t.max) // For size_t.max sets N to be infinite/dynamic; { mixin("Tuple!("~"T,".repeat(N).join()~") data;"); @property size_t Length() { return rect.length; } @property double norm(size_t n = 2)() { return (iota(0,data.length).map!(a => data[a].pow(n))).pow(1/cast(double)n); } } else { mixin("Tuple!("~"T,".repeat(N).join()~") data;"); @property size_t Length() { return N; } @property double norm(size_t n = 2)() { mixin("return ("~(iota(0,N).map!(a => "data["~to!string(a)~"].pow(n)").join("+"))~").pow(1/cast(double)n);"); } } auto opDispatch(string s, Args...)(Args v) if (s.length > 1 && s[0] == 'x') { static if (N == size_t.max) if (data.length < to!int(s[1..$])) for(int i = 0; i < to!int(s[1..$]) - data.length; i++) data ~= 0; static if (Args.length == 0) mixin(`return data[`~s[1..$]~`];`); else static if (Args.length == 1) mixin(`data[`~s[1..$]~`] = v[0]; `); } alias data this; } void main() { import std.math, std.variant; Vector!(Algebraic!(Vector!int, int)) v; //v.x1 = 3; //v.x2 = 4; //v.x3 = 5; //writeln(v.x3); //writeln(v.norm); } Trying to create a vector of vectors where any entry can be another vector of vectors or an int.
Re: overload .
On Monday, 25 June 2018 at 13:37:01 UTC, Mr.Bingo wrote: One can overload assignment and dispatch so that something like A.x = ... is valid when x is not a typical member but gets resolved by the above functions. Therefore, I can create a member for assignment. How can I create a member for getting the value? A.x = 3; // Seems to get translated in to A.opDispatch!("x")(3) works but foo(A.x); // fails and the compiler says x does not exist I need something consistent with opDot. I am trying to create "virtual"(not as in function) fields and I can only get assignment but not accessor. A.x is translated in to A.opDispatch!"x" with no args. So I guess you can overload or you can static if on a template parameter sequence: import std.stdio; struct S { auto opDispatch(string name, Args...)(Args args) { static if (!Args.length) { return 3; } else { // set something } } } void main() { S s; s.x = 3; writeln(s.x); } Cheers, - Ali
Re: VisualD / fatal error C1905: Front-End and Back-End are not compatible (have to use the same processor)
With the latest releasae I still have the same problem. I really don't have any idea what the cause could be or how to fix it... Anyone? Viele Grüsse. Robert M. Münch On 2018-05-21 17:46:45 +, Robert M. Münch said: A project I can compile via the command line and dub, gives an error in VisualD. I created the VisualD configuration through dub: fatal error C1905: Front-End und Back-End sind nicht kompatibel (müssen den gleichenProzessor verwenden). This translates to: "Front-End and Back-End are not compatible (have to use the same processor)" Well, I don't have a clue what this should mena, nor how this could happen. It sounds a bit like if the compiler & linker are not useing the same architecture (I want to use x64) but I didn't find any options to check/change this. Any ideas? -- Robert M. Münch http://www.saphirion.com smarter | better | faster
overload .
One can overload assignment and dispatch so that something like A.x = ... is valid when x is not a typical member but gets resolved by the above functions. Therefore, I can create a member for assignment. How can I create a member for getting the value? A.x = 3; // Seems to get translated in to A.opDispatch!("x")(3) works but foo(A.x); // fails and the compiler says x does not exist I need something consistent with opDot. I am trying to create "virtual"(not as in function) fields and I can only get assignment but not accessor.
Re: Wrapping a forward range in another forward range
On Sunday, 24 June 2018 at 21:28:06 UTC, aliak wrote: On Sunday, 24 June 2018 at 20:33:32 UTC, Rudy Raab wrote: So I have an XLSX (MS Excel 2007+ file format) library that I wrote (https://github.com/TransientResponse/dlang-xlsx) that I recently converted from std.xml to dxml. That went well and it still works (much faster too). [...] I think it's the isSomeChar!(ElementType!R), not the isRandomAccessRange (because string isSomeString and !isSomeChar)? Cheers, - Ali Changing it to isSomeString!(ElementType!R) moves the error to my empty() function: ``` source\xlsx.d(205,22): Error: template std.range.primitives.empty cannot deduce function from argument types !()(XLSheet!(string[])), candidates are: C:\D\dmd2\windows\bin\..\..\src\phobos\std\range\primitives.d(2090,16): std.range.primitives.empty(T)(auto ref scope const(T) a) if (is(typeof(a.length) : size_t) || isNarrowString!T) ``` I tried implementing a length() function (the number of rows remaining in the range, which is known at runtime), but the error remains.
Re: Determine if CTFE or RT
On Monday, 25 June 2018 at 10:49:26 UTC, Simen Kjærås wrote: On Monday, 25 June 2018 at 09:36:45 UTC, Martin Tschierschke wrote: I am not sure that I understood it right, but there is a way to detect the status of a parameter: My question was different, but I wished to get a ctRegex! or regex used depending on the expression: import std.regex:replaceAll,ctRegex,regex; auto reg(alias var)(){ static if (__traits(compiles, {enum ctfeFmt = var;}) ){ // "Promotion" to compile time value enum ctfeReg = var ; pragma(msg, "ctRegex used"); return(ctRegex!ctfeReg); }else{ return(regex(var)); pragma(msg,"regex used"); } } } So now I can always use reg!("") and let the compiler decide. To speed up compilation I made an additional switch, that when using DMD (for development) alway the runtime version is used. The trick is to use the alias var in the declaration and check if it can be assigned to enum. The only thing is now, that you now always use the !() compile time parameter to call the function. Even, when in the end is translated to an runtime call. reg!("") and not reg("..."). Now try reg!("prefix" ~ var) or reg!(func(var)). This works in some limited cases, but falls apart when you try something more involved. It can sorta be coerced into working by passing lambdas: template ctfe(T...) if (T.length == 1) { import std.traits : isCallable; static if (isCallable!(T[0])) { static if (is(typeof({enum a = T[0]();}))) { enum ctfe = T[0](); } else { alias ctfe = T[0]; } } else { static if (is(typeof({enum a = T[0];}))) { enum ctfe = T[0]; } else { alias ctfe = T[0]; } } } string fun(string s) { return s; } unittest { auto a = ctfe!"a"; string b = "a"; auto c = ctfe!"b"; auto d = ctfe!("a" ~ b); // Error: variable b cannot be read at compile time auto e = ctfe!(() => "a" ~ b); auto f = ctfe!(fun(b)); // Error: variable b cannot be read at compile time auto g = ctfe!(() => fun(b)); } -- Simen This doesn't work, the delegate only hides the error until you call it. auto also does not detect enum. Ideally it should be a manifest constant if precomputed... this allows chaining of optimizations. auto x = 3; auto y = foo(x); the compiler realizes x is an enum int and then it can also precompute foo(x). Since it converts to a runtime type immediately it prevents any optimizations and template tricks.
Re: Determine if CTFE or RT
On Monday, 25 June 2018 at 09:36:45 UTC, Martin Tschierschke wrote: I am not sure that I understood it right, but there is a way to detect the status of a parameter: My question was different, but I wished to get a ctRegex! or regex used depending on the expression: import std.regex:replaceAll,ctRegex,regex; auto reg(alias var)(){ static if (__traits(compiles, {enum ctfeFmt = var;}) ){ // "Promotion" to compile time value enum ctfeReg = var ; pragma(msg, "ctRegex used"); return(ctRegex!ctfeReg); }else{ return(regex(var)); pragma(msg,"regex used"); } } } So now I can always use reg!("") and let the compiler decide. To speed up compilation I made an additional switch, that when using DMD (for development) alway the runtime version is used. The trick is to use the alias var in the declaration and check if it can be assigned to enum. The only thing is now, that you now always use the !() compile time parameter to call the function. Even, when in the end is translated to an runtime call. reg!("") and not reg("..."). Now try reg!("prefix" ~ var) or reg!(func(var)). This works in some limited cases, but falls apart when you try something more involved. It can sorta be coerced into working by passing lambdas: template ctfe(T...) if (T.length == 1) { import std.traits : isCallable; static if (isCallable!(T[0])) { static if (is(typeof({enum a = T[0]();}))) { enum ctfe = T[0](); } else { alias ctfe = T[0]; } } else { static if (is(typeof({enum a = T[0];}))) { enum ctfe = T[0]; } else { alias ctfe = T[0]; } } } string fun(string s) { return s; } unittest { auto a = ctfe!"a"; string b = "a"; auto c = ctfe!"b"; auto d = ctfe!("a" ~ b); // Error: variable b cannot be read at compile time auto e = ctfe!(() => "a" ~ b); auto f = ctfe!(fun(b)); // Error: variable b cannot be read at compile time auto g = ctfe!(() => fun(b)); } -- Simen
Re: Determine if CTFE or RT
On 06/25/2018 07:47 AM, Mr.Bingo wrote: The docs say that CTFE is used only when explicit, I was under the impression that it would attempt to optimize functions if they could be computed at compile time. The halting problem has nothing to do with this. The ctfe engine already complains when one recurses to deep, it is not difficult to have a time out function that cancels the computation within some user definable time limit... and since fail can simply fall through and use the rtfe, it is not a big deal. The problem then, if D can't arbitrarily use ctfe, means that there should be a way to force ctfe optionally! A D compiler is free to precompute whatever it sees fit, as an optimization. It's just not called "CTFE" then, and `__ctfe` will be false during that kind of precomputation. For example, let's try compiling this code based on an earlier example of yours: int main() { return foo(3) + foo(8); } int foo(int i) { return __ctfe && i == 3 ? 1 : 2; } `dmd -O -inline` compiles that to: <_Dmain>: 0: 55 push rbp 1: 48 8b ecmovrbp,rsp 4: b8 04 00 00 00 moveax,0x4 9: 5d poprbp a: c3 ret As expected, `ldc2 -O` is even smarter: <_Dmain>: 0: b8 04 00 00 00 moveax,0x4 5: c3 ret Both compilers manage to eliminate the calls to `foo`. They have been precomputed. `__ctfe` was false, though, because the term "CTFE" only covers the forced/guaranteed kind of precomputation, not the optimization.
Re: Determine if CTFE or RT
On Monday, 25 June 2018 at 08:05:53 UTC, Mr.Bingo wrote: On Monday, 25 June 2018 at 07:02:24 UTC, Jonathan M Davis wrote: On Monday, June 25, 2018 05:47:30 Mr.Bingo via Digitalmars-d-learn wrote: The problem then, if D can't arbitrarily use ctfe, means that there should be a way to force ctfe optionally! If you want to use CTFE, then give an enum the value of the expression you want calculated. If you want to do it in place, then use a template such as template ctfe(alias exp) { enum ctfe = exp; } so that you get stuff like func(ctfe!(foo(42))). I would be extremely surprised if the compiler is ever changed to just try CTFE just in case it will work as an optimization. That would make it harder for the programmer to understand what's going on, and it would balloon compilation times. If you want to write up a DIP on the topic and argue for rules on how CTFE could and should function with the compiler deciding to try CTFE on some basis rather than it only being done when it must be done, then you're free to do so. https://github.com/dlang/DIPs But I expect that you will be sorely disappointed if you ever expect the compiler to start doing CTFE as an optimization. It's trivial to trigger it explicitly on your own, and compilation time is valued far too much to waste it on attempting CTFE when in the vast majority of cases, it's going to fail. And it's worked quite well thus far to have it work only cases when it's actually needed - especially with how easy it is to make arbitrary code run during CTFE simply by doing something like using an enum. - Jonathan M Davis You still don't get it! It is not trivial! It is impossible to trigger it! You are focused far too much on the optimization side when it is only an application that takes advantage of the ability for rtfe to become ctfe when told, if it is possible. I don't know how to make this any simpler, sorry... I guess we'll end it here. I am not sure that I understood it right, but there is a way to detect the status of a parameter: My question was different, but I wished to get a ctRegex! or regex used depending on the expression: import std.regex:replaceAll,ctRegex,regex; auto reg(alias var)(){ static if (__traits(compiles, {enum ctfeFmt = var;}) ){ // "Promotion" to compile time value enum ctfeReg = var ; pragma(msg, "ctRegex used"); return(ctRegex!ctfeReg); }else{ return(regex(var)); pragma(msg,"regex used"); } } } So now I can always use reg!("") and let the compiler decide. To speed up compilation I made an additional switch, that when using DMD (for development) alway the runtime version is used. The trick is to use the alias var in the declaration and check if it can be assigned to enum. The only thing is now, that you now always use the !() compile time parameter to call the function. Even, when in the end is translated to an runtime call. reg!("") and not reg("...").
Re: Determine if CTFE or RT
On Monday, 25 June 2018 at 07:02:24 UTC, Jonathan M Davis wrote: On Monday, June 25, 2018 05:47:30 Mr.Bingo via Digitalmars-d-learn wrote: The problem then, if D can't arbitrarily use ctfe, means that there should be a way to force ctfe optionally! If you want to use CTFE, then give an enum the value of the expression you want calculated. If you want to do it in place, then use a template such as template ctfe(alias exp) { enum ctfe = exp; } so that you get stuff like func(ctfe!(foo(42))). I would be extremely surprised if the compiler is ever changed to just try CTFE just in case it will work as an optimization. That would make it harder for the programmer to understand what's going on, and it would balloon compilation times. If you want to write up a DIP on the topic and argue for rules on how CTFE could and should function with the compiler deciding to try CTFE on some basis rather than it only being done when it must be done, then you're free to do so. https://github.com/dlang/DIPs But I expect that you will be sorely disappointed if you ever expect the compiler to start doing CTFE as an optimization. It's trivial to trigger it explicitly on your own, and compilation time is valued far too much to waste it on attempting CTFE when in the vast majority of cases, it's going to fail. And it's worked quite well thus far to have it work only cases when it's actually needed - especially with how easy it is to make arbitrary code run during CTFE simply by doing something like using an enum. - Jonathan M Davis You still don't get it! It is not trivial! It is impossible to trigger it! You are focused far too much on the optimization side when it is only an application that takes advantage of the ability for rtfe to become ctfe when told, if it is possible. I don't know how to make this any simpler, sorry... I guess we'll end it here.
Re: Determine if CTFE or RT
On Monday, June 25, 2018 05:47:30 Mr.Bingo via Digitalmars-d-learn wrote: > The problem then, if D can't arbitrarily use ctfe, means that > there should be a way to force ctfe optionally! If you want to use CTFE, then give an enum the value of the expression you want calculated. If you want to do it in place, then use a template such as template ctfe(alias exp) { enum ctfe = exp; } so that you get stuff like func(ctfe!(foo(42))). I would be extremely surprised if the compiler is ever changed to just try CTFE just in case it will work as an optimization. That would make it harder for the programmer to understand what's going on, and it would balloon compilation times. If you want to write up a DIP on the topic and argue for rules on how CTFE could and should function with the compiler deciding to try CTFE on some basis rather than it only being done when it must be done, then you're free to do so. https://github.com/dlang/DIPs But I expect that you will be sorely disappointed if you ever expect the compiler to start doing CTFE as an optimization. It's trivial to trigger it explicitly on your own, and compilation time is valued far too much to waste it on attempting CTFE when in the vast majority of cases, it's going to fail. And it's worked quite well thus far to have it work only cases when it's actually needed - especially with how easy it is to make arbitrary code run during CTFE simply by doing something like using an enum. - Jonathan M Davis