Re: Class qualifier vs struct qualifier
On Wednesday, 13 June 2018 at 07:35:25 UTC, RazvanN wrote: Hello, I'm having a hard time understanding whether this inconsistency is a bug or intended behavior: immutable class Foo {} immutable struct Bar {} void main() { import std.stdio : writeln; Foo a; Bar b; writeln("typeof(a): ", typeof(a).stringof); writeln("typeof(b): ", typeof(b).stringof); } prints: typeof(Foo): Foo typeof(Bar): immutable(Bar) It seems like the class storage class is not taken into account which leads to some awkward situations like: immutable class Foo { this() {} } void main() { Foo a = new Foo(); // error: immutable method `this` is not callable using a // mutable object } To make it work I have to add immutable to both sides of the expression : immutable Foo a = new immutable Foo(); this is a wonder of redundancy. I already declared the class as immutable so it shouldn't be possible to have mutable instances of it (and it isn't), however I am forced to write the immutable twice even though it is pretty obvious that the class cannot be mutated. Just tested and I only seem to need to add the immutable to the left of the expression. https://run.dlang.io/is/EZ7es0 Also with the struct `Bar* b = new Bar();` works fine so i guess the discrepancy is because the class ref is mutatable.
Re: What is the point of nothrow?
On Thursday, 14 June 2018 at 19:06:07 UTC, Jonathan M Davis wrote: As I said, personally, I think that the program shut just print and terminate rather than throwing an Error. Walter seems to It makes perfectly sense for it to throw an error and not just print and terminate. This is especially true for server applications and especially server applications running in the cloud where you do not necessarily have access to a console, but you most likely will have access to a database. So in case of a thrown Error, you can catch it and log it to a database. If it was just printing it would be a lot hard to do such a task, especially when you take permission etc. into account. D would _never_ be fit for large scalable applications if it wasn't possible to retrieve the errors on any system and platform, no matter your limitations.
Create an Algebraic type at compile time and more
Hello, I wanted to know if such code was possible : alias Operation = Algebraic!(/* All type that implements X UDA */) struct X { int opcode; Operation h; } @X(0x01, Hello(3)) @X(0x02, Hello(4)) struct Hello { int Hello; } @X(0x03, Toto(5)) @X(0x05, Toto(6)) struct Toto { int A; } It has two problems, Operation not being defined while I use it in the UDA, and how to get back all the types that implements this UDA to give it to Algebraic. I don't know if it's a good idea or not, but might as well try the dark side of meta programming. Thanks a lot :)
Re: What is the point of nothrow?
On Thursday, June 14, 2018 18:11:20 wjoe via Digitalmars-d-learn wrote: > On Wednesday, 13 June 2018 at 20:08:06 UTC, Jonathan M Davis > wrote: > > On Wednesday, June 13, 2018 10:56:41 wjoe via > > The idea is that because your program is in an invalid state, > > attempting a graceful shutdown is unsafe. But regardless of > > whether you agree with that, the fact that nothrow doesn't do > > clean-up pretty much ensures that it isn't safe in the general > > case, and nothrow can't do clean-up without negating one of the > > main reasons that it exists in the first place - which is to > > that fact also means that a program which threw an Error cannot > be debugged anymore; because of invalid state you can neither > know, nor assume, that what you see in your debugger or core dump > is actually the state which led to the throw. As I said, personally, I think that the program shut just print and terminate rather than throwing an Error. Walter seems to have designed things from the premise that you could rerun the program to reproduce the problem (which in is usually true with the programs he works on). And in that case, simply getting the error message and stacktrace would be plenty. The problem is when you can't simply rerun the program to reproduce the problem and is why I'm of the opinion that printing and terminating would be better than throwing an Error. > > improve performance by not emitting exception-handling code. > > if performance matters so much I would use betterC. > > 'Improving' performance without profiling and such a trade-off is > a bad idea. If it were opt in - yes, I know I will get > undebuggable code on error thrown but I want or need the > performance gain, fair enough. But instead you are a victim of > that implementation whether you like it or not and you might not > even be aware about it. betterC really has nothing to do with performance. It just has to do with avoiding druntime so that you can have C code just link in and use the D code as if it were C (though using it would avoid the issue of Errors, since the runtime wouldn't be there to handle them). And again, Errors are intended for fatal cases, so most of the concerns about doing clean-up are irrelevant. Attempting to recover from an Error is considered to be like attempting to recover from a segfault, which is a terrible, terrible idea. And there are plenty of folks who want to be able to have their code be more performant when it's not using exceptions. So, as long as folks aren't trying to catch Errors, the fact that nothrow doesn't emit the exception handling code really isn't a problem. > Out of curiosity, in case of out of memory, will the program > crash trying to throw OutOfMemoryError or is it pre-allocated > along with the memory needed to format and print the message? OutOfMemoryError is pre-allocated. core.exception's onOutOfMemoryError is called rather than allocating a new OutOfMemoryError. > And out of curiosity again, where would I find this place where > the printing and terminating occurs ? I'd have to go digging in druntime. I don't know. I've probably seen it before, but it's not code that I've dealt with often enough to remember exactly where it is off the top of my head. > > Personally, I'm increasingly of the opinion that having Errors > > was a mistake and that the better solution would have been to > > just print out the error message and stack trace right there > > and immediately kill the program. That would then give you a > > coredump exactly where you were and avoid all of the confusion > > about Errors being thrown but not really doing proper clean-up. > > I don't know why Walter decided to do it the way he did, > > particularly since he's made it very clear that the entire > > point of Errors is to indicate a fatal error in the program, > > that doing any clean-up at that point is dangerous, and that > > the program needs to terminate, not try to recover. > > Aborting with an error code would be sufficient. No need to print > anything. > A backtrace can be obtained in the debugger. Given that a coredump isn't always generated (and in the case of Windows, I don't know if they even have an equivalent), printing out the same information that gets printed out now would often be desirable. It's just that if it kills the program right there rather than doing any unwinding of the stack, then the coredump is for the state of the program at the point of failure like it arguably should be. > > But I don't think that changing it so that Errors terminated > > immediately (and thus gave you a coredump for that spot in the > > program) would change what's an Error and what's an Exception. > > No it wouldn't but not everything declared or treated as an error > in D is one. > > There should be only a few and everything else should be an > Exception. There are no Errors in druntime or Phobos that I'm aware of where I would not consider it perfectly reasonable that they're Errors, but there are cases
Re: What is the point of nothrow?
On Wednesday, 13 June 2018 at 20:08:06 UTC, Jonathan M Davis wrote: On Wednesday, June 13, 2018 10:56:41 wjoe via Digitalmars-d-learn wrote: On Wednesday, 13 June 2018 at 03:14:33 UTC, Jonathan M Davis > regardless of whether the decision to treat failed memory > allocations as an Error was a good one or not, the fact > remains that as soon as an Error is thrown, you lose the > ability to deal with things cleanly, because full clean up > is not done when an Error is thrown (and can't be due to > things like how nothrow works). So, regardless of whether a > failed memory allocation is a condition that can be > recovered from in principle, the way that D handles GC > allocations make it unrecoverable in practice - at least as > far as GC-allocated memory is concerned. Did I get that right? You say when an error is thrown destructors, scope statements, etc. are not executed - if declared as nothrow because the exception handler mechanism stuff is missing, is that correct? And it does execute if not declared as nothrow but can't be relied upon because some other nothrow functions could have omitted some of those statements? Yes, and there's no guarantee that clean-up will occur even if nothing is nothrow. While the current implementation will do Good to know. The thought crossed my mind to remove nothrow from all functions... ;) clean-up in those cases, it didn't used to, and Walter has repeatedly stated that it is not guaranteed to do so. Errors are _not_ intended to be caught and recovered from. They're essentially a segfault that prints a message and stack trace but which for some reason uses the exception throwing mechanism to get to the code that prints the message and exits the program. So the program is in a stable enough state to run the exception handling mechanism to print a message and die, but not stable enough to run a memset or writePin(11,0) before continuing trampling forward to where the printing and dying happens ? So I shoot myself in the foot with nothrow and if I don't use it I'm still in a world of hurt? I understand the idea that an Error is not supposed to be caught but why would such a 'feature' be desirable? Where's the benefit if nothing can be relied upon ? If all errors would be treated like an exception, the developer could decide whether it is an error which needs to terminate right away or be able to handle the issue and continue or gracefully shutdown. The idea is that because your program is in an invalid state, attempting a graceful shutdown is unsafe. But regardless of whether you agree with that, the fact that nothrow doesn't do clean-up pretty much ensures that it isn't safe in the general case, and nothrow can't do clean-up without negating one of the main reasons that it exists in the first place - which is to that fact also means that a program which threw an Error cannot be debugged anymore; because of invalid state you can neither know, nor assume, that what you see in your debugger or core dump is actually the state which led to the throw. improve performance by not emitting exception-handling code. if performance matters so much I would use betterC. 'Improving' performance without profiling and such a trade-off is a bad idea. If it were opt in - yes, I know I will get undebuggable code on error thrown but I want or need the performance gain, fair enough. But instead you are a victim of that implementation whether you like it or not and you might not even be aware about it. Could even set a break point or force a core dump right there. The fact that Errors don't immediately kill the program and instead sort of unwind the stack is a definite problem for getting good debug information on crashes. I suspect that Walter doesn't quite understand the issues here, because whenever anyone raises issues related to stuff like this, he has a tendancy to basically tell people to rerun their program - which works fantastically in the world he typically programs in (compilers) but doesn't work very well with stuff like OSes or server programs where you frequently have no clue how to replicate the problem. My guess is that Errors work they do in part because he's not used to debugging situations where a coredump being created at the point of failure the first time it happens (rather than during attempts to reproduce the problem) is what you really need. Or any program which is interactive or where input is non deterministic. Out of curiosity, in case of out of memory, will the program crash trying to throw OutOfMemoryError or is it pre-allocated along with the memory needed to format and print the message? And out of curiosity again, where would I find this place where the printing and terminating occurs ? I think that the reasons that this is not a bigger problem than it is stem primarily from the fact that Errors are going to be very rare in production code unless it wasn'
Re: Static Array Idiom not working anymore.
On Thursday, June 14, 2018 08:40:10 Steven Schveighoffer via Digitalmars-d- learn wrote: > On 6/14/18 7:07 AM, Guillaume Piolat wrote: > > On Tuesday, 12 June 2018 at 15:35:42 UTC, Steven Schveighoffer wrote: > >> No, that's not what I mean. What I mean is: > >> > >> int[] arr = [1,2,3].s; > >> int[] arr2 = [4,5,6].s; > >> > >> Legally, the compiler is allowed to reuse the stack memory allocated > >> for arr for arr2. The lifetime of the arr data is over. > >> > >> -Steve > > > > https://github.com/p0nce/d-idioms/issues/150 > > > > Especially if the stdlib has a way to do this now. > > The Phobos PR isn't merged yet, so I think it's still valid to have this > idiom, but it needs to be changed such that the implicit slicing doesn't > happen. > > To be clear, I think the static array idiom is useful for many reasons > (my favorite is avoiding issues with literal and type size mismatch), it > just that the showcase usage leads to an instant dangling pointer and > should be altered. Yeah. The problem is not the function to create a static array. The problem is that the return value is being sliced rather than storing it on the stack and then slicing the local variable. Slicing the return value was never a safe thing to do, and if the compiler is now giving an error when you try to do it, that's actually a really good thing. - Jonathan M Davis
Re: Class qualifier vs struct qualifier
On Thursday, June 14, 2018 08:39:48 RazvanN via Digitalmars-d-learn wrote: > > Honestly, from what I understand of how this works, what I find > > weird is the struct case. immutable on classes does _not_ make > > the class itself immutable. It just makes all of its members > > immutable - hence the error about trying to allocate new Foo > > instead of new immutable Foo. So, that is exactly what I would > > expect. And honestly, being able to write Foo and have it imply > > immutable Foo would get _really_ confusing when reading and > > debugging code. > > Maybe it has something to do with structs being value types and > classes > being reference types. If you declare an immutable struct then > you cannot > modify it's value, while if you declare a class, the pointer to > the class > is mutable, while the class fields are not. This kind of makes > sense, however > the thing is that in both situations you are not able to mutate > any of the > class/struct fields no matter how you instantiate it, so it is > really redundant > to use `immutable` when creating the instance. Except that it isn't redundant, because without using immutable, it looks like you're trying to create a mutable object. Would you honestly ever look at something like Foo foo; and expect the object to be immutable? And sadly, looking at the type declaration isn't even necessarily enough to know that it was marked with immutable, because someone could have done something dumb like put immutable: higher up in the file. Types normally require that you explicitly mark them as immutable to make them immutable when using them anywhere - including declaring variables or allocating objects. I don't see why having made all of the members of the type immutable should change that. It's just extra magic that makes it harder to read the code. Sure, it would save you a little bit of typing when you do something like auto foo = new Foo; if makes it immutable for you, but it's at the cost of code clarity. - Jonathan M Davis
Re: foreach DFS/BFS for tree data-structure?
On Thursday, 14 June 2018 at 11:31:50 UTC, Robert M. Münch wrote: I have a simple tree C data-structure that looks like this: node { node parent: vector[node] children; } I would like to create two foreach algorthims, one follwing the breadth first search pattern and one the depth first search pattern. Is this possible? I read about Inputranges, took a look at the RBTree code etc. but don't relly know/understand where to start. While it's possible to do with input ranges, it's not pretty and I'm not sure that it's as performant as the traditional method. I would recommend going with one of the other suggestions in this thread.
Re: foreach DFS/BFS for tree data-structure?
On 6/14/18 8:35 AM, Robert M. Münch wrote: On 2018-06-14 11:46:04 +, Dennis said: On Thursday, 14 June 2018 at 11:31:50 UTC, Robert M. Münch wrote: Is this possible? I read about Inputranges, took a look at the RBTree code etc. but don't relly know/understand where to start. You can also use opApply to iterate over a tree using foreach, see: https://tour.dlang.org/tour/en/gems/opdispatch-opapply Ah... that looks very good. Need to dig a bit deeper on how to use it. Thanks. Just to clarify, RBTree can easily do DFS without a real stack because there are a finite number of children (2) for each node, and it's an O(1) operation to figure out which child the current node is in relation to the parent (am I a left child or right child?). Now, with your C version, if your children are stored in the array itself, figuring out the "next" child is a matter of doing &this + 1. But if you are storing pointers instead, then figuring out the next child would be an O(n) operation. Using the stack to track where you are (via opApply) is a valid way as well. You could also unroll that into a malloc'd stack, but the code is not as pretty of course. -Steve
Re: Static Array Idiom not working anymore.
On 6/14/18 7:07 AM, Guillaume Piolat wrote: On Tuesday, 12 June 2018 at 15:35:42 UTC, Steven Schveighoffer wrote: No, that's not what I mean. What I mean is: int[] arr = [1,2,3].s; int[] arr2 = [4,5,6].s; Legally, the compiler is allowed to reuse the stack memory allocated for arr for arr2. The lifetime of the arr data is over. -Steve https://github.com/p0nce/d-idioms/issues/150 Especially if the stdlib has a way to do this now. The Phobos PR isn't merged yet, so I think it's still valid to have this idiom, but it needs to be changed such that the implicit slicing doesn't happen. To be clear, I think the static array idiom is useful for many reasons (my favorite is avoiding issues with literal and type size mismatch), it just that the showcase usage leads to an instant dangling pointer and should be altered. -Steve
Re: foreach DFS/BFS for tree data-structure?
On 2018-06-14 11:46:04 +, Dennis said: On Thursday, 14 June 2018 at 11:31:50 UTC, Robert M. Münch wrote: Is this possible? I read about Inputranges, took a look at the RBTree code etc. but don't relly know/understand where to start. You can also use opApply to iterate over a tree using foreach, see: https://tour.dlang.org/tour/en/gems/opdispatch-opapply Ah... that looks very good. Need to dig a bit deeper on how to use it. Thanks. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Re: foreach DFS/BFS for tree data-structure?
On Thursday, 14 June 2018 at 11:31:50 UTC, Robert M. Münch wrote: Is this possible? I read about Inputranges, took a look at the RBTree code etc. but don't relly know/understand where to start. You can also use opApply to iterate over a tree using foreach, see: https://tour.dlang.org/tour/en/gems/opdispatch-opapply
Re: foreach DFS/BFS for tree data-structure?
On 14/06/2018 11:31 PM, Robert M. Münch wrote: I have a simple tree C data-structure that looks like this: node { struct Node { node parent: Node* parent; vector[node] children; Node[] children; } I would like to create two foreach algorthims, one follwing the breadth first search pattern and one the depth first search pattern. Here is (very roughly breadth): auto search(Method method) { struct Voldermort { Node parent; size_t offset; @property { Node front() { return parent.children[offset]; } bool empty() { return offset == parent.children.length; } } void popFront() { offset++; } } return Voldermort(this); } Depth will be a bit of a pain since you'll need to know where you have been at each set of children.
foreach DFS/BFS for tree data-structure?
I have a simple tree C data-structure that looks like this: node { node parent: vector[node] children; } I would like to create two foreach algorthims, one follwing the breadth first search pattern and one the depth first search pattern. Is this possible? I read about Inputranges, took a look at the RBTree code etc. but don't relly know/understand where to start. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Re: Static Array Idiom not working anymore.
On Tuesday, 12 June 2018 at 15:35:42 UTC, Steven Schveighoffer wrote: No, that's not what I mean. What I mean is: int[] arr = [1,2,3].s; int[] arr2 = [4,5,6].s; Legally, the compiler is allowed to reuse the stack memory allocated for arr for arr2. The lifetime of the arr data is over. -Steve https://github.com/p0nce/d-idioms/issues/150 Especially if the stdlib has a way to do this now.
Re: Class qualifier vs struct qualifier
Honestly, from what I understand of how this works, what I find weird is the struct case. immutable on classes does _not_ make the class itself immutable. It just makes all of its members immutable - hence the error about trying to allocate new Foo instead of new immutable Foo. So, that is exactly what I would expect. And honestly, being able to write Foo and have it imply immutable Foo would get _really_ confusing when reading and debugging code. Maybe it has something to do with structs being value types and classes being reference types. If you declare an immutable struct then you cannot modify it's value, while if you declare a class, the pointer to the class is mutable, while the class fields are not. This kind of makes sense, however the thing is that in both situations you are not able to mutate any of the class/struct fields no matter how you instantiate it, so it is really redundant to use `immutable` when creating the instance. What's bizarre is that marking the struct with immutable would affect anything other than its members. Bar b; should not claim that typeof(b) is immutable(Bar). b was not marked as immutable. It was listed as Bar, not immutable Bar. So, b shouldn't be immutable. - Jonathan M Davis