Re: called copy constructor in foreach with ref on Range
On Tue, Jun 23, 2020 at 03:25:55AM +, Stanislav Blinov via Digitalmars-d-learn wrote: > On Tuesday, 23 June 2020 at 02:41:55 UTC, Jonathan M Davis wrote: > > > As things stand, uncopyable ranges aren't really a thing, and common > > range idiomns rely on ranges being copyable. > > Which idioms are those? I mean, genuine idioms, not design flaws like > e.g. references. > > > We'd need some major redesigning to make uncopyable ranges work, and > > personally, I don't think that it's worth the trouble. > > Of course we would. Andrei is entertaining changing the whole input > range API. Though he, like you, seems opposed to the idea of > uncopyables. [...] I'm also wondering what's the motivation behind supporting non-copyable ranges, and whether it's worth the effort and inevitable complications to support it if it's a rare use-case. Do you have any compelling use-case in mind, that might make this worthwhile? T -- Любишь кататься - люби и саночки возить.
Flagging special conditions on return from a function call
Is there a preferred idiom in D for flagging special conditions when returning from a function call? Here "special conditions" refers to expected situations (e.g. reaching the end of something, like EOF) rather than outright errors (so exception-try-catch is inappropriate). I've come across many ways for a function to return both a value and flag, including: (1) Assign an unused value for the flag (e.g. -1 when the function returns an int), and return the combined value/flag. (2) Return a tuple with the value and the flag (3) Return a struct or tuple with named value and flag members (4) Set the function return value normally, and put the flag in an "out" variable passed as an argument to the function (5) Return the flag, and put the value in an "out" variable passed to the function (i.e. the reverse of #4) (6) Use two separate functions, one that returns the value, and another that can be called afterwards to check the flag (like eof(), for example) (7) Use a side effect and set a designated global variable I'm sure there are others. * Is there a preferred approach? * Which ones are discouraged? * General recommendations or guidelines? If there is a best practice, I'd rather learn it sooner than many lines of code later. (In the interest of brevity, I'll limit my own comments in this post to the following: In the past, I've tried to adhere to KISS. This means that I would generally prefer #1. But often it isn't possible to combine the value and flag that way, in which case one of the other methods must be used.)
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On 6/22/20 2:37 PM, mw wrote: > On Monday, 22 June 2020 at 20:58:58 UTC, Ali Çehreli wrote: > >> Others have other explanations for this but my understanding is about >> exception safety: If it changed internal state and returned the front >> object, you would no be able to make a function like popFront() >> strongly exception safe. (There are ample documentation for this topic >> in C++ circles.) > > That's one consideration. But, it will be more interesting in knowing > (based on actual usage): > > (a) how many bugs % are due to exception un-safe I am not claiming that strong exception safety is the reason for Phobos design. However, knowing what I know, I would use the same primitive operations. It's the same with e.g. C++ as well: != end(), operator*, operator++. And operator++ does not return the current object either. Even if zero bugs are due to exception un-safe, a library designer would not oversee that knowledge. It is impossible to make an interface strongly exception safe but the reverse is always possible. > (b) how many bugs % are due to front / popFront separation? I made the mistake of forgetting to call popFront() perhaps 10 times and got stuck in an infinite loop and quickly hit a segmentation fault and that was it. > And which is more bug-prone for a typical programmer? my gut feeling is > (b), at least we just saw one in this thread. > > > And > > -- loop thru a static structure content like a simple array, why we need > to worry about exception safe? *If* strong exception guarantee is needed, it doesn't matter whether it's a simple array or not. > -- loop thru dynamically generated content, esp. network or more > external complex structure may worth considering exception safety. But > even there, do people always need to call !range.empty() check first? > when it's not empty, how much more exception un-safety that > popAndReturnFront() can introduce than the combination of `front(); ...; > popFront();"? It has been demonstrated on a Stack type that conflating top() and pop() cannot be made strongly exception safe. It's the same with front() and popFront(). > And why not provide a popAndReturnFront(), and let the user decide based > on his/her own actual usage? Because if the primitives were empty() and popAndReturnFront(), then it wouldn't be made strongly exception safe. With the current primitives of empty(), front(), and popFront(), it's possible to implement popAndReturnFront(). I like the following one that I wrote in three minutes. :) import std.stdio; auto popAndReturnFront(R)(ref R range) { import std.range : empty, front, popFront, ElementType; import std.typecons : Nullable, nullable; if (range.empty) { return Nullable!(ElementType!R)(); } scope (success) range.popFront(); return nullable(range.front); } void main() { auto range = [ 1, 2, 3 ]; while (true) { auto front = range.popAndReturnFront; if (!front.get()) { break; } writeln(front); } } >> Another reason is cohesion: We want functions to have as little >> responsibility as possible (ideally single). > > Again we have to weight which way is more bug-prone, any actual > statistics on the above (a) v.s (b)? I am not aware of any bugs related to the separation of front() and popFront(). Ali
Re: called copy constructor in foreach with ref on Range
On Monday, June 22, 2020 9:25:55 PM MDT Stanislav Blinov via Digitalmars-d- learn wrote: > On Tuesday, 23 June 2020 at 02:41:55 UTC, Jonathan M Davis wrote: > > As things stand, uncopyable ranges aren't really a thing, and > > common range idiomns rely on ranges being copyable. > > Which idioms are those? I mean, genuine idioms, not design flaws > like e.g. references. It is extremely common to wrap ranges in other ranges (and in fact, you basically have to in order to have lazy ranges). That really doesn't work very well - if at all - if you can't copy the range. It might be possible with a bunch of explicit calls to move, but that would result in range-based code in general being @system without a clean way to use @trusted, since whether it's really safe to mark such moves with @trusted would depend on the specific range implementation, which then is a big problem with generic code. Regardless of whether it's actually possible to make it work though, it's a complete mess in comparison to simply copying. And the fact that chaining range-based calls is extremely common makes the problem that much worse. The way that ranges are routinely passed around and wrapped works as well as it does, because ranges are copyable. > > As things stand, it is _not_ true that it's safe to copy > > forward ranges and then use the original. Sure, it will work > > for some ranges, but for others it won't. The entire reason > > that save exists is for ranges that are classes, because > > copying them does not result in an independent range. The range > > API does not require that copying a range result in an > > independent copy. It's not even guaranteed that copying a range > > that's a struct will result in an indpendent copy. Depending on > > the range type, copying it could result in an independent copy, > > or it could result in a reference to to the original range, or > > it could even result in part of the state being copied and part > > of it being a reference (e.g. if the current front is stored > > directly in the range, but the iteration state is stored by > > reference). > > If copying isn't making a copy, it's not copying. The semantics of copying a variable or object vary wildly depending on the type regardless of whether we're talking about ranges. Copying a pointer or reference is still a copy even if it isn't a deep copy. Copying a range _always_ results in a copy, just like it does with any other type. It just doesn't necessarily result in a copy which can be independently iterated. - Jonathan M Davis
Re: called copy constructor in foreach with ref on Range
On Tuesday, 23 June 2020 at 02:41:55 UTC, Jonathan M Davis wrote: As things stand, uncopyable ranges aren't really a thing, and common range idiomns rely on ranges being copyable. Which idioms are those? I mean, genuine idioms, not design flaws like e.g. references. We'd need some major redesigning to make uncopyable ranges work, and personally, I don't think that it's worth the trouble. Of course we would. Andrei is entertaining changing the whole input range API. Though he, like you, seems opposed to the idea of uncopyables. The range API has no requirement that the init value of a range be empty, and any generic code which relies on such behavior is buggy. In the case of classes, it would outright crash, because the init value would be null. Yeah, that's a shame. I agree that ideally the range API would require that the init state of a range be a valid, empty range, but that's simply not how it works right now. In order to make it work that way, we'd have to redesign the range API in at least some respects (e.g. getting rid of save and making it illegal for classes to be forward ranges). Better yet - making it illegal for classes to be ranges. As things stand, it is _not_ true that it's safe to copy forward ranges and then use the original. Sure, it will work for some ranges, but for others it won't. The entire reason that save exists is for ranges that are classes, because copying them does not result in an independent range. The range API does not require that copying a range result in an independent copy. It's not even guaranteed that copying a range that's a struct will result in an indpendent copy. Depending on the range type, copying it could result in an independent copy, or it could result in a reference to to the original range, or it could even result in part of the state being copied and part of it being a reference (e.g. if the current front is stored directly in the range, but the iteration state is stored by reference). If copying isn't making a copy, it's not copying. If you rely on copying a range resulting in an independent copy, you will have buggy code - even if it's a forward range. The _only_ way that the range API specifies that you can get an independent copy of a range is to use save, and generic code should never rely on any other mechanism for that. That's exactly what I said. "Copying of forward ranges is absolutely fine. It's what the current `save()` primitive is supposed to do." ...Except, of course, that that shouldn't be the case. *Copying* should be creating a copy. :) Now, ideally, we'd get rid of save and require that copying a forward range result in an independent copy (which would then require that a class be wrapped in a struct to be a range) Yes! but that's simply not how the current range API works. No, it's not :(
Re: called copy constructor in foreach with ref on Range
On Mon, Jun 22, 2020 at 08:53:22PM -0600, Jonathan M Davis via Digitalmars-d-learn wrote: [...] > Exactly. It's because of issues like this that generic, range-based > functions need to be tested with a variety of range types - including > reference types. Without that, bugs are routinely missed. [...] > If we do actually rework the range API as has occasionally been > discussed, it would be _really_ nice if we could more thoroughly > restrict the copying semantics of ranges so that some of these > problems go away, but without such a redesign, we're stuck with such > problems. Redesign or not, I think the new range API should be much more specific in specifying exactly what behaviours ranges ought to have. The current API is under-specified, with two of the main problem points that I can remember being: 1) Transient-ness: is the value return by .front guaranteed to remain valid, or does .popFront invalidate it? This is not specified in the current spec, and we have ranges in Phobos of either type (e.g., iota and .byLine), with a lot of code just blindly assuming non-transience only to discover that things break down when handed a .byLine instance. 2) The exact semantics of copying (assigning) a range. What parts of a range's state is/isn't preserved when you assign a range to a local variable, for example? What happens if you pass a range to a function and then continue to operate on the original? All of this must be specified precisely in the range API so that we don't have one range acting one way and another range acting another way, thereby compromising the semantics of generic code. T -- Democracy: The triumph of popularity over principle. -- C.Bond
real.mant_dig on windows?
Should it always be 53? or it can be 64, when? Thank you
Re: called copy constructor in foreach with ref on Range
On Monday, June 22, 2020 3:33:08 PM MDT H. S. Teoh via Digitalmars-d-learn wrote: > On Mon, Jun 22, 2020 at 09:11:07PM +, Stanislav Blinov via Digitalmars- d-learn wrote: > > That is not true. Copying of forward ranges is absolutely fine. It's > > what the current `save()` primitive is supposed to do. It's the > > copying of input ranges should just be rejected, statically. > > Jonathan is coming from the POV of generic code. The problem with move > leaving the original range in its .init state isn't so much that it will > crash or anything (even though as you said that does indicate a flaw in > the range's implementation), but that the semantics of generic code > changes in subtle ways. For example: > > auto myGenericFunc(R)(R r) { > ... > foreach (x; r) { > doSomething(x); > } > if (!r.empty) > doSomethingElse(r); > ... > } > > Suppose for argument's sake that the above foreach/if structure is an > essential part of whatever algorithm myGenericFunc is implementing. Now > there's a problem, because if R has array-like semantics, then the > algorithm will do one thing, but if R has reference-like or move > semantics, then the behaviour of the algorithm will be different, even > if both ranges represent the same sequence of input values. > > Note that in real-life code, this problem can be far more subtle than a > blatant foreach loop and if statement like the above. For example, > consider a function that drops the first n elements of a range. Your > generic function might want to pop the first n elements then do > something else with the rest of the range. Well, if you write it the > obvious way: > > auto myAlgo(R)(R r) { > size_t n = ...; > dropFirstN(r, n); > ... // do something else with r > } > > then you have a subtle bug, because the state of r after the call to > dropFirstN might be completely different depending on whether r behaves > like an array or like a by-reference or move type. Exactly. It's because of issues like this that generic, range-based functions need to be tested with a variety of range types - including reference types. Without that, bugs are routinely missed. I think that a fairly large percentage of Phobos (maybe even all of it) gets that right now, because we've added tests for reference type ranges, but there used to be quite a few bugs in Phobos, because there were no such tests. It's frequently the case that people write range-based code under the assumption that ranges will act like dynamic arrays, and their code then also works with many ranges which have similar copying behavior, but it doesn't work with all range types. Such problems don't generally get caught without extensive testing. It matters a lot less for code within a program that doesn't use a large variety of range types, but for libraries using generic functions, it's a must. If we do actually rework the range API as has occasionally been discussed, it would be _really_ nice if we could more thoroughly restrict the copying semantics of ranges so that some of these problems go away, but without such a redesign, we're stuck with such problems. And even with a redesign, the best fix for it is far from obvious, because basic input ranges and forward ranges inherently have different copying semantics. We can probably require that copying forward ranges always results in an independent copy (thereby essentially having value semantics), but basic input ranges can't really be value types, because if they could, they could be forward ranges, meaning that they're generally going to either be reference types or pseudo-reference types, which of course have different copying semantics. So, as long as the range API is set up so that the same code can operate on both basic input and forward ranges, we pretty much inherently have a problem with the copying semantics in ranges being inconsistent - though requiring value semantics for forward ranges would still be a significant improvement. - Jonathan M Davis
Re: called copy constructor in foreach with ref on Range
On Monday, June 22, 2020 3:11:07 PM MDT Stanislav Blinov via Digitalmars-d- learn wrote: > On Monday, 22 June 2020 at 20:51:37 UTC, Jonathan M Davis wrote: > > You're unlikely to find much range-based code that does that > > and there really isn't much point in doing that. Again, copying > > isn't the problem. It's using the original after making the > > copy that's the problem. > > Copy *is* the problem. If you can't make a copy (i.e. you get a > compilation error) - you don't have a problem, the compiler just > found a bug for you. Sadly, historically D's libraries were built > around lots of redundant copying, even though there are very few > cases where copies are actually required. The reason why we're > "unlikely to find much range-based code that does that [move]" is > (a) sloppy or shortsighted design and (b) poor language support. > The latter hopefully stands to change. As things stand, uncopyable ranges aren't really a thing, and common range idiomns rely on ranges being copyable. We'd need some major redesigning to make uncopyable ranges work, and personally, I don't think that it's worth the trouble. > > And moving doesn't fix anything, since the original variable is > > still there > > (just in its init state, which would still be invalid to use in > > generic code and could outright crash in some cases if you > > tried to use it - e.g. if it were a class reference, since it > > would then be null). > > Eh? A range in 'init' state should be an empty range. If you get > a crash from that then there's a bug in that range's > implementation, not in user code. The range API has no requirement that the init value of a range be empty, and any generic code which relies on such behavior is buggy. In the case of classes, it would outright crash, because the init value would be null. I agree that ideally the range API would require that the init state of a range be a valid, empty range, but that's simply not how it works right now. In order to make it work that way, we'd have to redesign the range API in at least some respects (e.g. getting rid of save and making it illegal for classes to be forward ranges). > > So, code that does a move could accidentally use the original > > range after the move and have bugs just like code that copies > > the range has bugs if the original is used after the copy has > > been made. So, the rule of thumb is not that you should avoid > > copying ranges. It's that once you've copied a range, you > > should then use only the copy and not the original. > > That is not true. Copying of forward ranges is absolutely fine. > It's what the current `save()` primitive is supposed to do. It's > the copying of input ranges should just be rejected, statically. As things stand, it is _not_ true that it's safe to copy forward ranges and then use the original. Sure, it will work for some ranges, but for others it won't. The entire reason that save exists is for ranges that are classes, because copying them does not result in an independent range. The range API does not require that copying a range result in an independent copy. It's not even guaranteed that copying a range that's a struct will result in an indpendent copy. Depending on the range type, copying it could result in an independent copy, or it could result in a reference to to the original range, or it could even result in part of the state being copied and part of it being a reference (e.g. if the current front is stored directly in the range, but the iteration state is stored by reference). If you rely on copying a range resulting in an independent copy, you will have buggy code - even if it's a forward range. The _only_ way that the range API specifies that you can get an independent copy of a range is to use save, and generic code should never rely on any other mechanism for that. Now, ideally, we'd get rid of save and require that copying a forward range result in an independent copy (which would then require that a class be wrapped in a struct to be a range), but that's simply not how the current range API works. Because the current range API does not make any guarantees about the semantics of copying a range, generic code cannot assume that those semantics are the same as save. As such, if you want an independent copy of a forward range in generic code, you must use save, or the code will be buggy. Similarly, generic code cannot use the original range after it's been copied (unless it simply never does anything with the copy), because mutating the copy may or may not mutate the original, and the original may or may not even be in a valid state if the copy is mutated. With non-generic code, you can rely on the behaviors of specific ranges and what will happen when you copy them (e.g. not bothering to call save when passing strings around), but with generic code, that's not true. And there's plenty of D code out there that works correctly with a specific range type but which would fail miserably if it were used
Re: called copy constructor in foreach with ref on Range
On Monday, June 22, 2020 3:38:02 PM MDT Paul Backus via Digitalmars-d-learn wrote: > On Monday, 22 June 2020 at 21:33:08 UTC, H. S. Teoh wrote: > > Jonathan is coming from the POV of generic code. The problem > > with move leaving the original range in its .init state isn't > > so much that it will crash or anything (even though as you said > > that does indicate a flaw in the range's implementation), but > > that the semantics of generic code changes in subtle ways. For > > example: > > > > [...] > > Seems to me like adding some unit tests with non-copyable input > ranges to Phobos could expose a number of latent bugs. At this point, non-copyable ranges really aren't a thing. foreach does not support them and neither does Phobos. isInputRange doesn't actually reject them, but they don't really work in practice, and it's unlikely that you're going to find much in Phobos that happens to work with them. isForwardRange does outright reject them though. - Jonathan M Davis
Re: called copy constructor in foreach with ref on Range
On Monday, 22 June 2020 at 21:33:08 UTC, H. S. Teoh wrote: Don't be shocked when you find out how many Phobos ranges have .init states that are invalid (e.g., non-empty, but .front and .popFront will crash / return invalid values). Which ones? Jonathan is coming from the POV of generic code. The problem with move leaving the original range in its .init state isn't so much that it will crash or anything (even though as you said that does indicate a flaw in the range's implementation), but that the semantics of generic code changes in subtle ways. Well that means that the code is not generic, i.e. the bug originates in the design, not implementation. auto myGenericFunc(R)(R r) { ... foreach (x; r) { doSomething(x); } if (!r.empty) doSomethingElse(r); ... } Suppose for argument's sake that the above foreach/if structure is an essential part of whatever algorithm myGenericFunc is implementing. Now there's a problem, because if R has array-like semantics, then the algorithm will do one thing, but if R has reference-like or move semantics, then the behaviour of the algorithm will be different, even if both ranges represent the same sequence of input values. Yep, definitely not generic. Which is exactly the kind of error that should be caught at compile time. Which we, sadly, can't do with the current range API. consider a function that drops the first n elements of a range. Your generic function might want to pop the first n elements then do something else with the rest of the range. Well, if you write it the obvious way: auto myAlgo(R)(R r) { size_t n = ...; dropFirstN(r, n); ... // do something else with r } then you have a subtle bug, because the state of r after the call to dropFirstN might be completely different depending on whether r behaves like an array or like a by-reference or move type. Err... that one is actually fine. It would take the range by ref and pop off the elements. There already is such a function - the popFrontN. It's the functions that take ranges by value that present the not-so-subtle issue with reference types. For example, its chainable would-be-counterpart drop(). "Would-be" because that one takes the argument by value. We should move toward disallowing reference types to be ranges. A good deal of the rest can be solved with API and design changes (like disallowing copying of input ranges). It's kind of interesting how, with the ongoing discussion about range API in the general forum, a couple of range questions are brought up in learn. Something, as they say, is in the air.
Re: Temporary File Creation
On Monday, June 22, 2020 3:46:57 PM MDT Per Nordlöw via Digitalmars-d-learn wrote: > Has anybody written a procedure for creating a temporary file in > a race-free manner? > > And why has such a procedure not already been added to std.file > when std.file.tempDir has? > > See: https://dlang.org/library/std/file/temp_dir.html I created a PR for one a while back that resulted in a surprisingly large amount of arguing. IIRC, there were some tweaks I still needed to make to get it merged, but I keep forgetting to get back to it. - Jonathan M Davis
Re: Temporary File Creation
On Monday, 22 June 2020 at 21:46:57 UTC, Per Nordlöw wrote: Has anybody written a procedure for creating a temporary file in a race-free manner? And why has such a procedure not already been added to std.file when std.file.tempDir has? See: https://dlang.org/library/std/file/temp_dir.html tempDir just returns a path (i.e. "/tmp" or whatever it is on Windows, etc.), it doesn't create anything. Given the synopsis of std.file, a procedure for *creating* a file doesn't belong there, as it would only be half of an operation. There is a https://dlang.org/library/std/stdio/file.tmpfile.html
Re: How to work Get & Set text in clipboard in Windows ?
On Monday, 22 June 2020 at 10:26:12 UTC, aberba wrote: It would be a one-liner if it was an api. Such utility APIs are quite missing in D. Um, You are right. How about putting them together into a package? Well, this is an inspiration to me. Let me try. :)
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Monday, 22 June 2020 at 21:27:12 UTC, Steven Schveighoffer wrote: auto line = range.front; range.popFront; // pop immediately This is a bad idea, once you popFront, the original front is possibly invalid (and technically is the case for byLine). In this case, it's caused by underlying structure may reuse the `byLine` buffer, but I'm asking a more general question about range interface: why not provide an extra popAndReturnFront(), and the user to choose in the actual usage scenario.
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Monday, 22 June 2020 at 21:22:10 UTC, H. S. Teoh wrote: On Mon, Jun 22, 2020 at 08:51:49PM +, mw via Digitalmars-d-learn wrote: [...] >auto line = range.front; >range.popFront; // pop immediately [...] This is dangerous, because it assumes .front is not invalidated by .popFront. It will not work, for example, with byLine because .front returns a buffer which is reused by .popFront (a so-called "transient range"). This is valid reason, but as I replied in the other post: it depends on the actual underlying data structure and usage scenario, so: "why not provide a popAndReturnFront(), and let the user decide based on his/her own actual usage?"
Temporary File Creation
Has anybody written a procedure for creating a temporary file in a race-free manner? And why has such a procedure not already been added to std.file when std.file.tempDir has? See: https://dlang.org/library/std/file/temp_dir.html
Re: called copy constructor in foreach with ref on Range
On Monday, 22 June 2020 at 21:33:08 UTC, H. S. Teoh wrote: Jonathan is coming from the POV of generic code. The problem with move leaving the original range in its .init state isn't so much that it will crash or anything (even though as you said that does indicate a flaw in the range's implementation), but that the semantics of generic code changes in subtle ways. For example: [...] Seems to me like adding some unit tests with non-copyable input ranges to Phobos could expose a number of latent bugs.
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Monday, 22 June 2020 at 20:58:58 UTC, Ali Çehreli wrote: Others have other explanations for this but my understanding is about exception safety: If it changed internal state and returned the front object, you would no be able to make a function like popFront() strongly exception safe. (There are ample documentation for this topic in C++ circles.) That's one consideration. But, it will be more interesting in knowing (based on actual usage): (a) how many bugs % are due to exception un-safe (b) how many bugs % are due to front / popFront separation? And which is more bug-prone for a typical programmer? my gut feeling is (b), at least we just saw one in this thread. And -- loop thru a static structure content like a simple array, why we need to worry about exception safe? -- loop thru dynamically generated content, esp. network or more external complex structure may worth considering exception safety. But even there, do people always need to call !range.empty() check first? when it's not empty, how much more exception un-safety that popAndReturnFront() can introduce than the combination of `front(); ...; popFront();"? And why not provide a popAndReturnFront(), and let the user decide based on his/her own actual usage? Another reason is cohesion: We want functions to have as little responsibility as possible (ideally single). Again we have to weight which way is more bug-prone, any actual statistics on the above (a) v.s (b)? > (or do we have another function for this)? There are many useful functions in std.range: https://dlang.org/phobos/std_range.html The "take" and "drop" functions may be useful. Use these functions inside a while(!range.empty()) {...} can only introduce more code complexity.
Re: called copy constructor in foreach with ref on Range
On Monday, June 22, 2020 3:10:28 PM MDT kinke via Digitalmars-d-learn wrote: > On Monday, 22 June 2020 at 20:51:37 UTC, Jonathan M Davis wrote: > > [...] > > That's why I put the struct in parantheses. Moving a class ref > makes hardly any sense, but I've also never written a *class* to > represent a range. Moving is the no-brainer solution for > transferring ownership of struct ranges and invalidating the > original instance. Invalidating the instance doesn't actually prevent it from being misused though. At best, the fact that you moved the instance rather than copying it makes it more likely that accidentally using the instance will cause more extreme bugs or crash. The core issue that the original is potentially invalid but can still be used exists whether you copy the range or move it. Also, since the issue here is generic code, you have to take classes into account and cannot assume that the range is a struct. True, it's usually a bad idea to use classes for ranges, and ideally, we'd alter the range API so that classes weren't valid ranges, but the reality of the matter is that they are, and generic code has to take that into account. - Jonathan M Davis
Re: called copy constructor in foreach with ref on Range
On Mon, Jun 22, 2020 at 09:11:07PM +, Stanislav Blinov via Digitalmars-d-learn wrote: > On Monday, 22 June 2020 at 20:51:37 UTC, Jonathan M Davis wrote: [...] > > And moving doesn't fix anything, since the original variable is > > still there (just in its init state, which would still be invalid to > > use in generic code and could outright crash in some cases if you > > tried to use it - e.g. if it were a class reference, since it would > > then be null). > > Eh? A range in 'init' state should be an empty range. If you get a > crash from that then there's a bug in that range's implementation, not > in user code. Don't be shocked when you find out how many Phobos ranges have .init states that are invalid (e.g., non-empty, but .front and .popFront will crash / return invalid values). > > So, code that does a move could accidentally use the original range > > after the move and have bugs just like code that copies the range > > has bugs if the original is used after the copy has been made. So, > > the rule of thumb is not that you should avoid copying ranges. It's > > that once you've copied a range, you should then use only the copy > > and not the original. > > That is not true. Copying of forward ranges is absolutely fine. It's > what the current `save()` primitive is supposed to do. It's the > copying of input ranges should just be rejected, statically. Jonathan is coming from the POV of generic code. The problem with move leaving the original range in its .init state isn't so much that it will crash or anything (even though as you said that does indicate a flaw in the range's implementation), but that the semantics of generic code changes in subtle ways. For example: auto myGenericFunc(R)(R r) { ... foreach (x; r) { doSomething(x); } if (!r.empty) doSomethingElse(r); ... } Suppose for argument's sake that the above foreach/if structure is an essential part of whatever algorithm myGenericFunc is implementing. Now there's a problem, because if R has array-like semantics, then the algorithm will do one thing, but if R has reference-like or move semantics, then the behaviour of the algorithm will be different, even if both ranges represent the same sequence of input values. Note that in real-life code, this problem can be far more subtle than a blatant foreach loop and if statement like the above. For example, consider a function that drops the first n elements of a range. Your generic function might want to pop the first n elements then do something else with the rest of the range. Well, if you write it the obvious way: auto myAlgo(R)(R r) { size_t n = ...; dropFirstN(r, n); ... // do something else with r } then you have a subtle bug, because the state of r after the call to dropFirstN might be completely different depending on whether r behaves like an array or like a by-reference or move type. T -- Truth, Sir, is a cow which will give [skeptics] no more milk, and so they are gone to milk the bull. -- Sam. Johnson
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On 6/22/20 4:49 PM, mw wrote: On Monday, 22 June 2020 at 20:46:30 UTC, mw wrote: On Monday, 22 June 2020 at 20:00:50 UTC, Steven Schveighoffer wrote: I wouldn't recommend it, instead do a while loop: auto range = File(fn).byLine; while(!range.empty) { auto line = range.front; if(someCond(line)) { range.popFrontN(n); I'm asking this, because here it need to be range.popFrontN(n+1); `n` actually isn't defined, you defined it in a comment in your original code ;) I just threw it in there. Of course, make sure it works how you are expecting, I don't know what your code is doing. i.e. bug-prone can be fixed by: auto line = range.front; range.popFront; // pop immediately This is a bad idea, once you popFront, the original front is possibly invalid (and technically is the case for byLine). } else { regularProcess(line); range.popFront; } } Thanks. so `front` is peek, and `popFront` is the pop action whose return type is `void`, why we need two *separate* calls instead of just let `popFront` return T (or do we have another function for this)? i.e if the user forget `popFront`, it will prone to infinite loop bug? so my question. There is no requirement to actually construct members. e.g. popFrontN calls popFront N times, but does not actually invoke front at all. Separating the concerns is for correctness and performance. -Steve
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Mon, Jun 22, 2020 at 08:51:49PM +, mw via Digitalmars-d-learn wrote: [...] > >auto line = range.front; > >range.popFront; // pop immediately [...] This is dangerous, because it assumes .front is not invalidated by .popFront. It will not work, for example, with byLine because .front returns a buffer which is reused by .popFront (a so-called "transient range"). In the realm of defensive programming, it is better to make less assumptions (don't assume .front remains valid after .popFront) than add implicit assumptions (range is non-transient) that may break in subtle ways that are not immediately obvious. T -- Why ask rhetorical questions? -- JC
Re: called copy constructor in foreach with ref on Range
On Monday, 22 June 2020 at 20:51:37 UTC, Jonathan M Davis wrote: You're unlikely to find much range-based code that does that and there really isn't much point in doing that. Again, copying isn't the problem. It's using the original after making the copy that's the problem. Copy *is* the problem. If you can't make a copy (i.e. you get a compilation error) - you don't have a problem, the compiler just found a bug for you. Sadly, historically D's libraries were built around lots of redundant copying, even though there are very few cases where copies are actually required. The reason why we're "unlikely to find much range-based code that does that [move]" is (a) sloppy or shortsighted design and (b) poor language support. The latter hopefully stands to change. And moving doesn't fix anything, since the original variable is still there (just in its init state, which would still be invalid to use in generic code and could outright crash in some cases if you tried to use it - e.g. if it were a class reference, since it would then be null). Eh? A range in 'init' state should be an empty range. If you get a crash from that then there's a bug in that range's implementation, not in user code. So, code that does a move could accidentally use the original range after the move and have bugs just like code that copies the range has bugs if the original is used after the copy has been made. So, the rule of thumb is not that you should avoid copying ranges. It's that once you've copied a range, you should then use only the copy and not the original. That is not true. Copying of forward ranges is absolutely fine. It's what the current `save()` primitive is supposed to do. It's the copying of input ranges should just be rejected, statically.
Re: called copy constructor in foreach with ref on Range
On Monday, 22 June 2020 at 20:51:37 UTC, Jonathan M Davis wrote: [...] That's why I put the struct in parantheses. Moving a class ref makes hardly any sense, but I've also never written a *class* to represent a range. Moving is the no-brainer solution for transferring ownership of struct ranges and invalidating the original instance.
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On 6/22/20 1:46 PM, mw wrote: > so `front` is peek, and `popFront` is the pop action whose return type > is `void`, why we need two *separate* calls instead of just let > `popFront` return T Others have other explanations for this but my understanding is about exception safety: If it changed internal state and returned the front object, you would no be able to make a function like popFront() strongly exception safe. (There are ample documentation for this topic in C++ circles.) Another reason is cohesion: We want functions to have as little responsibility as possible (ideally single). > (or do we have another function for this)? There are many useful functions in std.range: https://dlang.org/phobos/std_range.html The "take" and "drop" functions may be useful. Ali
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Monday, 22 June 2020 at 20:49:55 UTC, mw wrote: On Monday, 22 June 2020 at 20:46:30 UTC, mw wrote: On Monday, 22 June 2020 at 20:00:50 UTC, Steven Schveighoffer wrote: I wouldn't recommend it, instead do a while loop: auto range = File(fn).byLine; while(!range.empty) { auto line = range.front; if(someCond(line)) { range.popFrontN(n); I'm asking this, because here it need to be either range.popFrontN(n+1); i.e. bug-prone or can be fixed by: auto line = range.front; range.popFront; // pop immediately of course.
Re: called copy constructor in foreach with ref on Range
On Monday, June 22, 2020 1:41:34 PM MDT kinke via Digitalmars-d-learn wrote: > On Monday, 22 June 2020 at 19:03:44 UTC, Jonathan M Davis wrote: > > in practice, that means that generic code cannot use a range > > once it's been copied > > Translating to a simple rule-of-thumb: never copy a (struct) > range, always move. You're unlikely to find much range-based code that does that and there really isn't much point in doing that. Again, copying isn't the problem. It's using the original after making the copy that's the problem. And moving doesn't fix anything, since the original variable is still there (just in its init state, which would still be invalid to use in generic code and could outright crash in some cases if you tried to use it - e.g. if it were a class reference, since it would then be null). So, code that does a move could accidentally use the original range after the move and have bugs just like code that copies the range has bugs if the original is used after the copy has been made. So, the rule of thumb is not that you should avoid copying ranges. It's that once you've copied a range, you should then use only the copy and not the original. - Jonathan M Davis
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Monday, 22 June 2020 at 20:46:30 UTC, mw wrote: On Monday, 22 June 2020 at 20:00:50 UTC, Steven Schveighoffer wrote: I wouldn't recommend it, instead do a while loop: auto range = File(fn).byLine; while(!range.empty) { auto line = range.front; if(someCond(line)) { range.popFrontN(n); I'm asking this, because here it need to be range.popFrontN(n+1); i.e. bug-prone can be fixed by: auto line = range.front; range.popFront; // pop immediately } else { regularProcess(line); range.popFront; } } Thanks. so `front` is peek, and `popFront` is the pop action whose return type is `void`, why we need two *separate* calls instead of just let `popFront` return T (or do we have another function for this)? i.e if the user forget `popFront`, it will prone to infinite loop bug? so my question.
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Monday, 22 June 2020 at 20:00:50 UTC, Steven Schveighoffer wrote: I wouldn't recommend it, instead do a while loop: auto range = File(fn).byLine; while(!range.empty) { auto line = range.front; if(someCond(line)) { range.popFrontN(n); } else { regularProcess(line); range.popFront; } } Thanks. so `front` is peek, and `popFront` is the pop action whose return type is `void`, why we need two *separate* calls instead of just let `popFront` return T (or do we have another function for this)? i.e if the user forget `popFront`, it will prone to infinite loop bug?
Re: are std.traits.FieldNameTuple and std.traits.Fields returned value always in sync?
On Monday, 22 June 2020 at 19:55:29 UTC, mw wrote: Yes, in the same order and of the same length. Can we add this information to the doc? to make it clear to the user: https://dlang.org/library/std/traits.html It's pretty clear in that doc already: alias FieldNameTuple(T) = staticMap!(NameOf,T.tupleof[0..__dollar-isNested!T]); alias Fields(T) = typeof(T.tupleof[0..__dollar-isNested!T]); But if you'd like it spelled out in text as well, you can make a PR for the Phobos repository.
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
If you are referring to the next line, not the next n lines, that's a simple `continue;` statement.
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On Monday, 22 June 2020 at 20:02:22 UTC, kinke wrote: If you are referring to the next line, not the next n lines, that's a simple `continue;` statement. [Please discard, that'd obviously be skipping the *current* line.]
Re: how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
On 6/22/20 3:53 PM, mw wrote: Hi, I need this logic: ``` auto range = File(fn).byLine(); foreach (line; range) { if (comeCond(line)) { // skip the next n line // and continue the foreach loop from the (n+1) line } else { regularProcess(line); } } ``` Is it possible to do this in a foreach loop? I wouldn't recommend it, instead do a while loop: auto range = File(fn).byLine; while(!range.empty) { auto line = range.front; if(someCond(line)) { range.popFrontN(n); } else { regularProcess(line); range.popFront; } } -Steve
Re: are std.traits.FieldNameTuple and std.traits.Fields returned value always in sync?
On Saturday, 20 June 2020 at 20:42:03 UTC, Stanislav Blinov wrote: On Saturday, 20 June 2020 at 20:17:54 UTC, mw wrote: Are their returned value, i.e the field names and their types are always in the same order, and of the same length? If they are not, how to get sync-ed pairs (name, type)? If they are, why we need two separate calls, which cause confusion. Yes, in the same order and of the same length. Can we add this information to the doc? to make it clear to the user: https://dlang.org/library/std/traits.html
Re: How to correctly integrate D library to Swift/Obj-C mobile project?
On Monday, 22 June 2020 at 19:41:22 UTC, Vlad wrote: Usually, when you connect c++/c, you have header files so you can call functions from Objective-C/swift code. We need something similar. There's a pretty recent -HC switch to generate C++ headers from `extern(C++)` declarations. Not sure how usable it is at this point. Is it even possible to compile D for iOS and use it the same way as compiled C++ static library? (We do need a D runtime) iOS shouldn't be any different than other targets (especially macOS) in this regard.
how to skip the next (n) item & continue from (n+1) with a range ? e.g. in File(fn).byLine();
Hi, I need this logic: ``` auto range = File(fn).byLine(); foreach (line; range) { if (comeCond(line)) { // skip the next n line // and continue the foreach loop from the (n+1) line } else { regularProcess(line); } } ``` Is it possible to do this in a foreach loop? If not, how can I achieve that, esp. when reading a file line-by-line? do I have to read the whole file into memory, and split by line and using regular `for` loop?
Re: How to correctly integrate D library to Swift/Obj-C mobile project?
On Monday, 22 June 2020 at 18:40:21 UTC, Kagamin wrote: If you want to use them from D, you need those classes and methods declared in the D language, in text. We want to use compiled D as a library in a iOS swift project. Usually, when you connect c++/c, you have header files so you can call functions from Objective-C/swift code. We need something similar. — Is it even possible to compile D for iOS and use it the same way as compiled C++ static library? (We do need a D runtime)
Re: called copy constructor in foreach with ref on Range
On Monday, 22 June 2020 at 19:03:44 UTC, Jonathan M Davis wrote: in practice, that means that generic code cannot use a range once it's been copied Translating to a simple rule-of-thumb: never copy a (struct) range, always move.
Re: called copy constructor in foreach with ref on Range
On Monday, June 22, 2020 10:59:45 AM MDT kinke via Digitalmars-d-learn wrote: > If copying a range is considered to be generally unsafe and a > common pitfall (vs. the save() abomination), maybe range-foreach > shouldn't allow any lvalue ranges in the 1st place, thus not > making any copies and forcing the user to specify some rvalue (as > returned by `range.save()`, or `move(range)` if destructive > iteration is indeed intended). Copying ranges isn't a problem. Almost all range-based code copies ranges quite a bit (e.g. almost all range-based functions take a range by value, and chaining range-based function calls wouldn't work if it didn't). Rather, it's using the original after a copy has been made that's a problem (at least in generic code), because the semantics of copying aren't part of the range API and can vary wildly depending on the type. Really, the issues with copying were not properly taken into account when the range API was created, and mistakes were made. If we were to rework the range API at this point, we would probably get rid of save and require that copying be equivalent to save for forward ranges (which would then make it illegal for classes to be forward ranges without wrapping them in a struct). That would fix the problem for forward ranges, but basic input ranges can't have those semantics, or they could be forward ranges, so exactly what the correct solution would be is debatable, and if generic code operates on both basic input ranges and forward ranges, the copying semantics won't always be the same, which is the problem we have now. So, if we were to rework the range API, it's one of the open problems that needs to be sorted out. Regardless, as things stand, because copying a range can have reference semantics, value semantics, or something in between, in practice, that means that generic code cannot use a range once it's been copied, because the semantics will vary from type to type. The copy can be used (and most range-based code relies on that), but the original needs to be left alone. Non-generic code has more leeway, because it can rely on the behavior of specific range types, but with generic code, you have to be careful. And foreach is just one of the places where the issue of not using the original after making a copy comes up. - Jonathan M Davis
Re: Read to stdout doesn't trigger correct action
On Monday, 22 June 2020 at 14:27:18 UTC, Steven Schveighoffer wrote: I'm sure if there is a clib that doesn't work with this, it is a bug with druntime, and should be addressed. I don't know enough about the exact functionality to be able to write such a bug report, but you probably should if it's not working for you. -Steve I don't really have a good solution for this and it seems to work for existing supported C libraries. One solution would be to move the makeGlobal implementation to become OS/C-library specific and each platform would have a suitable implementation but that kind of contradicts having as little platform specific code possible. Another thing that is a bit sketchy here is the usage of raw atomics as spinlocks as it would have side effects in case of locking (CPU could spin for several milliseconds if the locker CPU is preempted and does something else). I guess in this particular case it is used because it would be race creating a mutex as well because we want lazy initialization. While I kind of like lazy initialization myself it kind creates situations like these. I don't quite follow the exact reason behind avoiding static ctors, lazy initialization is nice for many things but ctors have their place as well. Using C stdio without druntime would read the OS specific FILE* directly, or what am I missing here?
Re: How to correctly integrate D library to Swift/Obj-C mobile project?
If you want to use them from D, you need those classes and methods declared in the D language, in text.
Re: GtkD code review - How to update a progressbar using data sharing concurrency
On 6/21/20 5:52 AM, adnan338 wrote: I am trying to figure out how to prevent this data race. I still like the std.concurrency method I used here: https://forum.dlang.org/post/rkitcprqvslexgqaf...@forum.dlang.org The only difference is that your individual progresses are from 0% to 100%. The example can be changed easily to report 100% once at the end of each download. Ali
Re: called copy constructor in foreach with ref on Range
On Monday, 22 June 2020 at 16:25:11 UTC, Jonathan M Davis wrote: The reason that foreach copies the range is simply due to how the code is lowered. e.g. foreach(e; range) { } essentially becomes for(auto r = range; !r.empty; r.popFront()) { auto e = r.front; } And the fact that a copy is made is likely simply a result of it mimicking what happens with arrays. I was trying to explain/guess the rationale for that copy (not in terms of how it's implemented, but why it's implemented that way). This 'mimicking-an-array' doesn't make any sense to me. If the original idea wasn't to make sure the range is reusable afterwards, I guess it's done for implementation simplicity, to promote an rvalue range to the required lvalue. If copying a range is considered to be generally unsafe and a common pitfall (vs. the save() abomination), maybe range-foreach shouldn't allow any lvalue ranges in the 1st place, thus not making any copies and forcing the user to specify some rvalue (as returned by `range.save()`, or `move(range)` if destructive iteration is indeed intended).
Re: called copy constructor in foreach with ref on Range
On Sunday, June 21, 2020 2:25:37 PM MDT kinke via Digitalmars-d-learn wrote: > A foreach over a custom range makes an initial copy, so that the > original range is still usable after the foreach (not empty). No, it's not so that the range will be useable afterwards. In fact, for generic code, you must _never_ use a range again after passing it to foreach, because the copying semantics of ranges are not specified, and you can get radically different behavior depending on what the copying semantics of a range are (e.g. if it's a class, then it's just copying the reference). In general, you should never use a range after it's been copied unless you know exactly what type of range you're dealing with and what its copying behavior is. If you want an independent copy, you need to use save. The reason that foreach copies the range is simply due to how the code is lowered. e.g. foreach(e; range) { } essentially becomes for(auto r = range; !r.empty; r.popFront()) { auto e = r.front; } And the fact that a copy is made is likely simply a result of it mimicking what happens with arrays. Either way, you should never be doing something like foreach(e; range) { } auto v = range.front; in generic code. It needs to be foreach(e; range.save) { } auto v = range.front; instead. - Jonathan M Davis
Re: When is exception throwing @nogc with -dip1008?
On 6/22/20 10:36 AM, Per Nordlöw wrote: On Monday, 22 June 2020 at 14:10:15 UTC, Mike Parker wrote: https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md The spec says: " The only place a refcounted Throwable is ever created is when the following statement is in the user code: throw new E(string); " What other typical throw cases will _not_ be allocated on the GC? My understanding is none. So for example: auto ex = new Exception(string); // heap allocated throw ex; It entirely depends on you immediately throwing the exception because otherwise, you may have stored a pointer elsewhere. Because classes (exceptions) cannot be truly refcounted on copy, you need a very controlled environment to ensure the ref-counting works. -Steve
Re: When is exception throwing @nogc with -dip1008?
On Monday, 22 June 2020 at 14:10:15 UTC, Mike Parker wrote: https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md The spec says: " The only place a refcounted Throwable is ever created is when the following statement is in the user code: throw new E(string); " What other typical throw cases will _not_ be allocated on the GC?
How to correctly integrate D library to Swift/Obj-C mobile project?
I have a static library (.a) compiled with LDC for iOS platform. But I can't figure out how to correctly connect it to the project and call its functions. I've already linked binary with library to the project but IDE still doesn't see its classes and methods. Do I need to do some additional configurations?
Re: Read to stdout doesn't trigger correct action
On 6/22/20 10:05 AM, IGotD- wrote: This seems do some atomic operation preventing the D File class for stdio not to be initialized several times. I'm not quite sure if this is global or per thread but I guess it is for the entire process. For some reason the std File classes are never initialized at all. Another problem is that the function that is called to obtain the clib stdin/out use a structure that is lazy initialized per thread, so it must be called at least a first time for each thread in order to get the correct stdin/out. Removing the atomic operations so that the File initialization is done every time, then it works. The point of the makeGlobal call is to avoid dependency on static ctors (which is how it was done before this implementation), to allow usage of stdio without druntime. The __gshared designation is equivalent to C's globals, so it is shared among all threads (though the type is still as if it were thread-local). Question is if "File makeGlobal(StdFileHandle _iob)()" is correct when it comes to ensure compatibility among all the clib versions out there. Not too seldom are clib global variables really functions, like errno is often a function rather than a variable. The internal code the the clib (Newlib) does not have this "optimization" but always get stdin/out using this function call. I'm sure if there is a clib that doesn't work with this, it is a bug with druntime, and should be addressed. I don't know enough about the exact functionality to be able to write such a bug report, but you probably should if it's not working for you. -Steve
Re: When is exception throwing @nogc with -dip1008?
On Monday, 22 June 2020 at 13:59:22 UTC, Per Nordlöw wrote: I can't find any docs on DIP-1008. The link https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md given in the release notes here Ref: https://dlang.org/changelog/2.079.0.html#dip1008 is broken. https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md
Re: When is exception throwing @nogc with -dip1008?
On Monday, 22 June 2020 at 13:59:22 UTC, Per Nordlöw wrote: I can't find any docs on DIP-1008. The link https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md is broken. Found the postponed spec here: https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md
Read to stdout doesn't trigger correct action
I've done some adaptations to druntime for another C library that isn't currently supported. Obtaining the FILE* structure of the clib is done via a function call rather than global variables. However this function call is never triggered when issuing a writeln function call. The FILE* structure is a pointer that points to a completely wrong location. Reading core.stdc.stdio.stdin and core.stdc.stdio.stdout explicitly will trigger the function call and the correct addresses can be read. The function writeln seems to obtain the std FILE* structures is "@property ref File makeGlobal(StdFileHandle _iob)()" in std.stdio.d. // Undocumented but public because the std* handles are aliasing it. @property ref File makeGlobal(StdFileHandle _iob)() { __gshared File.Impl impl; __gshared File result; // Use an inline spinlock to make sure the initializer is only run once. // We assume there will be at most uint.max / 2 threads trying to initialize // `handle` at once and steal the high bit to indicate that the globals have // been initialized. static shared uint spinlock; import core.atomic : atomicLoad, atomicOp, MemoryOrder; if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2) { for (;;) { if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2) break; if (atomicOp!"+="(spinlock, 1) == 1) { with (StdFileHandle) assert(_iob == stdin || _iob == stdout || _iob == stderr); impl.handle = mixin(_iob); result._p = atomicOp!"+="(spinlock, uint.max / 2); break; } atomicOp!"-="(spinlock, 1); } } return result; } This seems do some atomic operation preventing the D File class for stdio not to be initialized several times. I'm not quite sure if this is global or per thread but I guess it is for the entire process. For some reason the std File classes are never initialized at all. Another problem is that the function that is called to obtain the clib stdin/out use a structure that is lazy initialized per thread, so it must be called at least a first time for each thread in order to get the correct stdin/out. Removing the atomic operations so that the File initialization is done every time, then it works. Question is if "File makeGlobal(StdFileHandle _iob)()" is correct when it comes to ensure compatibility among all the clib versions out there. Not too seldom are clib global variables really functions, like errno is often a function rather than a variable. The internal code the the clib (Newlib) does not have this "optimization" but always get stdin/out using this function call.
When is exception throwing @nogc with -dip1008?
Under what circumstances is exception throwing currently `@nogc` if the dmd flag `-dip1008` is used? For instance in code such as: void main() @nogc { throw new Exception("I'm @nogc now"); } Are all exceptions forced to be statically allocated when -dip1008 is used or can exception throwing be `@nogc` only when certain conditions hold? I can't find any docs on DIP-1008. The link https://github.com/dlang/DIPs/blob/master/DIPs/DIP1008.md given in the release notes here Ref: https://dlang.org/changelog/2.079.0.html#dip1008 is broken.
Re: Some questions about strings
On Monday, 22 June 2020 at 09:06:35 UTC, Jacob Carlborg wrote: String **literals** have a terminating null character, to help with integrating with C functions. But this null character will disappear when manipulating strings. You cannot assume that a function parameter of type `string` will have a terminating null character, but calling `printf` with a string literal is fine: printf("foobar\n"); // this will work since string literals have have a terminating null character OK, it makes sense that the null terminator would be added where compatability with C is required. Good to know.
Re: Passing a variable number of slices into a function
On 6/21/20 10:04 PM, user1234 wrote: On Monday, 22 June 2020 at 01:47:49 UTC, repr-man wrote: Is there any way to pass an unknown number of slices into a function? I'm trying to do something along the lines of: void func(T)(T[] args...) { //... } That wasn't working, [...] Thanks for the help! Can you provide more details for "that wasnt working" ? because void func(T)(T[] args...) { writeln(args.length); } void main() { int[] a,b; func(a[0..$],b[0..$]); } works, so there must be a missing detail. Maybe each slice has different type ? I think the problem is that typesafe variadics bind to arrays as well as individual parameters. So func(a[0 .. $]) for instance would resolve as func!int instead of the desired func!(int[]). The answer might be: func(T)(T[][] args...) as Adam says. -Steve
Re: Rules for Symbol Name Lookup seem contradictory
On Monday, 22 June 2020 at 02:16:52 UTC, user1234 wrote: [...] Maybe that the spec is a bit vague as it doesn't mention that [...] A vague place in a spec is usually called "Dark Corner" and the functionality then marked as "Implementation defined". But this mark is missing here. And restricting the validity of the contradictory seeming sentences to overloadables only, puts all others into an unregulated state. Therefore the stated problem stays unexplained.
Re: How to work Get & Set text in clipboard in Windows ?
On Saturday, 20 June 2020 at 19:39:56 UTC, Vinod K Chandran wrote: On Saturday, 20 June 2020 at 13:46:05 UTC, Dennis wrote: Thanks a lot. Well, i thought it should be a one liner like- Clipboard.SetText(sText) But after reading your reply, i realized that this is D, not a scripting language. :) It would be a one-liner if it was an api. Such utility APIs are quite missing in D. How about putting them together into a package?
Re: Some questions about strings
On Monday, 22 June 2020 at 04:08:10 UTC, Denis wrote: The terminating null character was one of the reasons I thought strings were different from char arrays. Now I know better. String **literals** have a terminating null character, to help with integrating with C functions. But this null character will disappear when manipulating strings. You cannot assume that a function parameter of type `string` will have a terminating null character, but calling `printf` with a string literal is fine: printf("foobar\n"); // this will work since string literals have have a terminating null character -- /Jacob Carlborg