Re: Why does Nullable implicitly casts when assigning a variable but not when returning from a function?
On Wednesday, April 10, 2024 2:41:56 PM MDT Lettever via Digitalmars-d-learn wrote: > ``` > import std; > > Nullable!int func() => 3; > void main() { > Nullable!int a = 3; > //works fine > Nullable!int b = func(); > //does not compile > } Technically, no implicit conversion is going on here. What's happening is that you're effectively using an alternate syntax for construction. So, Nullable!int a = 3; gets treated as Nullable!int a = Nullable!int(3); And this only happens in cases where the compiler must do construction, which is what you're doing when you declare a variable and give it a value that isn't the exact same type as the variable, since construction is the only thing that it can do in that case. It's either that or requiring that you call the constructor explicitly, and for better or worse, the compiler interprets it as a constructor call without the explicit constructor call. But it only works, because it's explicit that a variable is being declared, and therefore it must be the case that you're constructing it when you use = to give it a value. On the other hand, when you're returning a value from a function, you're not telling the compiler to construct anything, because unlike when declaring a variable, construction isn't necessary. So, at that point, the value being returned has to either have the same type as the return type, or it needs to implicitly convert to the return type, and int does not implicitly convert to Nullable!int, so you get an error. The only implicit conversions that the language has for user-defined types are 1. When a type has an alias this, it defines an implicit conversion _to_ the type that the alias evaluates to. 2. When a type has a base type (e.g. a class or an enum), then it will implicitly convert to its base type. There is no way to implicitly convert via construction, since having that makes it harder to keep track of what happens when calling a function. This can get very bad in C++ given that it allows you to declare a type to implicitly convert to, and it has implicit construction. They had to add the ability to mark parameters as explicit to block implicit conversions, because it causes enough problems. So, Walter decided to simplify how implicit conversions work in D, and we don't have implicit construction. Of course, there are times when the lack of implicit construction can get annoying (and Nullable is a prime example of that), but other problems are reduced as a result. For Nullable, the solution is to use the nullable helper function so that you don't have to repeat the type. E.G. Nullable!int func() => nullable(3); or auto func() => nullable(3); And in many cases, rather than implicitly calling the constructor when declaring a variable, it's arguably better to just use auto with an explicit constructor call (and it definitely helps with Nullable). E.g. auto a = nullable(3); allows you to not bother giving the explicit type of the Nullable at all. - Jonathan M Davis
Re: Using core/sys/posix/mqueue.d on FreeBSD
Actually, since I'm usually the one who does the FreeBSD ones anyway, here you go: https://github.com/dlang/dmd/pull/16359 The declarations compile, and they should match the ones in C, since I copied them over and then tweaked them, but I haven't actually tested them. All that being said, even if they're merged immediately, they won't be available as part of druntime until dmd 2.109.0 is released (and 2.108.0 was released less than a week ago), so you'll probably need to copy them into your own install or use the development version of dmd to get the updated bindings if you want to use them now. - Jonathan M Davis
Re: Using core/sys/posix/mqueue.d on FreeBSD
On Saturday, April 6, 2024 3:57:46 AM MDT Arjan via Digitalmars-d-learn wrote: > I'm using posix mqueue in a D application on Linux. Works fine. > But on FreeBSD it fails to compile due to the version statement: > > [version (CRuntime_Glibc):]( > https://github.com/dlang/dmd/blob/0cfdd7a589fd34fdf91db72e4999b4920efe5dc2/d > runtime/src/core/sys/posix/mqueue.d#L31) > > While the file seems to be perfectly fine for FreeBSD too. > > If I comment that line out, the app builds but have to supply the > linker the lib -ltr in dub.sdl to get it linking too: > > ``` > lflags "-lrt" > ``` > > Which I don't have to do on Linux. > > How to process from here to supply a PR? > Supposedly that version statement is there for a reason. How do I > also incorporate FreeBSD? Everything in core.sys is supposed to be in version statements specific to each OS (and Linux complicates that further by having different libcs, resulting in even more version statements). The core.sys.posix files are supposed to contain only stuff from standard POSIX, whereas core.sys.linux contains Linux-specific stuff, core.sys.freebsd contains FreeBSD-specific stuff, etc. So, a file in core.sys.posix really shouldn't be doing version (CRuntime_Glibc): Rather, it should be version (CRuntime_Glibc) { } and the other POSIX OSes should have their own version statements in there with declarations which match what they have (which isn't necessarily the same as Glibc on Linux) - though only the standard POSIX declarations should be in there. So, for instance, FreeBSD's standard POSIX declarations for mqueue should go in version (FreeBSD) { } within that core.sys.posix.mqueue, but if it has stuff in mqueue.h which is not standard POSIX, then it would need to go in core.sys.freebsd.mqueue instead. You can see this pattern with other files in core.sys, e.g. https://github.com/dlang/dmd/blob/0cfdd7a589fd34fdf91db72e4999b4920efe5dc2/ druntime/src/core/sys/posix/time.d vs https://github.com/dlang/dmd/blob/0cfdd7a589fd34fdf91db72e4999b4920efe5dc2/ druntime/src/core/sys/freebsd/time.d So, the correct thing to do with core.sys.posix.mqueue is to fix it so that all of the existing declarations are in version (CRuntime_Glibc) { } and version (CRuntime_Glibc): is gone. Then version (FreeBSD) { } needs to be added with the standard POSIX declarations from FreeBSD. None of the declarations should be shared across OSes. https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/mqueue.h.html shows what's standard, but you'll have to get the actual C declarations from /usr/include/mqueue.h on FreeBSD and convert them to the appropriate declarations for D. As for any linker commands, they may vary between OSes. All that druntime provides for language bindings is the declarations. If a particular OS requires any linker flags for any particular symbols, then you'll need to take care of that with your build just like you would with C/C++. But ultimately, which bindings druntime has is highly dependent on what someone needed previously, since usually, they get put in there when someone needs them rather than anyone trying to add them all. And in this case, Sociomantic needed them for Linux, so they got them added, but either no one else has needed the bindings, or no one else has needed them for anything other than Linux with glibc - or if they did, they didn't get them into druntime. - Jonathan M Davis
Re: impure
On Friday, April 5, 2024 3:11:42 AM MDT Dom DiSc via Digitalmars-d-learn wrote: > On Sunday, 24 March 2024 at 09:16:20 UTC, Jonathan M Davis wrote: > > So, yes, you've run into a problem that it would be nice to > > have a better fix for, but even if we could negate attributes > > in general, there are good reasons to prefer to avoid > > mass-applying attributes. > > I don't see it as "mass-applying attributes" rather than changing > the default to something more sane, so that I have to apply > *less* attributes to any single function. The core issue is that the place that is applying the attributes is far from what they're being applied to, which makes it extremely easy to miss them and not know which attributes are actually being applied - particularly when reviewing code, since then you tend to be looking primarily at diffs and not the entire file. It's going to be less of a problem with personal projects, but with any project where multiple people work on the code, it can start being a problem. Ultimately, having the attributes directly on what they apply to tends to result in fewer maintenance issues - particularly with larger projects. > If the addition of new keywords (like "throws", "@gc" and > "impure") is a problem, why not doing it like @nogc(false) or > @nogc=false (likewise for nothrow and pure)? There are a number of ways that it could be done, but regardless, we'd need a DIP and an implementation, and neither has happened. It's a known problem but hasn't been a big enough problem to have been made a priority. - Jonathan M Davis
Re: How can I get an identifiquer of an usb or a harddisk? using C or Cpp or Dlang
On Monday, April 1, 2024 5:37:56 PM MDT dany via Digitalmars-d-learn wrote: > Actually I would get an ID's Usb That's going to depend on the operating system, and it's also going to depend on exactly what kind of ID you're looking for. - Jonathan M Davis
Re: How to make fields inaccessible (unreadable and unachangeable) outside of the structure?
On Friday, March 29, 2024 4:50:53 PM MDT curiousprogramma08 via Digitalmars-d- learn wrote: > ```d > struct QueueNode > { > > private int data; > private QueueNode *next = null; > > this(int data) > { > this.data = data; > } > } > ``` > I also tried to write it like this too: > > ```d > struct QueueNode > { > > private: > int data; > QueueNode *next = null; > > public: > this(int data) > { > this.data = data; > } > } > ``` > (I will use readonly ```@property``` as only way to read them) > > But ```data``` and ```next``` can be changed and can be read from > outside of the structure. What to do to prevent fields from being > read and changed from outside the structure? In D, a private symbol is private to the module, not the type. A package symbol is then private to the modules within that package. And a public symbol is then available to anything that imports the module that it's in. D does not provide a way to make anything within a module inaccessible to other code within that same module. The module is treated as the level of encapsulation. For the most part, this simplifies the code (e.g. friends aren't necessary, unlike in C++, and it's much easier to write unit tests which need to have access to a type's internals), and if your module is so large that you have to worry about code within a module accidentally accessing other code within that module, then your module is probably too large anyway. So, if don't want code to be able to access any of the private members of your QueueNode type, that code will need to be in a separate module rather than in the same module as the QueueNode type. - Jonathan M Davis
Re: Setting up a final switch from a list
On Thursday, March 28, 2024 4:21:03 PM MDT Salih Dincer via Digitalmars-d- learn wrote: > How can we add all members of an enum type to a list without > duplicating code? As the documentation for EnumMembers explains, you can use std.meta.NoDuplicates to strip out duplicates if you want to do something like generate a switch statement from the list of enum members. https://dlang.org/phobos/std_traits.html#EnumMembers - Jonathan M Davis
Re: impure
On Sunday, March 24, 2024 1:41:41 AM MDT Dom DiSc via Digitalmars-d-learn wrote: > I'm creating a library that is completely pure, but it doesn't > compile with pure: at the top because of one impure unittest > (which uses random to test some things only probabilistic)! > > So do I really need to declare every function pure individually > because of a test?!? > > Can we please have a @impure attribute? > And by the way also @throws and @gc? > That would make live so much easier... It's been brought up a number of times before that it would be desirable to have a way to negate attributes, and maybe we'll get that ability at some point, but for now, we don't have it. The only attributes that can be negated are @safe, @trusted, and @system, because using one of them directly on a function overrides any others that are applied more globally. So, for now, you cannot apply pure to an entire module and then have it not apply to something within the module (though you could put that one test at the top before you apply pure). Another thing you could do would be to use debug {} to ignore attributes within that block (though then that code will only be run when building with -debug). How much sense that makes depends on what your test is doing, but it is a way to get around pure in code that isn't intended to be used in production. All of that being said, I'd be inclined to argue that in general, mass-applying attributes is asking for trouble. It works to a point, but it makes it easy to forget which attributes apply, and in some cases, attributes get ignored when they're mass-applied (though that's mostly on types IIRC). It makes more sense when you're applying an attribute to the entire module and not just a section of a module, but it does have a tendency to become a maintenance problem - particularly when it's code that more than one person works on. It also makes code harder to review, because diffs won't include any of the attributes that are being mass-applied, making it easy to miss the fact that a particular attribute applies to the code being changed. So, yes, you've run into a problem that it would be nice to have a better fix for, but even if we could negate attributes in general, there are good reasons to prefer to avoid mass-applying attributes. - Jonathan M Davis
Re: Mutate immutable inside shared static constructor
On Saturday, March 23, 2024 3:23:23 PM MDT Nick Treleaven via Digitalmars-d- learn wrote: > I've not used static constructors before, but it seems like the > following should not be allowed: > > ```d > import std.stdio; > > immutable int x; > > @safe shared static this() > { > x.writeln(); // 0 > x = 5; > x.writeln(); // 5 > x = 6; > x++; > assert(x == 7); > } > ``` > Should I file a bug to require that `x` is only written to once? > That would make it consistent with class constructors: > > ```d > class C > { > immutable int x; > this() > { > x = 5; > x = 6; // error, x initialized multiple times > } > } > ``` Yes, it's a bug. It's a clear violation of the type system if a non-mutable variable is ever given a value more than once. It should be initialized, and then it should be treated as illegal to ever assign to it - or to do anything else which would mutate it. So, clearly, the logic in static constructors with regards to non-mutable variables is overly simple at the moment. - Jonathan M Davis
Re: Mutability issue
On Saturday, March 23, 2024 1:30:29 PM MDT Menjanahary R. R. via Digitalmars- d-learn wrote: > The next code works as is but ... > > ``` > import std.stdio; > import std.traits; > > bool isPrime(T)(T n) if (isIntegral!T) { > if (n <= T(3)) return n > T(1); > > if (n % T(2) == T(0) || n % T(3) == T(0)) return false; > > for (T candidate = T(5); candidate * candidate <= n; > candidate += T(6)) { > if (n % candidate == T(0) || n % (candidate + T(2)) == > T(0)) { > return false; > } > } > > return true; > } > > T nextPrime(T)(T n) if (isIntegral!T) { > if (n < T(2)) > return T(2); > > T candidate = (n % T(2) == T(0)) ? n + T(1) : n + T(2); // > Start from next Odd > > for (;; candidate += T(2)) { // Skip even > if (isPrime(candidate)) { > return candidate; > } > } > } > > void main() { > int num = 10; // Example starting number > writeln("\nNext prime after ", num, " is ", nextPrime(num)); > } > ``` > > ... it doesn't at all once I change `int num = 10;` to `const int > num = 10;`. I'm confused > > How to fix it? Well, when nextPrime is instantiated, the type of T is inferred from the function argument. So, if num is int, then T is int, whereas if num is const int, then T is const int. The same with isPrime. And so, when you declare candidate to be T, it'll be const int when num is const int, in which case, you can't use += on it, because that would mutate it. One way to fix this would be to do something like alias U = Unconst!T; at the top of each of your functions (Unconst is from std.traits and removes const, inout, and immutable from the type), and then instead of using T within the function, you use U - though if you want to return a mutable value, then you'd want to change nextPrime to return auto instead of T. Another approach that does basically the same thing would be to change the function signatures to bool isPrime(T : const U, U)(T n) if (isIntegral!T) T nextPrime(T : const U, U)(T n) if (isIntegral!T) https://dlang.org/spec/template.html#argument_deduction in the spec talk about that trick a bit, but it's basically a way to declare a second template parameter based on the first one. It's more obtuse if you're not used to reading that sort of thing, but it infers U based on the fact that T has to be implicitly convertible to const U, which in effect means that U is mutable whether T was mutable, const, immutable, or inout. Either way, isIntegral!T still restricts T to be an integral type. Ultimately, the effect is the same as declaring alias U = Unconst!T; inside the function, but it's then part of the function signature, and you didn't need to instantiate Unconst. And of course, like the first solution, you would need to change the internals of those functions to use U instead of T. So, which you go with is a matter of personal preference. On the other hand, if you don't absolutely need to retain the original type, an even simpler solution is to just use long. bool isPrime(long n) long nextPrime(long n) since then any of the built-in integral types will work with it - but of course, the result is then long, not int or whatever it is that you passed in. Regardless, the key thing to understand here is that when templated functions infer the types of their arguments, any qualifiers on the type are normally left on the type. The one exception is dynamic arrays. They're inferred as having the type you get when slicing them - which is tail-const. E.G. immutable string s; static assert(typeof(s[]) == string); or const(int[]) arr; static assert(typeof(arr[]) == const(int)[]); For better or worse, arrays do that to avoid requiring that you explicitly slice them to pass them to range-based functions in the cases where you did something like declare a string to be immutable. But nothing like that happens with any other types, so if you pass a const Foo - or a const int - to a templated function, it's instantiated with that exact type. - Jonathan M Davis
Re: Implicit conversion of string to array of immutable ubytes
On Saturday, March 23, 2024 12:11:15 AM MDT Per Nordlöw via Digitalmars-d- learn wrote: > Why doesn't string implicitly convert to immutable(ubyte)[] in > @safe mode? Why would it? They're different types. Their elements happen to have the same size, but that doesn't mean that they're used for the same thing at all. And having them be implicitly convertible could cause serious problems with overloading. If you want to do that conversion without a cast, then you can just use std.string.representation (which will do the cast internally). - Jonathan M Davis
Re: The std.file rename method fails in the docker environment.
On Wednesday, March 13, 2024 3:49:55 PM MDT zoujiaqing via Digitalmars-d-learn wrote: > On Wednesday, 13 March 2024 at 21:21:21 UTC, Jonathan M Davis > > wrote: > > On Wednesday, March 13, 2024 3:03:30 PM MDT zoujiaqing via > > > > Digitalmars-d-learn wrote: > >> upload file to server in docker, but upload directory volume > >> to host machine. > >> > >> Exception error: > >> ``` > >> Invalid cross-device link > >> ``` > >> > >> Have other function like move(a, b) ? > >> > >> https://github.com/huntlabs/hunt-framework/blob/master/source/hunt/framew > >> ork /file/File.d#L102> > > Well, the subject of your post mentions std.file, and then you > > link to a framework that does basically the same thing. So, I > > don't know what you're actually using. > > > > However, both of those functions use the OS function, rename, > > which renames the file within a file system, but it can't move > > files across file systems. Strictly speaking, it's not possible > > to move a file across file systems. What a program like mv does > > when the destination is on a different file system from the > > source file is copy the file and then delete the original. So, > > if you want to "move" a file across file systems within your > > program, you'll have to do the same thing. > > > > There may be a projcet on code.dlang.org which has a function > > which tries to move the file within the file system and then > > does a copy and remove instead if moving within the file system > > doesn't work, but otherwise, you'll have to implement that > > yourself, which could be as simple as catching the any > > exceptions from move and then attempting to copy the file and > > then remove it if an exception was thrown. > > > > - Jonathan M Davis > > this is bug in D. > > Docker run app code: > ```d > reanme("/tmp/aaa", "/data/attachments/aaa"); > ``` > > docker volume path: > ```txt > VOLUME /data/attachments > ``` > > docker compose yml: > ```yml > volumes: >- /data/attachments:/data/attachments > ``` > > Error exception: > ``` > Invalid cross-device link > ``` How is it a bug in D? You are attempting to rename a file across filesystems, and that's not possible. The error is coming from the OS function - https://www.man7.org/linux/man-pages/man2/rename.2.html - and it's telling you what the problem is. You are attempting to rename a file across devices / filesystems, which rename does not support. Linux's rename doesn't even support renaming across different mount points if that filesystem is mounted in multiple places. It's designed to do an atomic move within a filesystem, and that's it. The documentation for std.file's rename even explains that it won't work across filesystems / mount points / drives: https://dlang.org/phobos/std_file.html#rename If you want to move a file between two separate filesystems, then you need to copy the file, not rename it, and then if you want the original to be gone, you can then remove it. You may not like that, but you're trying to use rename to do something that it does not support, because the OS function that it's a wrapper for does not support it. std.file could theoretically add a higher level wrapper that attempted to use rename and then copied the file and removed the original if rename failed, but std.file does not currently provide such a function, and rename is not intended to be that function. It's just a cross-platform wrapper around the OS function, rename. So, while rename may not do what you want, it is working as intended, and it's not a bug. - Jonathan M Davis
Re: The std.file rename method fails in the docker environment.
On Wednesday, March 13, 2024 3:03:30 PM MDT zoujiaqing via Digitalmars-d-learn wrote: > upload file to server in docker, but upload directory volume to > host machine. > > Exception error: > ``` > Invalid cross-device link > ``` > > Have other function like move(a, b) ? > > https://github.com/huntlabs/hunt-framework/blob/master/source/hunt/framework > /file/File.d#L102 Well, the subject of your post mentions std.file, and then you link to a framework that does basically the same thing. So, I don't know what you're actually using. However, both of those functions use the OS function, rename, which renames the file within a file system, but it can't move files across file systems. Strictly speaking, it's not possible to move a file across file systems. What a program like mv does when the destination is on a different file system from the source file is copy the file and then delete the original. So, if you want to "move" a file across file systems within your program, you'll have to do the same thing. There may be a projcet on code.dlang.org which has a function which tries to move the file within the file system and then does a copy and remove instead if moving within the file system doesn't work, but otherwise, you'll have to implement that yourself, which could be as simple as catching the any exceptions from move and then attempting to copy the file and then remove it if an exception was thrown. - Jonathan M Davis
Re: static functions?
On Monday, March 11, 2024 10:51:48 AM MDT Andy Valencia via Digitalmars-d- learn wrote: > On Monday, 11 March 2024 at 16:25:13 UTC, Jonathan M Davis wrote: > > ... > > But what exactly static means varies based on the context. > > Thank you for the list! But none of those appear to apply to a > function defined in the outermost scope of the module. Is static > accepted here--but has no actual effect? There are a number of cases where D allows you to use attributes that are then ignored when they're used on a symbol where they don't make sense. It's particularly useful when using the : syntax, since then you can apply an attribute to the file as a whole without getting a bunch of errors about that attribute not applying to some of the symbols within the module, but it does have the downside of making it less obvious when an attribute is ignored. > I will look at the privacy controls--thanks again. Yes, that's what you want if you want to control which symbols are visible outside the module. - Jonathan M Davis
Re: static functions?
On Monday, March 11, 2024 9:56:24 AM MDT Andy Valencia via Digitalmars-d-learn wrote: > Leveraging my knowledge of C, I assumed a "static" function would > be hidden outside of its own source file. I can't find any > statement about the semantics of a static function in the > documentation, and in practice (ldc2 on Linux) it doesn't hide > the function? No, static does nothing of the sort in D. You can read the documentation here: https://dlang.org/spec/attribute.html#static But what exactly static means varies based on the context. For module constructors and destructors, it means that that constructor or destructor runs once for the entire program, whereas otherwise, they would run once per thread. For member functions (i.e. functions on structs or classes), it means that the function has no this reference. So, it's a function on the struct/class itself and not associated with an instance of the struct/class, whereas non-static member functions must be called on an instance of that struct or class. For nested functions, it means that the function does not have access to variables inside the function that it's nested in (whereas a non-static member function has access the symbols in the function that it's declared in). For member variables (that is, variables on a struct or class), it makes it so that there is only one instance of that variable for that struct or class per thread rather than one per instance of the struct or class. For variables within a function, it makes it so that there is only one instance of that variable per thread (as opposed to the variable only existing for the duration of a specific function call). For nested structs or classes, it means that they don't have access to their outer scope (generally either the function that they're declared in or the class or struct that they're declared in). I'm probably missing some other uses of static, but those are the ones that come to mind at the moment. Aside from module constructors and destructors, I can't think of any case off the top of my head where static does anything at module scope. So, putting static on a function at module scope (rather than within a struct or class) does nothing. If you want to control the visibility of any symbol within a module, then you need to use the visibility attributes: https://dlang.org/spec/attribute.html#visibility_attributes And specifically, to make a symbol only be visible inside a module, you mark it with private. - Jonathan M Davis
Re: chain of exceptions, next method
On Wednesday, March 6, 2024 6:06:34 AM MST kdevel via Digitalmars-d-learn wrote: > On Saturday, 10 September 2022 at 08:48:39 UTC, Andrej Mitrovic > > wrote: > > [...] > > I wish the compiler would rewrite scope(failure) to use chained > > exceptions. Otherwise any exceptions thrown within > > scope(failure) can end up losing information about what was the > > original exception that was thrown. > > Ran into this issue with the following ordering bug: > > auto tmpfilename = fn.dup ~ ".XX\0"; > int fd = mkstemp (tmpfilename.ptr); > scope (failure) remove (tmpfilename); // bug: > if (fd == -1) >throw new Exception (strerror(errno).to!string); > > The error thrown was > > Failed to remove file ... > > Is there any work in progress to chain the exceptions in > scope(failure)? If anything, I think that it's been decided that chained exceptions were a mistake. So, if things go in any direction with them, it's likely to be towards removing them, not doing more to support them. - Jonathan M Davis
Re: Are exceptions caught in unittests?
On Friday, February 16, 2024 1:06:26 AM MST Ferhat Kurtulmuş via Digitalmars- d-learn wrote: > On Friday, 16 February 2024 at 07:43:24 UTC, Ferhat Kurtulmuş > > wrote: > > When I tried to catch exceptions in my unit test, I found that > > exceptions were not thrown or caught in the unit test blocks. > > So, I cannot use assertThrown at all. Is this a bug or expected > > behavior that I don't know? > > > > Using LDC 1.36.0 on Windows. > > > > https://github.com/aferust/evalex/blob/main/source/evalex.d#L694 > > Even a test code like this does not work: > > ```d > string division(int a, int b) { > string result = ""; > > try { >if( b == 0 ) { > throw new Exception("Cannot divide by zero!"); >} else { > result = format("%s",a/b); >} > } catch (Exception e) { >result = e.msg; > } > > return result; > } > > assertThrown({division(50, 0);}); > ``` 1. assertThrown does not test whether something somewhere in what you called threw an exception. It asserts that it catches an exception. Your example here is catching the exception and returning so the exception never escapes the division function, and there's nothing for assertThrown to catch. 2. You're just passing a lambda to assertThrown without actually calling it. The first argument to assertThrown is lazy, which means that it creates a delegate from what you pass to it. assertThrown then calls that delegate. However, in your example here, that delegate just contains a lambda declaration, so it would basically be the equivalent of having void foo() { {division(50, 0);}; } which would be like doing void foo() { int i; } in the sense that the function just contains a declaration that does nothing. The normal thing to do here would be to not use a lambda at all, since it just contains a function call. assertThrown(division(50, 0)); but in cases where you actually need a lambda, because you're doing something more complex than simply calling a function, you need to call it when passing it. e.g. assertThrown({division(50, 0);}()); That then would be like declaring void foo() { {division(50, 0);}(); } so when assertThrown calls the delegate, the delegate actually calls the lambda. But regardless, the expression that you pass to assertThrown has to actually throw an Exception if you want it to pass, and division doesn't do that, because it catches the Exception internally. If what you want to test is whether something deeper down the stack threw an Exception that got caught, then you're out of luck. That's the sort of thing where you'd need a debugger or some other tool that tracked what the code was doing. assertThrown is just a simple library function that helps you test that an expression throws an exception - generally with the idea that it allows you to test that a piece of code throws an exception when it's suposed to (e.g. because you gave it bad input). void assertThrown(T : Throwable = Exception, E) (lazy E expression, string msg = null, string file = __FILE__, size_t line = __LINE__) { import core.exception : AssertError; try expression(); catch (T) return; static if (!is(immutable E == immutable noreturn)) throw new AssertError("assertThrown failed: No " ~ T.stringof ~ " was thrown" ~ (msg.length == 0 ? "." : ": ") ~ msg, file, line); } - Jonathan M Davis
Re: what was the problem with the old post blit operator already ?
On Wednesday, February 14, 2024 7:17:15 PM MST Basile B. via Digitalmars-d- learn wrote: > From what I remember, it was that there was no reference to the > source. Things got blitted and you had to fix the copy, already > blitted. Was that the only issue ? There were probably some use cases where you needed access to both the source and the destination so that you could do something to the source as well, but the core problem was simply that blitting and then mutating the copy to fix it doesn't work with const or immutable objects, since it would violate the type system to cast away const or immutable to fix the copy. The only way to work properly with const or immutable is to construct the object with the changes in the first place rather than mutating the copy after the fact. - Jonathan M Davis
Re: std.string.assumeUTF() silently casting mutable to immutable?
On Tuesday, February 13, 2024 12:40:57 AM MST Forest via Digitalmars-d-learn wrote: > I may have found a bug in assumeUTF(), but being new to D, I'm > not sure. > > The description: > > Assume the given array of integers arr is a well-formed UTF > > string and return it typed as a UTF string. > > ubyte becomes char, ushort becomes wchar and uint becomes > > dchar. Type qualifiers are preserved. > > The declaration: > > ```d > auto assumeUTF(T)(T[] arr) > if (staticIndexOf!(immutable T, immutable ubyte, immutable > ushort, immutable uint) != -1) > ``` > > Shouldn't that precondition's `immutable T` be simply `T`? > > As it stands, I can do this with no complaints from the > compiler... > > ```d > string test(ubyte[] arr) > { > import std.string; > return arr.assumeUTF; > } > > ``` > > ...and accidentally end up with a "string" pointing at mutable > data. > > Am I missing something? It's not a bug in assumeUTF. if you changed your code to string test(ubyte[] arr) { import std.string; pragma(msg, typeof(arr.assumeUTF)); return arr.assumeUTF; } then the compiler will output char[] because assumeUTF retains the type qualifier of the original type (as its documentation explains). Rather, it looks like the problem here is that dmd will implictly change the constness of a return value when it thinks that it can do so to make the code work. Essentially, that means that the function has to be pure and that the return value can't have come from any of the function's arguments. And at a glance, that would be true here, because no char[] was passed into assumeUTF. However, casting from ubyte[] to char[] is @safe, so dmd should be taking that possibility into account, and it's apparently not. So, there's definitely a bug here, but it's a dmd bug. Its checks for whether it can safely change the constness of the return type apparently aren't sophisticated enough to catch this case. - Jonathan M Davis
Re: The difference between the dates in years
On Saturday, February 10, 2024 7:31:47 PM MST Steven Schveighoffer via Digitalmars-d-learn wrote: > On Saturday, 10 February 2024 at 23:48:56 UTC, Jonathan M Davis > > wrote: > > If I understand correctly, he cares about how far into the > > month the dates > > are, whereas diffMonths ignores the smaller units, meaning that > > you get the > > same result no matter when in the month the dates are. So, > > 2000-05-10 - 1990-05-09 would give 10, whereas 2000-05-10 - > > 1990-05-30 > > would give 9. diffMonths / 12 would give 10 in both cases. > > I thought `diffMonths` was actually already taking this into > account... > > Looking at the impl, it's pretty simple. > > Would it make sense to have an overload that takes into account > the day as well as the month/year? This kind of stuff is > sometimes tricky to get right. Possibly. Given the trickiness involved, there probably should be a function in std.datetime to handle it, but I'll have to think about what the best way to do it would be in terms of the API. It is kind of similar to how some functions behave differently depending on how you want to treat the end of the month (e.g. whether adding a month to May 31st gives you June 30th or July 1st), but it's also not quite the same. So, the enum related to that wouldn't be appropriate, but it's yet another edge case where sometimes you want one behavior, and at other times, you want the other behavior. - Jonathan M Davis
Re: The difference between the dates in years
On Saturday, February 10, 2024 4:20:33 PM MST Steven Schveighoffer via Digitalmars-d-learn wrote: > On Saturday, 10 February 2024 at 15:53:09 UTC, Alexander Zhirov > > wrote: > > Is it possible to calculate the difference between dates in > > years using regular means? Something like that > > > > > > ``` > > writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1))); > > ``` > > > > At the same time, keep in mind that the month and day matter, > > because the difference between the year, taking into account > > the month that has not come, will be less. > > > > My abilities are not yet enough to figure it out more elegantly. > > Maybe I'm not understanding the question, but why not that result > / 12? If I understand correctly, he cares about how far into the month the dates are, whereas diffMonths ignores the smaller units, meaning that you get the same result no matter when in the month the dates are. So, 2000-05-10 - 1990-05-09 would give 10, whereas 2000-05-10 - 1990-05-30 would give 9. diffMonths / 12 would give 10 in both cases. - Jonathan M Davis
Re: The difference between the dates in years
On Saturday, February 10, 2024 3:11:48 PM MST Brad Roberts via Digitalmars-d- learn wrote: > Back when I was doing lots of software developer interviews, one of my > frequent questions involved date math. This wasn't because it's > difficult from a coding standpoint, but that it's NOT a coding problem. > The key part of the question is realization that it's a requirements > question. The thing that makes dates complicated is defining what the > question actually is. > > The topic _seems_ like it should be simple, but the deeper you dig the > more you realize it's anything but simple. Indeed. And because it seems simple at first, it's very common for code to be written which does the wrong thing with regards to dates and times - often which seems like it does the right thing, because it works a lot of the time, when in fact, there are edge cases where it definitely does the wrong thing (e.g. with regards to leap years or DST). And math around months is a prime area where it's difficult to get right in part because different people have different requirements depending on the actual problem that they're trying to solve. - Jonathan M Davis
Re: The difference between the dates in years
On Saturday, February 10, 2024 8:53:09 AM MST Alexander Zhirov via Digitalmars-d-learn wrote: > Is it possible to calculate the difference between dates in years > using regular means? Something like that > > > ``` > writeln(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1))); > ``` > > At the same time, keep in mind that the month and day matter, > because the difference between the year, taking into account the > month that has not come, will be less. > > My abilities are not yet enough to figure it out more elegantly. Well, diffMonths could be used to build what you want, but std.datetime doesn't really provide a direct solution for it. Subtracting one Date from another gives you a Duration, but that doesn't take the month or year into account, because the lengths of the months are variable. diffMonths is the solution to get around that, since it does take the exact years and months involved to give you the number of months. However, it doesn't take the days of the month into account. It's just diffing the months themselves, and any day of the month counts as being part of that month. However, after some mucking around, I think that I have a solution built on top of diffMonths, though of course, it's possible that I screwed up somewhere with my test dates. I named it yearsApart rather than diffYears, since unlike diffMonths, it does take the smaller units into account. But of course, you can name it whatever you want. import std.datetime.date : Date; int yearsApart(Date lhs, Date rhs) { auto months = lhs.diffMonths(rhs); auto years = months / 12; if(years == 0) return 0; auto remainder = months % 12; if(remainder != 0) return years; if(months >= 0) return lhs.day >= rhs.day ? years : years - 1; return lhs.day <= rhs.day ? years : years + 1; } unittest { assert(yearsApart(Date(1999, 3, 1), Date(1999, 1, 1)) == 0); assert(yearsApart(Date(1999, 1, 1), Date(1999, 3, 1)) == 0); assert(yearsApart(Date(1999, 3, 1), Date(1998, 1, 1)) == 1); assert(yearsApart(Date(1998, 3, 1), Date(1999, 1, 1)) == 0); assert(yearsApart(Date(2000, 12, 1), Date(1995, 12, 1)) == 5); assert(yearsApart(Date(1995, 12, 1), Date(2000, 12, 1)) == -5); assert(yearsApart(Date(2000, 12, 2), Date(1995, 12, 1)) == 5); assert(yearsApart(Date(1995, 12, 1), Date(2000, 12, 2)) == -5); assert(yearsApart(Date(2000, 12, 1), Date(1995, 12, 2)) == 4); assert(yearsApart(Date(1995, 12, 2), Date(2000, 12, 1)) == -4); assert(yearsApart(Date(2000, 2, 29), Date(1999, 2, 28)) == 1); assert(yearsApart(Date(1999, 2, 28), Date(2000, 2, 29)) == -1); assert(yearsApart(Date(2000, 2, 29), Date(1999, 3, 1)) == 0); assert(yearsApart(Date(1999, 3, 1), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(1998, 3, 1)) == 1); assert(yearsApart(Date(1998, 3, 1), Date(2000, 2, 29)) == -1); assert(yearsApart(Date(2001, 3, 1), Date(2000, 2, 29)) == 1); assert(yearsApart(Date(2000, 2, 29), Date(2001, 3, 1)) == -1); assert(yearsApart(Date(2005, 3, 1), Date(2000, 2, 29)) == 5); assert(yearsApart(Date(2004, 3, 1), Date(2000, 2, 29)) == 4); assert(yearsApart(Date(2003, 3, 1), Date(2000, 2, 29)) == 3); assert(yearsApart(Date(2002, 3, 1), Date(2000, 2, 29)) == 2); assert(yearsApart(Date(2001, 3, 1), Date(2000, 2, 29)) == 1); assert(yearsApart(Date(2000, 3, 1), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2001, 3, 1)) == -1); assert(yearsApart(Date(2000, 2, 29), Date(2002, 3, 1)) == -2); assert(yearsApart(Date(2000, 2, 29), Date(2003, 3, 1)) == -3); assert(yearsApart(Date(2000, 2, 29), Date(2004, 3, 1)) == -4); assert(yearsApart(Date(2000, 2, 29), Date(2005, 3, 1)) == -5); assert(yearsApart(Date(2005, 2, 28), Date(2000, 2, 29)) == 4); assert(yearsApart(Date(2004, 2, 29), Date(2000, 2, 29)) == 4); assert(yearsApart(Date(2004, 2, 28), Date(2000, 2, 29)) == 3); assert(yearsApart(Date(2003, 2, 28), Date(2000, 2, 29)) == 2); assert(yearsApart(Date(2002, 2, 28), Date(2000, 2, 29)) == 1); assert(yearsApart(Date(2001, 2, 28), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 28), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2000, 2, 28)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2000, 2, 29)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2001, 2, 28)) == 0); assert(yearsApart(Date(2000, 2, 29), Date(2002, 2, 28)) == -1); assert(yearsApart(Date(2000, 2, 29), Date(2003, 2, 28)) == -2); assert(yearsApart(Date(2000, 2, 29), Date(2004, 2, 28)) == -3); assert(yearsApart(Date(2000, 2, 29), Date(2004, 2, 29)) == -4); assert(yearsApart(Date(2000, 2, 29), Date(2005, 2, 28)) == -4); } - Jonathan M Davis
Re: User defined type and foreach
On Thursday, January 25, 2024 1:57:56 AM MST Jim Balter via Digitalmars-d- learn wrote: > The specification of ranges, which is independent of > the D language, says that the way to copy a range is to use > save(). I'm sorry, but you're misunderstanding the range specification if you think that save is the only way to copy a range or that the range specification makes any such guarantee. And if there is any official documentation that makes any such claim, then it needs to be fixed, because ranges have never worked that way, and they cannot work that way unless ranges in general are non-copyable, which is not the case at all. What save does is give you a guaranteed way to get a copy which can be independently iterated. However, it's perfectly valid to simply copy a range and then use the copy. They're not non-copyable types, and range-based code in general copies ranges all over the place - both with basic input ranges and with forward ranges. They're copied when they're passed to functions. They're copied when they're wrapped by other ranges. They're copied when they're passed to foreach. This is all falls within the expected use of ranges. What you don't get from those copies is the guarantee that the copy is independent, which is why save is necessary in cases where you need to be sure that you're getting an independent copy of a range. I gave that long explanation to try to get across what was happening with the copy semantics of ranges and what the consequences of that are. If that wasn't helpful to you, then I'm sorry. Either way, if you think that it goes against the range API to copy a range any way other than by calling save, then you are misunderstanding how ranges work, and looking at how Phobos uses ranges should make that clear, because it copies them all over the place, including with code that works on basic input ranges. The range API guarantees that copying a range will result in the copy having the same elements in the same order as the original would have had had it not been copied, because without that guarantee, range-based code in general simply wouldn't work. Range-based code in general relies on that ability to copy a range when passing it around. Now, what happens to the original after the copy has been made is not specified by the range API, and that's why save is necessary. But you can still copy a range and use that copy in generic code so long as you don't touch the original again. So, I've tried to explain to you why the current behavior is expected and does not violate the range specification. If that's not enough for you, then I'm sorry. Maybe someone else can help you out, but if what you've said about save were correct, then Phobos as a whole would be violating the range specification - as would the language itself with foreach. But they've been working this way for well over a decade. We would like to improve the situation with the next major version of Phobos so that the copy semantics of ranges are cleaner, and the range API will likely see a redesign to fix that issue, among others, but the current range API is as I've explained it to you, warts and all. - Jonathan M Davis
Re: Constructing arrays of structs
On Tuesday, January 23, 2024 12:32:31 PM MST Stephen Tashiro via Digitalmars- d-learn wrote: > On Tuesday, 23 January 2024 at 18:23:22 UTC, Renato wrote: > > This works , your mistake was to not actually assign the array > > to the class' field! > > > > Change this line: > > > > ```d > > auto array = new Point[][](the_dimension,the_dimension); > > ``` > > > > To this: > > > > ```d > > this.array = new Point[][](the_dimension,the_dimension); > > ``` > > Thank you. > > I don't really understand what the syntax > > new Point[][](the_dimension,the_dimension); > > denotes. Does it represent a function? To look up this topic, > what are the proper keywords? > > By experimentation, I found that "new > Point[the_dimension][the_dimension];" doesn't compile. Except for the first dimension, all subsequent dimensions have to go in the parens, since putting them between the brackets indicates that that dimension is for a static array rather than a dynamic array, so it would change the type of the array (allowing for dynamic arrays of static arrays). As it is, you can probably only put the first dimension between the brackets, because other languages do that, and allowing it makes it easier to port code. Arguably though, for consistency, you should always put the dimensions between the parens when allocating a new dynamic array. - Jonathan M Davis
Re: User defined type and foreach
On Friday, January 19, 2024 3:49:29 AM MST Jim Balter via Digitalmars-d-learn wrote: > On Friday, 17 November 2017 at 17:55:30 UTC, Jonathan M Davis > > wrote: > > When you have > > > > foreach(e; range) > > > > it gets lowered to something like > > > > for(auto r = range; !r.empty; r.popFront()) > > { > > > > auto e = r.front; > > > > } > > > > So, the range is copied when you use it in a foreach. > > Indeed, and the language spec says so, but this is quite wrong as > it violates the specification and design of ranges ... only > forward ranges are copyable and only via their `save` function. I > have an input range that can only be iterated once; if you try to > do so again it's empty ... but the foreach implementation breaks > that. You should be able to break out of the foreach statement, > then run it again (or another foreach) and it should continue > from where it left off, but copying breaks that. I need to know > how many elements of my range were consumed; copying breaks that. > I got around this by having a pointer to my state so only the > pointer gets copied. I would also note that tutorials such as Ali > Çehreli's "Programming in D – Tutorial and Reference" are unaware > of this breakage: > > " > Those three member functions must be named as empty, popFront, > and front, respectively. The code that is generated by the > compiler calls those functions: > > for ( ; !myObject.empty(); myObject.popFront()) { > > auto element = myObject.front(); > > // ... expressions ... > } > " No spec is being violated, and the behavior is completely expected. The core problem is that the range API doesn't actually specify the semantics of copying a range - and actually can't do so without making breaking changes. D types in general fall into one of three categories with regards to their copy semantics: 1. value types 2. reference types 3. pseudo-reference types When you copy a value type, you get two fully independent copies of the object (e.g. integers are a prime example of this; mutating a copy of an integer has no effect whatsoever on the original). When you copy a reference type, you get two fully dependent copies. The type is either a pointer or a reference (or a struct that holds just a pointer or a reference), and copying it results in another pointer or reference to the same object. So, mutating the object affects all pointers or references to that object. When you copy a pseudo-reference type, you get a partial copy. Typically, you're dealing with a struct which has both members which are value types and members which are reference types. The result is that some operations will affect only the object being mutated, whereas other operations will affect other copies of that object. Dynamic arrays are one example of this. They container a pointer and a size_t which is the length of the array, and reducing the size of the array by mutating the pointer and the length has no effect on other dynamic arrays which point to the same data, but mutating the actual elements affects all dynamic arrays which point to the same data. What this means for ranges is that depending on how they're implemented, you get one of four different behaviors when they're copied: 1. If the range is a value type, then copying the range results in two independent copies, so mutating the copy has no effect on the original. Code can iterate through both ranges independently. 2. If the range is a reference type, then copying the range results in two dependent copies, so mutating the copy mutates the original. Any code that iterates one of the two ranges then affects any code which would try to iterate the original, but the state is consistent across both ranges, because rather than containing their data, the data is elsewhere, and they both point to the same place. 3. If a range is a pseudo-reference type, and its iteration state is copied by value, then copying the range gives you the same behavior as a value type as far as iteration goes. Both the copy and the original can be iterated independently (though depending on the implementation, mutating the elements themselves could affect both ranges). Dynamic arrays fall in this category. 4. If a range is a pseudo-reference type, and its iteration state is not fully copied by value, then you end up with the copy and the original being partially dependent. This means that if you mutate one of them, it will only partially mutate the other, which tends to mean that the other ends up in an invalid state. A common situation where this can occur is if the range stores its front as a member variable, but the rest of its state is stored in another member variable which is a reference type. If you then call popFront on the copy, you'd end up with the copy's front changing, but the original's front wouldn't, and yet, the state they share for the rest of the range would be mutated, so if you then called popFront on the original, you wouldn't get the
Re: Datetime format?
On Thursday, January 18, 2024 4:58:32 PM MST zoujiaqing via Digitalmars-d- learn wrote: > On Thursday, 18 January 2024 at 23:43:13 UTC, Jonathan M Davis > > wrote: > > On Thursday, January 18, 2024 4:26:42 PM MST zoujiaqing via > > > > Digitalmars-d- learn wrote: > >> ```D > >> import std.datetime : Clock, format; > >> import std.stdio : writeln; > >> > >> void main() > >> { > >> > >> auto currentTime = Clock.currTime; > >> > >> auto formattedTime = currentTime.format("%Y-%m-%d > >> > >> %H:%M:%S"); > >> > >> writeln("Formatted Time: ", formattedTime); > >> > >> } > >> ``` > > > > std.datetime does not currently support custom date/time > > formats. It only supports the ISO format, the ISO Extended > > format, and Boost's simple time format. > > > > // e.g. 20240118T163806.5813052 > > auto iso = time.toISOString(); > > > > // e.g. 2024-01-18T16:38:06.5813052 > > auto isoExt = time.toISOExtString(); > > > > // e.g. 2024-Jan-18 16:38:06.5813052 > > auto boostSimple = time.toSimpleString(); > > > > So, if you want a different format, you'll either need to make > > one yourself by calling the various properties on SysTime and > > passing them to something like std.format's format to create a > > string, or there are several packages on https://code.dlang.org > > which have functions for doing custom date/time formatting. > > > > - Jonathan M Davis > > Thank you for your replay. > > So shame! The standard library doesn't have date formatting. It probably should, but it wasn't a priority when std.datetime was written, and I've never gotten around to adding it. > for this example "2024-Jan-18 16:38:06.5813052" > Why use Jan? no 01? > International standards should all apply numbers. > like this: > 2024-01-18 16:38:06.5813052 It uses Jan, because that's Boost's "simple" date/time format. At this point, I consider it a mistake to have put toSimpleString in there or to have had toString use toSimpleString instead of toISOExtString, but it's there because Boost had it with their date/time type. - Jonathan M Davis
Re: Datetime format?
On Thursday, January 18, 2024 4:26:42 PM MST zoujiaqing via Digitalmars-d- learn wrote: > ```D > import std.datetime : Clock, format; > import std.stdio : writeln; > > void main() > { > auto currentTime = Clock.currTime; > > auto formattedTime = currentTime.format("%Y-%m-%d %H:%M:%S"); > > writeln("Formatted Time: ", formattedTime); > } > ``` std.datetime does not currently support custom date/time formats. It only supports the ISO format, the ISO Extended format, and Boost's simple time format. // e.g. 20240118T163806.5813052 auto iso = time.toISOString(); // e.g. 2024-01-18T16:38:06.5813052 auto isoExt = time.toISOExtString(); // e.g. 2024-Jan-18 16:38:06.5813052 auto boostSimple = time.toSimpleString(); So, if you want a different format, you'll either need to make one yourself by calling the various properties on SysTime and passing them to something like std.format's format to create a string, or there are several packages on https://code.dlang.org which have functions for doing custom date/time formatting. - Jonathan M Davis
Re: length's type.
On Wednesday, January 17, 2024 11:33:48 PM MST zjh via Digitalmars-d-learn wrote: > On Thursday, 18 January 2024 at 04:30:33 UTC, Jonathan M Davis > wrote: > but for a lot of code, using auto and size_t makes it > > > so that you don't need to use int, and it would be a big > > problem in general if the language made length int. > > It's hard to imagine that an `'int'` needs to be replaced with > `'auto'`. It's very common in D code to do stuff like auto a = foo(); or auto len = arr.length; That way, you automatically get the correct type. Obviously, there are cases where you need to force a particular type, and that can require casting, but inferring types often simplifies code. It's _very_ common in idiomatic D code to use auto when you don't need to force a specific type. And when dealing with arrays, it's very typical to use either auto or size_t, because then you get the correct integer type regardless of the platform, and then you only need to worry about casting to int in cases where you actually need int for whatever reason. But regardless of whether you want to use auto, there are very good reasons for why length is size_t, and C/C++ made exactly the same choice for the same reasons. You can certainly disagree with that choice, but it's not the kind of thing that stands much chance of ever being changed. - Jonathan M Davis
Re: length's type.
On Wednesday, January 17, 2024 7:55:37 PM MST zjh via Digitalmars-d-learn wrote: > Can you change the type of 'length' from 'ulong' to 'int', so I > haven't to convert it every time! If you mean for arrays, length and array indices are specifically size_t so that their size will match the pointer size for the architecture. On 64-bit systems, that means that it's ulong (whereas on 32-bit systems, it would be uint). If it were int, then you couldn't access all of the elements of larger arrays (and arrays will get that large in some cases - e.g. when dealing with larger files). C/C++ does the same thing. If you want your code to be portable and to be able to handle larger arrays, then it should be using size_t for array indices and length and not int, in which case, you're not typically going to need to convert from ulong to int, because you'd just be using size_t, which would then be ulong on 64-bit systems. Obviously, when you do need to convert to int, then that can be annoying, but for a lot of code, using auto and size_t makes it so that you don't need to use int, and it would be a big problem in general if the language made length int. - Jonathan M Davis
Re: Understanding the Use of Nested Import and Selective Import in D
On Wednesday, January 17, 2024 7:03:18 AM MST Renato via Digitalmars-d-learn wrote: > My main reasoning is that D tools, surprisingly, cannot do > "Optimize Imports" (turns out that with all the metaprogramming > going on , it's impossible to tell for sure which imports are > being used - or so I read in another thread about this topic)... A tool could do it assuming that it's opinionated enough about local imports, and you're willing to let code break if imports that arguably should be local aren't. Otherwise, yeah, you can't really do it in the general case because of conditional compilation. Specifically, the problem is that it's possible to have an import statement which is always compiled into the code but where the symbols from it are only used some of the time. So, for the tool to know which imports are used or not, it would have to somehow be able to hit every conditional branch (from version statements, static if statements, templates, etc.), which isn't really something that can be done. For instance, if you have version statements in your code (e.g. Posix vs Windows), you can have symbols which are used only within a portion of the versioned blocks, but the import is at the top-level and always compiled in. So, if those version statements aren't being compiled in when the tool is run, then it would conclude that the import was unnecessary and remove it, which would then break the code when it's compiled in a situation where those version statements do get compiled in. And of course, the situation is further complicated by the fact that the module being imported could have a different set of symbols depending on conditional compilation. As such, the tool can't really determine for sure that an import isn't used. It _might_ be able to detect whether any code branches depend on conditional compilation and remove unused imports if there aren't any, but with how often conditional compilation is used in typical D code, that's not necessarily very useful, and if you have a situation where one module provides the symbols on one platform, but another module provides them on another platform (which could definitely happen with OS stuff), and both modules are being imported, then even if the module you're checking for unused imports doesn't have conditional compilation, you could still end up removing imports that you shouldn't have due to conditional compilation in the modules being imported. That being said, if the tool is opinionated about local imports, it could be done. Specifically, what it could do is take the stance that all imports that are used in conditionally compiled code must be local (and thus only imported when that code is compiled in), in which case, if it finds an import which isn't actually used, then it can just remove it. That would then break any code that hadn't used local imports to import symbols that were only used within conditionally compiled code, so whether it would really be a desirable tool to have in general would be debatable, but taking that approach should make it possible to have such a tool. Another approach would be to have a linting tool which warned you about possibly unused imports but didn't actually remove any. Since it would be less automatic, it would be more annoying to use, but it would avoid removing imports that were actually used in conditionally compiled code, and if you wanted to get rid of the warning you could make those imports local. Still, you can't really remove all unused imports, because the ones that are conditionally compiled in couldn't be examined properly unless that code was being compiled in, which would potentially require running the tool on a variety of platforms and with a variety of conditions that you wouldn't always run into. So, as is often the case, D's metaprogramming capabiltiies complicate the situation considerably with regards to tooling. - Jonathan M Davis
Re: Understanding the Use of Nested Import and Selective Import in D
On Tuesday, January 16, 2024 1:42:04 PM MST bomat via Digitalmars-d-learn wrote: > Wow, that was... exhaustive. Thanks for that. :) > One more question that I have, though... > > On Tuesday, 16 January 2024 at 19:05:43 UTC, Jonathan M Davis > > wrote: > > The downside of course is that you then have import statements > > throughout your code, and they're often going to be duplicated > > throughout a module (or even within a function if you localize > > them far enough), because separate parts of the code then need > > their own local imports. > > Apart from the aesthetic "clutter" of duplicate imports, will > they also put additional strain on the compiler and/or affect the > resulting binary? I mean, will the imports actually be compiled > in several times? Imports are never compiled in. Importing a D module is nothing like #including a C/C++ header file. It does not result in any code being inserted into the current module, and if it results in anything being added to the binary, it's because a template from that module was instantiated with a new set of arguments, resulting in a new template instantiation that has to end up in the binary. An import statement tells the compiler to allow the current code to use the symbols from the imported module. That requires compiling the imported module sufficiently for the compiler to then let those symbols be correctly used within the module that's doing the importing, but importing a module doesn't make it so that the compiler fully compiles the imported module. To do that, the module has to also be passed to the compiler to be compiled (be it as part of the same compilation process or compiled separately to be linked in later). And once a module has been imported, the compiler has already built the symbol table for that module, so importing the module additional times during the same round of compilation will not result in it being processed again. The import statement will need to be parsed, which isn't free, but it's so cheap in comparison to everything else that you'd likely have a very hard time detecting the cost even in a very large codebase with a lot of local import statements. And if anything, using local imports could reduce compilation times, because if an import is local to a template, and your code doesn't end up instantiating that template (or doesn't compile in a particular branch of a static if or version block), then the compiler doesn't need to do anything with that import. - Jonathan M Davis
Re: Understanding the Use of Nested Import and Selective Import in D
On Tuesday, January 16, 2024 6:19:59 AM MST Orfeo via Digitalmars-d-learn wrote: > I found myself a bit perplexed when it comes to the usage of > "nested imports" and selective imports. It seems that prominent D > programmers have varied opinions on the matter. I would love to > hear your insights and experiences on this topic. > > Here's a quick summary of what I've come across from three > influential D programmers: > > - Adam Ruppe: In his blog post titled [D's selective imports have > effects you may not > want](http://dpldocs.info/this-week-in-arsd/Blog.Posted_2023_11_06.html) > have effects you may not want, Adam advises against the use of selective > imports. He highlights potential unwanted side effects and suggests caution > when employing them. > > - Atila Neves: At DConf 2023, Atila Neves recommended the use of > nested imports. He argues that nested imports can make > refactoring easier and help in assessing the dependencies a > function has. > > - Rober Schadek: Also at DConf 2023, Rober Schadek discouraged > the use of nested imports, taking a stance different from Atila > Neves. > > Now, the big question is: What's your preferred approach? When local imports were introduced, they were pushed as best practice (in part, because Andrei is a big fan of them), and I think that for the most part, they still are, but there's definitely going to be some disagreement on it. The benefits of local imports have to do with encapsulation. When you localize an import as much as possible, you make it clear which parts of the code are using that import. That makes it easier to see which imports are used by a section of code and where symbols are coming from. It also makes it much easier to refactor imports, because you can see what's using the import and see whether it's still needed, whereas if an import is put at the top of a module, it'll probably sit there forever. Tools for removing unused imports would help with that problem, but having the imports be local still helps you reason about the imports for a section of code (and to an extent removes the need for such a tool). And of course, if all of the imports that a function uses are within its body, then removing the function removes all of those imports without you having to even spend the time to figure out what they were. Arguably an even bigger benefit of local imports comes from conditional compilation. When you have a template, static if block, or version statement in your code, the code inside of it may or may not be compiled into your program (depending on what your code is doing). So, by putting the imports used within that code inside of that code, you can avoid having them be imported at all if that code isn't actually compiled in (which is particularly critical in the case of version statements that could use platform-specific modules but helps with avoiding unnecessary compilation in general). The downside of course is that you then have import statements throughout your code, and they're often going to be duplicated throughout a module (or even within a function if you localize them far enough), because separate parts of the code then need their own local imports. So, some folks think that it's just simpler to throw the imports at the top of the module (and particularly for small programs, it probably is). Another issue is that local imports don't really work for stuff in function signatures. For member functions, you can localize imports to the class or struct, but for module-level imports, they have to go at the module level, so they're not local. There was talk of adding a language feature to fix that, but it never happened. However, we did at some point get a template to do it for you. object.d contains imported, which allows you to do stuff like auto foo(imported!"std.datetime".SysTime st) {...} I'm not sure how much it's really used though. I suspect that it's ugly enough that it's not used much, and I don't know if it's even very well known at this point (personally, I keep forgetting that it was added). However, it does have the benefit of making it so that removing the parameter or return type using that template would remove the import, just like removing a function removes any of its local imports in the process. So, it's arguably a good idea, but personally, I find it ugly enough that I don't use it. Another thing to keep in mind (though it also affects module-level imports) is that public, package, and private affect imports - even when used with : or with {} - so if you use an access modifier in a way that affects an import (e.g. if you put public: at the top of your class and then have imports right under it), you can accidentally end up with imports that aren't private. If you're in the habit of just slapping all of your imports at the top of the module, then this is unlikely to be an issue, but if you put them throughout the module (and not just within functions), then it could bite you. So, you do need to be
Re: Would you recommend TDPL today?
On Monday, January 15, 2024 7:25:32 PM MST matheus via Digitalmars-d-learn wrote: > Hi, I'm mostly a lurker in these Forums but sometimes I post here > and there, my first language was C and I still use today together > with my own library (A Helper) which is like a poor version of > STB (https://github.com/nothings/stb). > > I usually use D language sometimes as C on steroids, using AA and > GC and some other features, but I never entered in this realm > very deeply. > > I always wanted to dive in and I always postponed, but I decided > to go a littler deeper, and I thought about going with The D > Programming Language, but as I see it is from 2010, and I wonder > if is it a good resource to go currently? > > I don't care about the age of the book, since I learned C in late > 90's with Kernighan and Ritchie "The C Programming Language", but > at time C was "stable", now I think D maybe has evolved much more > in these 14 years, so I'm a bit on the fence. > > Any thoughts or recommendations? > > Thanks, > > Matheus. >From what I recall, it's mostly still correct, but there are things in there that have since changed or which were never implemented (e.g. synchronized classes never became a thing; synchronized functions still exist, but TDPL talks about them being replaced with synchronized classes and that never happened - and likely will never happen). There's also an errata for it, but AFAIK, that just fixes some mistakes it; it doesn't update it. This wiki entry tries to list some of the differences, but I expect that it also is rather out-of-date at this point: https://wiki.dlang.org/Differences_With_TDPL So, TDPL is a good resource, but you have to take into account the fact that some of the details are wrong, which you may not want to do. In that respect, Ali's book would likely work better: http://ddili.org/ders/d.en/index.html It was written more recently, and I'm pretty sure that Ali has updated it on some basis. I fully expect that there are things that you'd get out of TDPL that you wouldn't get from Ali's book, so there's definitely something to said for reading both, but again, whether that makes sense largely depends on whether you want to deal with figuring out which parts of TDPL are still valid. - Jonathan M Davis
Re: static array is not a range
On Tuesday, January 9, 2024 6:22:24 AM MST bachmeier via Digitalmars-d-learn wrote: > On Tuesday, 9 January 2024 at 10:11:35 UTC, Alexibu wrote: > > It looks like isInputRange is false for arrays with fixed > > length by design. > > > > I can do: > > > > ```d > > float[4] arr; > > foreach(x;arr) > > > >writefln("%s",x) > > > > ``` > > but not : > > > > ```d > > arr.each!(a => a.writefln("%s",a)); > > ``` > > Is there a good reason for this ? > > It took my a long time to figure out. > > Jonathan's been giving you good general information about this. > I'm curious about your partial example. If I fix the writefln > call, it works. > > ``` > import std; > float[4] arr; > void main() { >arr[0] = 1; >arr[1] = 2; >arr[2] = 3; >arr[3] = 4; >arr.each!(a => "%s".writefln(a)); > } > ``` >From the looks of it, each is explicitly designed to work with anything that can be iterated with foreach rather than just ranges. So, unlike a normal range-based functions, it will work directly with a static array (and should take it by ref to avoid copying) - at least so long as the function that it's given compiles properly. Personally, I'd just use a foreach loop and don't see much point in each at all, but looking at its implementation, it does look like the OP should be able to use it with static arrays in spite of the fact that they're not ranges - Jonathan M Davis
Re: static array is not a range
On Tuesday, January 9, 2024 4:13:23 AM MST Alexibu via Digitalmars-d-learn wrote: > On Tuesday, 9 January 2024 at 10:44:34 UTC, Jonathan M Davis > > wrote: > > How would it even be possible for a static array to be a range? > > It has a fixed length. For a type to work as a range, it needs > > to be possible to pop elements off of it, which you can't do > > with a static array. Input ranges must have front, popFront, > > and empty. Dynamic arrays have that from std.range.primitivies > > via UFCS (Universal Function Call Syntax), and that works, > > because it's possible to shrink a dynamic array, but it won't > > work with a static array, because its size will be fixed. > > > > Now, what you can do is slice a static array to get a dynamic > > array which refers to the static array. And since dynamic > > arrays work as ranges, you can use that with range-based > > functions. That being said, you do then have to be careful > > about the dynamic array (or any ranges which wrap it) escaping > > from the scope where the static array is, because if the static > > array goes out of scope and is destroyed, then any dynamic > > arrays referring to it will be referring to invalid memory, and > > you'll get undefined behavior. So, while slicing static arrays > > can be very useful, it needs to be done with caution. > > > > - Jonathan M Davis > > All good information, thanks. > I suppose I use ranges as things that can be arguments to > algorithms in std.algorithm and std.range. > > Although there is no state in the static array itself as you > point out, couldn't we have a temporary input range created and > then the compiler can elide it into whatever the foreach loop > does. > So all the range algorithms could auto convert a static array > into a range backed by the static array ? > > Something like this : (although written by someone more competent) > > ```d > > struct TempRange(X) > { > x[n] * array; > size_t i; > this(static_array a) > { >array = a; >i = 0; > } > X popFront() { return array[i]; } > bool empty() { return i == array.length;} > } > > > R each(R,F)(R r,F f) > static if (isInputRange!R) > { > normal implementation > }else if (isStaticArray!R) > { > return TempRange(r).each(f); > } > ``` If you want a range backed by a static array, simply slice the static array to get a dynamic array. e.g. int[5] a = [1, 2, 3, 4, 5]; int[] arr = a[]; However, it's not something that should be done automatically, because having any kind of pointer or reference to a static array poses the risk of leaking a pointer or reference to the stack - i.e. the exact same problem that you get when taking the address of a local variable. The scope attribute has a limited ability to track escaping references (and DIP 1000 increases those abilities), but ultimately, if you're doing stuff like passing a dynamic array that's a slice of a static array to range-based functions, there's a decent chance that the compiler will not be able to properly detect whether any references to the static array actually escape (which with DIP 1000 tends to mean errors about not being allowed to do stuff, because the compiler can't prove that what you're doing won't escape any references). If you're careful, you can slice a static array and pass the resulting dynamic array to a range-based function, and it'll work just fine, but you have to be very careful that no references / pointers to the static array escape, or you're going to end up referring to memory that used to be static array but is no longer, which would be a serious problem. Any user-defined type that you created which was a pointer to a static array would have the same problem as slicing the static array. If anything, you'd basically just be implementing a more limited form of D's dynamic arrays with such a type. Fundamentally, there really isn't a fully safe way to pass around a pointer to a static array without risking escaping references - not unless the compiler is smart enough to fully determine whether a reference might escape, and it's quite difficult for the compiler to be that smart - particularly when calling functions where the compiler can't necessarily see the source code. Ultimately, you really don't want anything to automatically slice a static array or take its address, because you're risking undefined behavior from references that escape. Static arrays are nice in that they provide a way to have an array of elements without allocating anything on the heap, but if you're going to start passing them around, pretty quickly, you want a dynamic array that refers to memory on the heap and not a static array. Slicing static arrays does provide a middle ground, but it's not completely safe to do so and really can't be, so having it be done implicitly for you is pretty much just asking for bugs. Unfortunately, if you pass a static array to a function that explicitly takes a dynamic array of the type you get when slicing
Re: static array is not a range
On Tuesday, January 9, 2024 3:11:35 AM MST Alexibu via Digitalmars-d-learn wrote: > It looks like isInputRange is false for arrays with fixed length > by design. > > I can do: > > ```d > float[4] arr; > foreach(x;arr) > writefln("%s",x) > ``` > but not : > > ```d > arr.each!(a => a.writefln("%s",a)); > ``` > Is there a good reason for this ? > It took my a long time to figure out. How would it even be possible for a static array to be a range? It has a fixed length. For a type to work as a range, it needs to be possible to pop elements off of it, which you can't do with a static array. Input ranges must have front, popFront, and empty. Dynamic arrays have that from std.range.primitivies via UFCS (Universal Function Call Syntax), and that works, because it's possible to shrink a dynamic array, but it won't work with a static array, because its size will be fixed. Now, what you can do is slice a static array to get a dynamic array which refers to the static array. And since dynamic arrays work as ranges, you can use that with range-based functions. That being said, you do then have to be careful about the dynamic array (or any ranges which wrap it) escaping from the scope where the static array is, because if the static array goes out of scope and is destroyed, then any dynamic arrays referring to it will be referring to invalid memory, and you'll get undefined behavior. So, while slicing static arrays can be very useful, it needs to be done with caution. - Jonathan M Davis
Re: Synchronisation help
On Tuesday, January 2, 2024 3:41:55 AM MST Anonymouse via Digitalmars-d-learn wrote: > On Monday, 1 January 2024 at 19:49:28 UTC, Jonathan M Davis wrote: > > [...] > > Thank you. Yes, `Foo` is a class for the purposes of inheritance > -- I left that out of the example. > > So a completely valid solution is to write a struct wrapper > around an AA of the type I need, overload the required operators, > and then just drop-in replace the current AA? All array > operations would then transparently be between lock and unlock > statements. > ... > I tried this and it seems to work. Is it glaringly incorrect > somehow, or am I free to roll with this? For a member function to work on a shared object, that member function must be marked as shared. For most types, of course, it's completely inappropriate to mark a member function as shared, since those types do nothing with thread synchronization, but for types which are specifically designed to work across threads, it's appropriate to mark the member functions as shared and then to have them internally do whatever they need to do to protect access across threads. So, if you want to create a type which wraps an AA to make it thread-safe, it would be appropriate to make all of its member functions shared and then deal with the locking primitives internally. That being said, as Christian pointed out, whether locking at the operation level is the best way to deal with thread synchronization depends on what you're doing. For instance, doing something like if (auto value = key in aa) { // do stuff with *value } would be seriously problematic if you simply wrapped the AA, because you'd then have a pointer to a value in the AA which might not even be in the AA anymore when you try to do something with it (since another thread could have happily mutated the AA via another function call). In addition, it's problematic if in returns a thread-local pointer, since it's referencing shared data. So, it's a bug for it to be returning a thread-local pointer. It either needs to be returning a shared pointer (meaning that the locking primitives need to be at a higher level), or you need to copy the data out rather than return a pointer (as well as ensuring that the data itself is fully thread-local, which could be problematic with reference types). For something like this, locking the mutex for a whole set of operations makes more sense, and if you're doing that, you probably don't want a struct or class which simply wraps the AA. Rather, you'd want to have whatever code was operating on the AA to be handling the locking - which would often be inside of a struct or class that has the AA as a shared member variable. So, all of the code that uses the AA would be encapsulated, but you wouldn't have created a type that's simply wrapping the AA. What you'd typically do would probably be one of two approaches: 1. Create a type which handles all of the threading stuff internally (including spawning the other thread) and which provides an API to the main thread which is thread-local in the sense that the main thread doesn't have to know or care about the threading that's being done internally. 2. Create a type which is passed from one thread to another and then designed to be used across threads in a thread-safe manner where any operation that you can do on that type is marked as shared and designed to be thread-safe, which would typically mean having the operations being high enough level that the caller doesn't have to worry about the synchronization primitives at all, though of course, depending on what you're doing, you might have to expose more. Either way, the idea would be to make it so that that shared object is handling as much of the threading synchronization stuff as possible, and when it doesn't, it provides the means for the code using it to use the thread synchronization mechanisms on the data in as simple and safe a manner as possible. You could of course have a much messier approach where nothing is really wrapped, but that makes it much harder to manage the code, whereas it will usually work much better if you can encapsulate the stuff that has to deal with shared. But you do have to pick an encapsulation level which allows you to actually protect the data, which isn't likely to be the case at the level of the AA itself but rather at a higher level which is using the AA to do stuff. Ultimately, what you do with sharing data across threads in D is essentially what you'd do in a language like C++. It's just that D's shared makes it clear to the type system what's being shared across threads and what's thread-local so that the type system can assume that most stuff is thread-local as well as prevent you from accessing shared data accidentally (whereas in C++, you only know what's thread-local and what isn't by convention, since normally, _everything_ is shared across threads but only a small portion of it is actusally used by multiple threads). So, we're able to
Re: Synchronisation help
On Tuesday, January 2, 2024 4:39:12 AM MST Anonymouse via Digitalmars-d-learn wrote: > On Tuesday, 2 January 2024 at 11:05:33 UTC, user1234 wrote: > > Do not use `shared` AA. Use `__gshared` + sync primitives. > > `shared` AA will lead to all sort of bugs: > > > > - https://issues.dlang.org/show_bug.cgi?id=20484#c1 > > - https://issues.dlang.org/show_bug.cgi?id=17088 > > - https://issues.dlang.org/show_bug.cgi?id=16597 > > - etc. > > Hmm, I see. > > Is `shared` safe to use with AAs *provided* I use sync > primitives, or should I favour `__gshared` over `shared`? I was > under the impression `__gshared` was only really meant for > interfacing with C. You should almost never use __gshared. It's really only intended to be used with C global variables. Some folks use __gshared, because they don't like the restrictions that shared places on your code, but the restrictions are there to protect you from accessing shared data when it's not properly protected. In general, what code should be doing is marking variables as shared so that you cannot accidentally access the data, and then in the sections of code where you've properly protected access to the data, you temporarily cast away shared to operate on it. This is obviously a tad annoying, which is why some folks then just use __gshared to shut up the compiler, but it's very much on purpose that things work this way, and if you mark a variable as __gshared, the type system treats it as thread-local, and it's never caught when you try to access the variable without first dealing with the proper synchronization primitives. Unless a type is specifically designed to work as shared (e.g. a class or struct with shared member functions which do all of the appropriate locking and casting internally), it's expected that you're going to have to either cast away shared or use atomics to operate on shared variables of that type. And AAs are designed to be thread-local, so they have no locking mechanisms built in, and you have to deal with the locking primitives yourself as well as casting away shared to then operate on the AA while it's protected. It's a bug when you can do pretty much anything with a shared AA other than pass it around without casting away shared first. - Jonathan M Davis
Re: Synchronisation help
On Monday, January 1, 2024 8:48:16 AM MST Anonymouse via Digitalmars-d-learn wrote: > I have a `shared string[int]` AA that I access from two different > threads. The function I spawn to start the second thread takes > the AA as an argument. > > ```d > class Foo > { > shared string[int] bucket; > Tid worker; > } > > void workerFn(shared string[int] bucket) > { > while (true) > { > // occasionally reads, occasionally modifies bucket > } > } > > void main() > { > auto foo = new Foo; > foo.bucket[0] = string.init; > foo.bucket.remove(0); > foo.worker = spawn(, foo.bucket); > > while (true) > { > // occasionally reads, occasionally modifies bucket > } > } > ``` > > (`run.dlang.io` shortening seems broken again, but I made a > [gist](https://gist.github.com/zorael/17b042c424cfea5ebb5f1f3120f983f4) of a > more complete example.) > > Reading the specs on `synchronized` statements, it seems I need > to provide an `Object` to base synchronisation on when two > *different* places in the code needs synchronising, whereas if > it's in the same place an expressionless `synchronize { }` will > do. > > The worker function can't see `Foo foo` inside `main`, so it > can't share synchronisation on that. > > What is the common solution here? Do I add a module-level `Object > thing` and move everything accessing the AA into > `synchronized(.thing)` statements? Or maybe add a `shared static` > something to `Foo` and synchronise with `synchronize(Foo.thing)`? In general, I would advise against using synchronized statements. They really don't add anything, particularly since in many cases, you need access to more complex thread-synchronization facilities anyway (e.g. condition variables). Really, synchronized statements are just a Java-ism that D got fairly early on that were arguably a mistake. So, I'd typically suggest that folks just use Mutex from core.sync.mutex directly (though you can certainly use them if you don't need to do anything more complex). https://dlang.org/phobos/core_sync_mutex.html If you're using synchronized statements, you're essentially just using syntax which does that underneath the hood without providing you the functionality to use stuff like Condition from core.sync.condition. https://dlang.org/phobos/core_sync_condition.html However, regardless of whether you use synchronized or use Mutex directly, what you need to do is to have an object that functions as a mutex to protect the shared data, locking it whenever you access it so that only one thread can access it at a time. The best place to put that mutex varies depending on what your code is doing. A shared static variable could make sense, but it's often the case that you would put the mutex inside the class or struct that contains the data that's shared across threads. Or if you don't have a type that's intended to encompass what you're doing with the shared data, then it often makes sense to create one to hold the shared data so that the code that's using it doesn't have to deal with the synchronization mechanisms but rather all of that mess is contained entirely within the class or struct that you're passing around. But even if you don't want to encapsulate it all within a struct or class, simply creating one to hold both the shared data and the mutex makes it so that they'll be together wherever you're passing them around, making it easy for the code using the AA to access the mutex. However, because you're not supposed to actually be mutating data while it's shared (and the compiler largely prevents you from doing so), what you generally need to do to operate on shared data is to lock the mutex that protects it, cast away shared so that you can operate on the data, do whatever it is that you need to do with the now thread-local data, make sure that no thread-local references to the data exist any longer, and then lock the mutex again. And to do that cleanly, it's often nice to create a struct or class with shared member functions which takes care of all of that for you so that that particular dance is encapsulated rather than having to deal with any code that has access to that shared data having to deal with the synchronization correctly. Given that you already have a class called Foo which contains the AA, I would say that the most obvious thing to do would be to just pass a shared Foo across threads rather than pass the AA from inside Foo. Then you can either put a mutex in Foo that then naturally gets passed along with the AA, or you just use the class itself as a mutex - e.g. synchronized(this) {} IIRC - since classes unfortunately have a mutex built into them to make synchronized member functions work (which is useful when you want to use synchronized functions but causes unnecessary bloat for most classes). And if it makes sense to lock the mutex during entire function calls, you can just make the member function synchronized rather than having
Re: Checking path name
On Thursday, December 14, 2023 12:33:36 PM MST cc via Digitalmars-d-learn wrote: > On Thursday, 14 December 2023 at 09:38:30 UTC, Joel wrote: > > On Thursday, 14 December 2023 at 08:47:49 UTC, Anonymouse wrote: > >> On Thursday, 14 December 2023 at 03:58:37 UTC, Joel wrote: > >> https://dlang.org/phobos/std_path.html#isValidPath > >> > >> https://dlang.org/phobos/std_path.html#.isValidFilename > > > > Oh, forgot about std.path > > > > But what's the difference between path and file name? > > File name can't contain path separators. While that's true to a point, most code, documentation, and programmers really aren't going to distinguish between the two. When anything talks about a file path, it's pretty clear that it's talking about relative and/or absolute file paths, and thus, file / path separators will often be involved. However, when talking about a file name, it could be just the file's name without any preceding path, or it could be its entire file path (absolute or relative). It really depends on who's talking and what the context is. And it's not uncommon for documentation on functions to use the term path and filename interchangeably. Ultimately though, with regards to Phobos, std.path is for functions that have to do with manipulating file paths without actually doing anything to files on disk. They're basically a bunch of file-specific string manipulation functions. That then mostly relates to stuff like separators, but it also involves stuff like file extensions. On the other hand, std.file is for actually manipulating files rather than the paths to files. So, it has stuff for checking whether a file on disk is a file or a directory, whether it exists, etc. - and of course, it has functions for reading in and writing to files. std.stdio also has some functions for reading from and writing to files, but the difference there is that it does it in pieces, whereas std.file reads and writes files as single units (e.g. std.stdio might read in a file 4096 bytes at a time, whereas std.file would read it all in at once as a single array). - Jonathan M Davis
Re: pegged: non@safe semantic actions
On Friday, December 8, 2023 10:13:21 AM MST Dmitry Ponyatov via Digitalmars-d- learn wrote: > What's wrong with using non@safe actions which creates and > modifies some external objects? > > ```D > class Layer { > int index; > string name; > this(int index, string name) { > > class SignalLayer : Layer { > class UserLayer : Layer { > > class PCB { > Layer[] layer; > > PT _deflayer(PT)(PT p) { > auto index = to!int(p.matches[0]); > switch (p.matches[2]) { > case "signal": > pcb.layer ~= new SignalLayer(index, p.matches[1]); break; > case "user": > pcb.layer ~= new UserLayer(index, p.matches[1]); break; > default: > break; > } > return p; > } > > mixin(grammar(` > parser: > kicad_pcb < :l :'kicad_pcb' verzion host general page > layers > layers< :l :'layers' (deflayer {_deflayer})+ :r > deflayer < :l unum layer ('signal'|'user') :r > > ``` > ``` > ../.dub/packages/pegged/0.4.9/pegged/pegged/peg.d(3049,19): > Error: `@safe` function `pegged.peg.action!(wrapAround, > _deflayer).action` cannot call `@system` function > `kicad._deflayer!(ParseTree)._deflayer` > src/kicad.d(27,5):which calls `kicad.SignalLayer.this` > src/kicad.d(68,4):`kicad._deflayer!(ParseTree)._deflayer` > is declared here > src/kicad.d-mixin-83(219,289): Error: template instance > `pegged.peg.action!(wrapAround, _deflayer)` error instantiating > src/kicad.d-mixin-83(932,7):instantiated from here: > `Genericparser!(ParseTree)` > ../.dub/packages/pegged/0.4.9/pegged/pegged/peg.d(544,20): Error: > none of the overloads of `layers` are callable using argument > types `(GetName)` > src/kicad.d-mixin-83(215,23):Candidates are: > `kicad.Genericparser!(ParseTree).Genericparser.parser.layers(ParseTree p)` > src/kicad.d-mixin-83(234,23): > `kicad.Genericparser!(ParseTree).Genericparser.parser.layers(string s)` > ../.dub/packages/pegged/0.4.9/pegged/pegged/peg.d(1598,24): > Error: template instance `pegged.peg.getName!(layers)` error > instantiating > src/kicad.d-mixin-83(183,436):instantiated from here: > `wrapAround!(named, layers, named)` > src/kicad.d-mixin-83(932,7):instantiated from here: > `Genericparser!(ParseTree)` > ``` Since I've never used pegged, I can't really comment on the specific semantics of what you're trying to do. However, any function which is marked as @safe cannot call any functions that are @system. So, if pegged is marking a function as @safe, and it is then trying to call your function, your function then needs to be @safe or @trusted, regardless of what it's actually doing, which means that you need to then make it so that your function either isn't doing anything that's @system so that it can be @safe, or you need to vet what it's doing to make sure that it's actually memory-safe in spite of the fact that the compiler can't verify it, in which case, you would need to mark it as @trusted. The default for functions is @system, and none of the code you've shown is marked with @safe. Templated functions and functions which return auto will have their attributes inferred, which includes @safe, so if _deflayer is not calling any @system functions or doing any operations which are @system, it will be inferred as @safe. However, it looks like the constructors that it's calling are not marked with @safe and are not templated. So, they will not infer their attributes and will be @system, which will in turn mean that _deflayer gets inferred as @system. And if pegged is calling _deflayer from code that's marked with @safe, then you're going to get a compilation error. So, based on what I can see here, it looks like you need to be marking your functions with @safe where you can, and if any of your code is doing stuff that isn't guaranteed to be memory-safe (and thus can't be @safe), then you'll need to make sure that what it's doing is actually memory-safe (in spite of the fact that the compiler can't guarantee it) and mark it with @trusted to indicate that you've verified it, and then @safe code can call it. - Jonathan M Davis
Re: anonymous structs within structs
On Monday, December 4, 2023 11:26:07 AM MST DLearner via Digitalmars-d-learn wrote: > Suppose we need a construct like: > ``` > void main() { > > struct A { >int I1; >int I2; >char X; > } > > struct B { >A Dummy; >int Var1; >int Var2; > } > } > ``` > But do not want to give an explicit name (like 'Dummy' above) to > the A struct held within the B struct. > > Just removing 'Dummy' does not work (Error: no identifier for > declarator `A`). > Nor does replacing 'Dummy' with {} > > Suggestions? Normally, if you're not going to actually use the member variables, then there's no point in them even being there. However, if you need them there for alignment purposes, then you can just make them private. - Jonathan M Davis
Re: D Phobos Library Documentation: What is the Internal API for?
On Monday, November 27, 2023 6:03:22 AM MST BoQsc via Digitalmars-d-learn wrote: > This is pretty basic question. > If you open [D Library > Reference](https://dlang.org/phobos/index.html) you are bound to > see the **Internal API** section in the table of content. > > What is the **Internal API** (internal.core, dmd, rt) for and > when, how and where to use it? > > ![](https://i.imgur.com/WyemZsG.png) They're modules that are supposed to be internal to the implementation. They should not be accessible to anyone outside of those projects. So, it's pretty weird that it's up on the website, but my guess is that someone put it there so that the folks working on the code base could see the rendered ddoc comments for that code. For instance, I could believe that the dmd section there is there just to make it easier for some of the folks working on the compiler to more easily get an overview of the public symbols within those modules. But if you're not contributing to those projects, there really isn't any reason to see those modules. - Jonathan M Davis
Re: interface opEquals
On Thursday, November 23, 2023 2:20:25 PM MST Antonio via Digitalmars-d-learn wrote: > * Why, when applied to interface, ```opEquals``` called directly > behavior is not the same that when calling ```==``` ? > > * Is it the expected behaviour? I'd have to take the time to study your code in detail to see whether what exactly you're seeing makes sense or not, but it's not expected that normal D code will call opEquals directly, and for classes, == does more than call lhs.opEquals(rhs). It does additional stuff to try to have the correct behavior for equality without you having to code it all up yourself in opEquals. == on classes results in the free function, opEquals, in object.d being called. That function does a variety of checks such as checking whether either reference is null (to avoid dereferencing null) and using is to compare the address of the class references first (to avoid calling the class' opEquals if both references are to the same object). It also makes sure that both rhs.opEquals(lhs) and lhs.opEquals(rhs) are true for == to be true to avoid subtle bugs that can come into play when comparing a base class against a derived class. https://github.com/dlang/dmd/blob/master/druntime/src/object.d#L269 - Jonathan M Davis
Re: comparing with c strings
On Thursday, November 23, 2023 11:29:09 AM MST denis via Digitalmars-d-learn wrote: > Let's say I have a D application, with some callbacks from C, > where some arguments to the callbacks are `const char* path`. > What is the recommended way to compare them to D strings? Without > making allocations, if that's possible std.string.fromStringz will slice the char* to give you a char[] (using strlen to find the end of the string). Then you can operate on the C string as a char[] - though since it's a slice of the char*, you'll want to dup or idup it if the char[] risks living longer than the char* that it's a slice of. But if all you're doing is comparing it against a D string, then presumably, you don't need to keep the char[] around, and you won't have to allocate a copy. https://dlang.org/phobos/std_string.html#.fromStringz - Jonathan M Davis
Re: How to write an interface but with different signatures
On Sunday, November 19, 2023 1:13:16 AM MST Chris Katko via Digitalmars-d- learn wrote: > I know that sounds stupid on the face of it. An interface > shouldn't change. But consider this: > > A frame timer and a clock timer(seconds) that re-use > functionality from a parent class/interface. > > ``` > class timerType > { > void start() = 0; > void stop() = 0; > void restart() = 0; > void set(?) = 0; // > } > > class frameTimer : timerType > { > void set(int numFrames){} > } > > class clockTimer : timerType > { > void set(float numSeconds){} > } > > ``` > > If we put both signatures in the top we're kind of polluting the > interface. If we don't put them in the interface, then you can > create a timer with no set() function. > > None of this is super important on a practical level. There's > going to probably be a total of two or three timer types. But as > a learning exercise, I'm curious if there is a right/proper/best > way to solve this conceptual problem. > > And to be clear, frames and seconds are NOT directly > interchangeable or convertible. If the game runs at 2 FPS for a > moment, a 5 second timer is still 5 seconds (e.g. a countdown), > but a frame timer of 2 (for an animation) is still going to be 2 > frames. Generally, if a function needs to accept different types depending on the actual type, it doesn't belong on an interface or on a base class, because it cannot be called generically. Rather, you'd normally put the function on the actual class and use it when you're still dealing with the actual object and not the interface. If you need to set the value when it's an interface, then you should probably rethink what you're doing. Obviously, the exact solution which is best is going to depend on your code and the situation, and in some cases, the best solution is to do something like cast the interface to its actual type so that you can call class-specific functions on it, but ideally, once you're dealing with an interface or base class, you just use it as that and don't need to do anything class-specific to it. So, if possible, it's generally better to figure out how to rework your code so that you don't need to set anything that's class-specific in the parts of the code which deal with the object through an interface or base class reference. - Jonathan M Davis
Re: How to do reflection on alias symbols
On Friday, November 17, 2023 2:11:30 AM MST Arafel via Digitalmars-d-learn wrote: > I mean, in order to know if something is an `enum`, I need to do: > > ```d > enum isEnum(alias a) = is(typeof(a)) && !is(typeof()); > ``` > > which feels like the wrong approach, and too much error-prone. I also > fear I'm forgetting to consider some corner case. > > There is `is(E == enum)`, but it only works on types, and fails for > anonymous enums, because `typeof` returns the base type. Well, anonymous enums are what are called manifest constants, so they literally aren't enums as far as their type goes, and the type system does not consider them to be enums. They're just a way to declare constants (they're essentialy the D equivalent of using #define for constants in C/C++). Arguably, they should use a keyword other than enum (and that's been debated in the past), but it's unlikely to change at this point. So, if we were to add something to std.traits for them, it would probably be something more like isManifestConstant than isEnum. In spite of the keyword being used, they really aren't intended to be considered enums. - Jonathan M Davis
Re: How to do reflection on alias symbols
On Thursday, November 16, 2023 6:04:43 PM MST Jonathan M Davis via Digitalmars-d-learn wrote: > I would suggest that you open up a bug report for it - > https://issues.dlang.org - and certainly, there's a good argument that what > you're seeing here is a bug. I fully expect that what you're trying do just > wasn't properly considered previously and thus was not dealt with properly > when the other bugs for visibility attributes on aliases were fixed however > many years ago that was now. I very much doubt that what you're seeing is > the intended behavior - or at least I fully expect that if Walter or one of > the other compiler devs sees the issue, they will agree that what you're > trying to do should work. Actually, it looks like there's already an old bug report on the issue: https://issues.dlang.org/show_bug.cgi?id=12363 So, it has been reported, but it looks it's one of those that's gone under the radar. - Jonathan M Davis
Re: How to do reflection on alias symbols
On Thursday, November 16, 2023 3:03:25 AM MST Arafel via Digitalmars-d-learn wrote: > Hi all, > > Please consider the following currently non-working code: > > ```d > struct bar { > public alias pubInt = int; > private alias privInt = int; > } > > static foreach(member ; __traits(allMembers, bar)) { > // Error: argument `int` has no visibility > pragma(msg, __traits(getVisibility, __traits(getMember, bar, member))); > } > ``` > > Is there any way to get the visibility, or more generically to reflect > on an alias member as itself and not as the symbol pointed to without > resorting to nasty __trait(compiles,...) tricks that fail more often > than not? Someone may be able to give you some advice on how to better deal with this problem, but in general, aliases don't really exist as far as the compiler is concerned. They get translated to the original type and handled that way rather than treated as a separate type that translates to another type. They exist enough that you can do stuff like give them different visibility attributes for the original symbol, but that pretty much just affects whether you can use the alias, and then it gets replaced with the real thing immediately. As it is, IIRC, it was previously the case that there were bugs where visibility attributes did not affect aliases properly, so it's not at all surprising if there isn't a good way to access the visibility attribute of the alias itself. I would suggest that you open up a bug report for it - https://issues.dlang.org - and certainly, there's a good argument that what you're seeing here is a bug. I fully expect that what you're trying do just wasn't properly considered previously and thus was not dealt with properly when the other bugs for visibility attributes on aliases were fixed however many years ago that was now. I very much doubt that what you're seeing is the intended behavior - or at least I fully expect that if Walter or one of the other compiler devs sees the issue, they will agree that what you're trying to do should work. - Jonathan M Davis
Re: why remove octal literal support?
On Sunday, November 5, 2023 9:59:22 PM MST d007 via Digitalmars-d-learn wrote: > On Friday, 3 November 2023 at 15:34:37 UTC, Steven Schveighoffer > > wrote: > > On Friday, 3 November 2023 at 15:07:41 UTC, d007 wrote: > >> dlang is know for compile speed, but in reality d project > >> compile slow because so much ctfe and tempalte. > >> > >> > >> Why bring more ctfe call by remmove octal literal ? > > > > octal literals are extremely error prone, because people > > sometimes use leading zeroes for alignment, not realizing that > > it means the number is completely different. > > > > Actual correct octal literal use is vanishingly small. Banning > > C-style octal literals just makes it so the compiler flags > > unintended errors like this. > > > > -Steve > > Thanks you all for explain. > > > In my opinion, use some thing like 0o700 will be a better > solution compare to template. I general, D's approach at this point is to have a solution be in the standard library rather than in the language if it doesn't need to be in the language. And in this case, not only does a template solve the problem quite easily, but it's solving a problem that only rarely needs to be solved these days. So, while some might prefer a language solution, this really isn't the sort of problem that D is likely to solve in the language at this point. - Jonathan M Davis
Re: DUB: Is it possible to set release as a default build for a dub package?
On Friday, November 3, 2023 1:21:42 PM MDT BoQsc via Digitalmars-d-learn wrote: > While using `dub`, you might notice that after running `dub` or > `dub run` command you will end up with notice: > > ``` > Starting Performing "debug" build using > C:\D\dmd2\windows\bin64\dmd.exe for x86_64. > ``` > > Example output: > > ``` > C:\Users\Windows10\Documents\Dlang winsock\datatypes>dub > Pre-gen Running commands for datatypes > Public Domain. No rights reserved. > Starting Performing "debug" build using > C:\D\dmd2\windows\bin64\dmd.exe for x86_64. > Building datatypes 0.0.0: building configuration [application] > Linking datatypes > Running builds/datatypes.exe > ``` > > **Question:** is it possible to set it to release build in a > `dub.json` or `dub.sdl` file? > > Yes, it is possible using command line `dub --build=release` > > ``` > C:\Users\Windows10\Documents\Dlang winsock\datatypes>dub > --build=release > Pre-gen Running commands for datatypes > Public Domain. No rights reserved. > Starting Performing "release" build using > C:\D\dmd2\windows\bin64\dmd.exe for x86_64. > Building datatypes 0.0.0: building configuration [application] > Linking datatypes > Running builds/datatypes.exe > ``` > > However I would want to try to enforce this behaviour from the > `dub.json` or `dub.sdl` file. I would suggest that you just use a script to run the command that you want. AFAIK, the only way to make "dub build" do anything different would be to change the definition of the default build config in your project's dub.json file, but that's going to be _very_ surprising to anyone else using your project and might cause issues if it's a project that other projects end up depending on. - Jonathan M Davis
Re: Convert String to Date and Add ±N Hours
On Saturday, November 4, 2023 12:11:53 PM MDT Vahid via Digitalmars-d-learn wrote: > Hi, > > I have a date string with the format of "2023-11-04 23:10:20". I > want to convert this string to Date object and also, add ±N hours > to it. For example: > > `"2023-11-04 23:10:20" + "+2:00" = "2023-11-05 01:10:20"` > `"2023-11-04 23:10:20" + "-2:30" = "2023-11-05 20:40:20"` > > How can I do this? If you're using D's standard library, you would need to replace the space with a T so that the time format was ISO extended. Then you can use DateTime.fromISOEXtString() in std.datetim.date to get a DateTime. You can then add a duration to the DateTime to change its value - e.g. using hours(2) (or dur!"hours"(2) for the generic version). Then if you want a string again, toISOExtString will convert the DateTime to the ISO extended format, and if you want a space instead of a T, then just replace the T with a space in the string. E.G. import core.time : hours; import std.array : replace; import std.datetime.date : DateTime; auto dt = DateTime.fromISOExtString(strBefore.replace(' ', 'T')); dt += hours(2); auto strAfter = dt.toISOExtString().replace('T', ' '); However, if you also need to convert a string like "+2:00" to a Duration, then you'll need to create a function like that yourself. If you already have an integer value though, then you can just create a Duration and add it to the DateTime. At present D's standard library just supports the ISO standard, ISO extended standard, and Boost's "simple" format for converting dates and times to and from strings. And it doesn't support any format for converting from strings to Durations. There are third party libraries on code.dlang.org which support custom formatting for dates and times (e.g. https://code.dlang.org/packages/ae), but I'm not familiar enough with any of them to tell you how to solve your problem with them. That being said, since you seem to haves strings that are almost in the ISO extendend format, it should be pretty easy to get them to work with D's standard library. - Jonathan M Davis
Re: Keyword "package" prevents from importing a package module "package.d"
On Friday, November 3, 2023 5:20:56 AM MDT Andrey Zherikov via Digitalmars-d- learn wrote: > On Friday, 3 November 2023 at 00:52:18 UTC, H. S. Teoh wrote: > > Supposedly you can do this: > > /* Original: */ > > > > // pkg/mymodule.d > > module mymodule; > > ... // code here > > > > // main.d > > import mymodule; > > void main() { ... } > > > > /* Split */ > > > > // pkg/mymodule/pub_submod.d > > module mymodule.pub_submod; > > ... // code here > > > > // pkg/mymodule/priv_submod.d > > module mymodule.priv_submod; > > ... // code here > > > > // pkg/mymodule/package.d > > module mymodule; > > public import priv_submod; > > > > // main.d > > import mymodule; > > void main() { ... } > > > > Barring the issues listed above, of course. > > I know how to do this with package.d but my question was about > "package.d is bad design decision" - How would I do this > refactoring without dedicated "main package file"? > Python, for example, has __init__.py as well You don't. package.d is the only solution that the language provides to split up a module in place without breaking code, and in general, it works just fine. The issues that Adam was complaining about relate to bad installs where you end up with both the old mymodule.d file and mymodule/package.d on someone's system. The language does not handle that well (and that should be fixed), but as long as the new files are installed properly (which would include removing all of the old ones first), there isn't a problem. The issue came up with Phobos because of folks who assumed that they could just unzip a dmd install on top of another one without removing the old one first, which will always be risky business if the set of modules changes (which could also include module removals without introducing a corresponding package.d, though that only happens after an appropriate deprecation period). However, because the list of modules usually only grows, some folks had been getting away with it before and weren't expecting issues when either modules were removed or when they ended up with both std/datetime.d and std/datetime/package.d on their system, because they didn't actually remove the old install first. But since they hadn't been removing their old install first, they ran into issues when modules were split up in-place. Whatever the pros and cons are for package.d overall, the entire reason that it exists is to allow you to replace a module with a package, and Walter went with that solution, because it required minimal changes to the language. All of the semantics with public imports are what you get normally. It's just that the compiler now will import foo/package.d when you say import foo; and foo/package.d exists instead of requiring that it be foo.d. If you don't want to use package.d as a solution for breaking up a module, then your only option is to do so by changing your modules in manner which will involve the new modules being named something completely different. E.G. mymodule.d becomes foo/a.d and foo/b.d, with public imports in mymodule.d like you would have done in mymodule/package.d. You then either deprecate everything in mymodule.d so that folks will eventually change their code to use foo/a.d and foo/b.d directly, or you leave the code in the weird situation of everything being in the foo package, but existing code continues to import mymodule potentially forever. - Jonathan M Davis
Re: Keyword "package" prevents from importing a package module "package.d"
On Thursday, November 2, 2023 7:04:37 AM MDT Adam D Ruppe via Digitalmars-d- learn wrote: > On Thursday, 2 November 2023 at 12:52:35 UTC, BoQsc wrote: > > Therefore the need to import `package.d` is needed and I can't > > see a solution, which means > > tbh package.d should never be used. It is a poorly designed, > buggy misfeature of the language with plenty of better working > alternatives (it is no different than making a `module > yourthing.all;` people can import execpt with more limitations > and bugs.) The entire reason that it was added to the language was to be able to split up existing modules without breaking code. And it does that well. It was never intended to be used for anything else, but of course, some people always find ways to misuse a feature. package.d is indeed completely unnecessary for creating a module that publicly imports other modules in order to be able to import a single module and get several modules. Either way, personally, I don't think that that's something that should typically be done (with package.d or with any module name), but for whatever reason, some folks seem to love the idea. - Jonathan M Davis
Re: bigEndian in std.bitmanip
On Tuesday, October 31, 2023 8:23:28 AM MDT Salih Dincer via Digitalmars-d- learn wrote: > On Tuesday, 31 October 2023 at 10:24:56 UTC, Jonathan M Davis > > wrote: > > On Tuesday, October 31, 2023 4:09:53 AM MDT Salih Dincer via > > > > Digitalmars-d- learn wrote: > >> Hello, > >> > >> Why isn't Endian.littleEndian the default setting for read() in > >> std.bitmanip? > > > > Why would you expect little endian to be the default? The > > typical thing to do when encoding integral values in a > > platform-agnostic manner is to use big endian, not little > > endian... > > Because when we create a structure with a Union, it does reverse > insertion with according to the static array(bytes) index; I > showed this above. I fail to see what the situation with the union has to do with anything. Sure, you can convert between an array of bytes and an int with a union if you want to, but what that does is going to be dependent on your local architecture. read and its related functions in std.bitmanip are architecture-independent. So, they will convert from little endian or big endian regardless of what your local architecture is. You would typically use it on ranges of bytes that come from the network or from serialized data. The most common scenario there is likely to be that they'll be in big endian, because that's what platforma-independent binary formats typically do, but you can explicitly tell read that the range is in little endian if your range of bytes happens to be in little endian. Both scenarios can occur, and it supports both. It just defaults to big endian, because that's the more common scenario when dealing with binary formats. > I also have a convenience template like this: > ```d > template readBytes(T, bool big = false, R) > {// pair endian version 2.0 >import bop = std.bitmanip; > >static if(big) > enum E = bop.Endian.bigEndian; >else > enum E = bop.Endian.littleEndian; > >auto readBytes(ref R dat) > => bop.read!(T, E)(dat); > } > ``` > Sorry to give you extra engage because I already solved the > problem with readBytes(). Thank you for your answer, but there is > 1 more problem, or even 2! The read() in the library, which is > 2nd function, conflicts with std.write. Yeah, there are many > solutions to this, but what it does is just read bytes. However, > you can insert 4 ushorts into one ulong. > > Don't you think the name of the function should be readBytes, not > read? Because it doesn't work with any type other than ubyte[]! D's module system makes it so that names do not need to be unique across modules, and this is not the only case in Phobos where multiple modules use the same function name. It's easy enough to import only the functions you're using or to rename them via the import if you happen to be importing from multiple modules containing functions with the same name. E.G. if you want to do std.bitmanip : readBytes = read; then you can. - Jonathan M Davis
Re: bigEndian in std.bitmanip
On Tuesday, October 31, 2023 4:09:53 AM MDT Salih Dincer via Digitalmars-d- learn wrote: > Hello, > > Why isn't Endian.littleEndian the default setting for read() in > std.bitmanip? Why would you expect little endian to be the default? The typical thing to do when encoding integral values in a platform-agnostic manner is to use big endian, not little endian. Either way, it supports both big endian and little endian, so if your use case requires little endian, you can do that. You just have to specifiy the endianness, and if you find that to be too verbose, you can create a wrapper to use in your own code. - Jonathan M Davis
Re: dlang.org/spec/function.html#pure-functions example
On Monday, October 16, 2023 12:05:04 PM MDT Paul via Digitalmars-d-learn wrote: > On Thursday, 12 October 2023 at 21:20:44 UTC, Jonathan M Davis > > wrote: > > look like? > > > > Types can have static members. > > > > Basically what it comes down to is that outside of immutable > > data, pure functions only have access to their arguments and to > > what they can access via their arguments (be it by getting > > pointers from those arguments or calling other pure functions > > on them). > > > > - Jonathan M Davis > > Can I say in the general sense that when the word static is used > it means that something is defined/declared at compile time? Hmmm. It seems like my message got eaten. So, I'll write it out again. In any case, no, in general, static really doesn't have much to with runtime vs compile time. It means different things in different contexts. Off the top of my head, the only contexts where static specifically has anything to do with compile time are with static if and static foreach, in which case, those constructs become compile-time constructs instead of runtime constructos. Other contexts have very different meanings for static. For instance, a static member function is a member function that doesn't have an implicit this reference/pointer and thus is pretty much just a function that's scoped to the class/struct rather than being a function that operates on instances of that class or struct. On the other hand, static member variables are variables which are associated with the class or struct and not with an instance of that class or struct. So, there is only one instance of that variable for all objects of that class or struct on a single thread, as opposed to non-static member variables which are specific to each object. static in functions has similar but different meanings. On a nested function, it makes it so that the function has no implicit parameter which is a reference to the context of the outer function, meaning that it's pretty much just a function within another function, whereas a non-static nested function actually has access to the outer function's scope and thus can access the variables in the outer scope. On the other hand, a static variable within a function is a variable where there is only one instance of that variable for every call to that function on a single thread, as opposed to normal function variables which get a new instance every time that the function is called. And there are other meanings for static in other contexts. There are similarities between them, but if there is a definition that can be given for what static means which covers all of those contexts (and there may be - C manages that in spite of the fact that static means very different things in different contexts there too), it's not an obvious definition. You mostly just have to learn what static means in each context that it's used rather than memorizing a general definition for it that can be applied in each context. - Jonathan M Davis
Re: dlang.org/spec/function.html#pure-functions example
On Monday, October 16, 2023 12:05:04 PM MDT Paul via Digitalmars-d-learn wrote: > On Thursday, 12 October 2023 at 21:20:44 UTC, Jonathan M Davis > > wrote: > > look like? > > > > Types can have static members. > > > > Basically what it comes down to is that outside of immutable > > data, pure functions only have access to their arguments and to > > what they can access via their arguments (be it by getting > > pointers from those arguments or calling other pure functions > > on them). > > > > - Jonathan M Davis > > Can I say in the general sense that when the word static is used > it means that something is defined/declared at compile time?
Re: dlang.org/spec/function.html#pure-functions example
On Thursday, October 12, 2023 1:33:32 PM MDT Paul via Digitalmars-d-learn wrote: > The spec doc has the following statement and corresponding > example: > ***"Pure functions cannot directly access global or static > mutable state."*** > > ```d > int x; > immutable int y; > > pure int foo(int i) > { > i++; // ok, modifying local state > //x = i; // error, modifying global state > //i = x; // error, reading mutable global state > i = y; // ok, reading immutable global state > throw new Exception("failed"); // ok > } > ``` > If **int x** is global mutable state, what does static mutable > state look like? Types can have static members. Basically what it comes down to is that outside of immutable data, pure functions only have access to their arguments and to what they can access via their arguments (be it by getting pointers from those arguments or calling other pure functions on them). - Jonathan M Davis
Re: The Power of Grammar Checkers: A Game-Changer for Writers!
On Thursday, October 12, 2023 2:19:20 AM MDT Imperatorn via Digitalmars-d- learn wrote: > On Thursday, 12 October 2023 at 06:08:43 UTC, charles reiley > > wrote: > > I hope you're all doing well in your writing endeavors! Today, > > I wanted to share my thoughts and experiences with grammar > > checkers, and I can't emphasize enough how much of a > > game-changer they've been for me > > at [url=https://myassignmenthelp.com/grammar-checker.html]www.myassignment > > help.com[/url] > How does this relate to D in any way? It's doesn't. It's spam. When you see posts like this, you should either just ignore them (and they'll be removed when one of the few admins see it), or report it to the admins so that they know about it (though I'm not sure what the proper contact address for that is). Replying to it just means that they have to remove your post too (which of course means that I'm adding to the problem as well, but at least now you know). - Jonathan M Davis
Re: How to use ".stringof" to get the value of a variable and not the name of the variable (identifier) itself?
On Monday, October 9, 2023 10:55:41 AM MDT rempas via Digitalmars-d-learn wrote: > On Monday, 9 October 2023 at 16:53:55 UTC, mw wrote: > > but you `import std.stdio;`? > > > > Or copy the std/conv.d over to your build, > > > > or copy / write a toString(int) function yourself, which is > > compile-time callable. > > I do on that example just to use "write". It wouldn't be > necessary, but I just included it. My normal project does not use > Phobos. The language does not have a way to convert variables to strings. The normal solution for that is to use Phobos. The closest to a built-in solution would be toString on classes and structs, but structs don't necessarily have a toString. Rather, in many cases, Phobos does introspection on the struct to figure out how to convert the struct's members to a string if no toString is present (which isn't necessarily pretty but generally works well for debug output). Anyone looking to be able to do that sort of thing without Phobos is either going to need to reimplement what Phobos does themselves or use a third party library that already does that. std.conv.to provides the ability to convert between types in general based on their constructors, opCast member functions, and toString (if the user-defined type has one). So, it's able to convert pretty much any type to a string using to!string. std.format.format is then D's equivalent to snprintf, and it's able to convert pretty much any type to a string. I'm not sure if it uses to!string internally though. Regardless, if you want to be able to convert an arbitrary variable to a string without using Phobos, you basically have to reimplement all of that yourself (or at least as much as you need to do whatever it is that you're doing). C functions like snprintf can be used to convert the primitive types (meaning that you wouldn't have to implement something like the mess that is converting floating points to string), but for user-defined types that don't have toString, you'd basically be forced to do the kind of type introspection that Phobos does to produce a string from each of the member variables. It's possible that there's library somewhere that doesn't rely on Phobos that provides some of the same functionality (e.g. for the guys looking to use -betterC as a long term solution rather than just as a porting tool), but the language itself doesn't have that kind of functionality. Now, if what you're looking to do is to specifically convert an integer to a string at compile time (which means that snprintf wouldn't be a solution, since you can't call C functions during CTFE) rather than converting variables in general to string, then it shouldn't be hard to write a simple function that converts from an integer to a string. So, if that's all that you're looking to do, it shouldn't be hard to avoid Phobos. However, you're still going to need to implement it yourself. - Jonathan M Davis
Re: array setting : Whats going in here?
On Sunday, October 8, 2023 8:08:46 AM MDT Imperatorn via Digitalmars-d-learn wrote: > On Saturday, 7 October 2023 at 00:00:48 UTC, claptrap wrote: > > char[] foo; > > foo.length = 4; > > foo[] = 'a'; // ok sets all elements > > foo[] = "a"; // range error at runtime? > > foo[] = "ab"; // range error at runtime? > > > > So I meant to init with a char literal but accidently used > > double quotes. Should that even compile? Shouldn't the compiler > > at least complain when trying to init with "ab"? > > Even though you now have gotten answers, I still agree with you > that there should be some kind of "warning" or suggestion like, > did you mean to assign incompatible types? > > It could just check the element type and see if it matches the > rhs type. Except that in those examples, they _do_ match. It's perfectly valid to copy elements of a string to a char[]. It's just copying immutable(char) to char. The compiler would complain if it couldn't implicitly convert the element type in the array being assigned from to the element type in the array being assigned to. The problem here is simply that the lengths of the arrays don't match. And in general, the compiler has no way of knowing whether the lengths match, because the lengths are dynamic. In this particular case, it could figure it out if it did sufficient flow analysis, but that's the sort of thing that typically isn't done in D, because it gets to be expensive and tends to result in inconsistent behavior, because small changes to the code could drastically change what the compiler is able to figure out. If the lengths were static, then the compiler actually would complain. e.g. foo[0 .. 3] = bar[1 .. 2]; would result in a compilation error such as q.d(5): Error: mismatched array lengths 3 and 1 for assignment `foo[0..3] = bar[1..2]` So, the compiler will complain both if it can't implicitly convert the element types to make the assignment work and if it can statically see that the lengths of the arrays don't much. As such, I'm not sure that there's actually anything that the compiler could do here to catch the problem in the OP's case (at least not without doing code flow analysis, which isn't going to happen). - Jonathan M Davis
Re: how to assign multiple variables at once by unpacking array?
On Saturday, October 7, 2023 1:31:45 AM MDT mw via Digitalmars-d-learn wrote: > https://stackoverflow.com/questions/47046850/is-there-any-way-to-assign-mult > iple-variable-at-once-with-dlang > > How to do this Python code in D: > > ``` > > >>> s = "1 2 3" > >>> A,B,C = map(int, s.split(" ")) > >>> A,B,C > > (1, 2, 3) > > ``` > > Is there a better way (since 2017)? This is the sort of feature that you're much more likely to see in a dynamically typed language than a statically typed one, and you will almost certainly never see it in D. The problem is that the compiler needs to be able to verify that the types match, and when the return type of a function is a dynamic array such as int[], it has no way of knowing how many elements the array has and therefore can't verify at compile time that the assignment will work. At best, it could add a runtime check to verify that the number of elements match, but that's not the sort of thing that you typically do with a statically typed language. This is in stark contrast to a dynamically typed language where such things are often done, because everything is already being checked at runtime anyway, and checking whether the number of elements match then isn't really any different from checking that the actual types of the variables match what you're trying to do with them. But statically typed languages expect to be able to do all of those kinds of checks at compile time, which means that they're typically not going to do something like convert an array of arbitrary length to a set of variables like that. Now, what D might gain the ability to do at some point is to return language-defined tuples, meaning that you'd be able to do something like (int, string, float) foo(string bar, int baz) { ... return (i * 2, "hello", 2.7 * x); } and (a, b, c) = foo("whatever", 42); This would work with a statically typed language, because the types are all known at compile time. However, while there are some benefits to being able to do this, the response by many programmers from statically typed languages is that it's cleaner to create a struct for this sort of thing, since then the values are contained together, and they have names associated with them (since they'll be member variables of the struct). So, while some programmers definitely want tuple types to be built into D, a number of others don't like the idea. As such, it's an open question whether we'll ever have such tuples in D. What we do currently have is Tuple and tuple in std.typecons. Tuple allows you to create a struct with a given set of fields without explicitly declaring it. e.g. alias Foo = Tuple!(string, "name", int, "x", int, "y"); foo = Foo("Bob", 12, 22); assert(foo.name == "Bob"); assert(foo.x == 12); assert(foo.y == 22); assert(foo[0] == "Bob"); assert(foo[1] == 12); assert(foo[2] == 22); and tuple allows you to create such a struct (without the field names) simply by calling a function. e.g. auto foo = tuple("Bob", 12, 22); assert(foo[0] == "Bob"); assert(foo[1] == 12); assert(foo[2] == 22); So, it becomes possible to create a new struct type to return from a function simply by calling tuple. e.g. auto doStuff(string str, float f) { ... return tuple(x, str[i .. $]); } And thanks to some magic in Tuple, you even get unpacking of a sort by using AliasSeq. E.G. AliasSeq!(a, b) = doStuff(bar, 2.7); So, for many programmers, Tuple and tuple from std.typecons are good enough, and whether we ever get tuples added to the language will largely depend on whether anyone can come up with a proposal for them that convinces Walter and Atila that they're worth adding. Either way, the unpacking of dynamic arrays likely stands no chance whatsoever of ever being added, because it would require runtime checks to determine whether the unpacking was valid. - Jonathan M Davis
Re: array setting : Whats going in here?
On Saturday, October 7, 2023 10:59:47 AM MDT claptrap via Digitalmars-d-learn wrote: > On Saturday, 7 October 2023 at 00:49:39 UTC, H. S. Teoh wrote: > > On Sat, Oct 07, 2023 at 12:00:48AM +, claptrap via > > Digitalmars-d-learn wrote: > > > > > > When you write `foo[]` you're taking a slice of the array, and > > in that case if the lengths of both sides of the assignment > > don't match, you'll get a runtime error. > > How did I not know that?? I'd always thought "foo[] = x" was just > special syntax for setting all the elements to the same value. > > Thanks. It is, but it's assigning them to the elements in the slice, and if you slice the entire array, then you're assigning to every element in the array. e.g. auto foo = new int[](6); foo[] = 5; assert(foo == [5, 5, 5, 5, 5, 5]); Alternatively, you can assign to a slice that refers to just some of the elements of the array being sliced. e.g. auto foo = new int[](6); foo[0 .. 3] = 5; assert(foo == [5, 5, 5, 0, 0, 0]); And if you're assigning another array to it rather than a value of the element type, then it assigns the individual elements. e.g. auto foo = new int[](6); auto bar = [1, 2, 3, 4]; foo[0 .. 4] = bar[]; assert(foo == [1, 2, 3, 4, 0, 0]); And when you assign an array/slice like that, the number of elements on each side must match. So, if you do any of foo[] = bar[]; or foo[] = bar; then foo and bar must have the same length (and must have compatible element types). The difference between those and foo = bar; is that assigning to foo[] results in the elements being copied, whereas assigning directly to foo results in foo being a slice of bar. auto foo = new int[](6); auto bar = new int[](6); foo[] = bar[]; assert(foo == bar); assert(foo !is bar); foo = bar[]; assert(foo is bar); So, it's probably best to think of foo[] = x; as being a way to assign to each individual element in that slice of foo rather than assigning to foo. And then which elements are assigned to depends on how much of foo you slice, and how those elements are assigned to depends on the type of x. - Jonathan M Davis
Re: Type constraint
On Tuesday, October 3, 2023 7:46:42 PM MDT Joel via Digitalmars-d-learn wrote: > I think the if without static is still static, since it's part of > the function name part, or so (outside of the curly bracket > scope). if on a template (or on a templated function) is a template constraint, in which case, that's a compile-time if like static if, because it's used to indicate whether that particular template can be instantiated with a particular set of arguments. But elsewhere, an if without static is a runtime construct, and you need static on it to make it a compile-time one. https://dlang.org/spec/template.html#template_constraints https://dlang.org/spec/version.html#staticif https://dlang.org/spec/statement.html#if-statement http://ddili.org/ders/d.en/templates.html http://ddili.org/ders/d.en/cond_comp.html http://ddili.org/ders/d.en/if.html - Jonathan M Davis
Re: Type constraint
On Tuesday, October 3, 2023 8:35:31 AM MDT Joel via Digitalmars-d-learn wrote: > Oh, I found, > ```d > static if (isIntegral!T) > ``` > seems to work. Yeah. static if will compile in the code in that branch based on whether the condition is true, whereas if without the static will branch at runtime, with both branches being compiled in. So, if you want to be changing what code is being compiled in, you need static if, not if. if(isIntegral!T) will compile just fine, but it'll just end up being either if(true) or if(false) and the code within that branch will be compiled in regardless (and potentially result in compiler errors if it doesn't work with the type that that the template is being instantiated with). So, you usually want to use static if with compile-time tests and not if. - Jonathan M Davis
Re: The difference between T[] opIndex() and T[] opSlice()
On Sunday, October 1, 2023 11:51:17 AM MDT Salih Dincer via Digitalmars-d- learn wrote: > On Sunday, 1 October 2023 at 17:41:08 UTC, Salih Dincer wrote: > > Also, is it correct to use [] when returning? > > > > Thanks... > > [Here](https://dlang.org/spec/operatoroverloading.html#slice), > the opIndex() is proposed and an example of parameterized the > opSlice() is given for multidimensional arrays. Like there's no > difference, huh? > > Puff :) I suspect that the only people who really understand the full mess with opIndex and opSlice at this point are the folks who have done a bunch with multi-dimensional containers, which fortunately, I haven't had to deal with any time recently, so I'm not well-versed on all of the nitty-gritty details. The situation used to be a bit clearer, but folks wanted better support for multi-dimensional containers, so some changes were made. The result is that if you're dealing with multiple arguments, the difference in which is called should come down to whether you're using the slice operator between indices - .. - or whether you're using commas (though since you can mix and match to an extent with multi-dimensional containers, it's still pretty confusing IMHO). Either way, when you have no arguments, the situation is certainly more confusing than it used to be. Previously, you would have always used opSlice with no parameters for something like returning the full slice of a container, but I think that it's now possible to do exactly the same thing with opIndex (confusing as that may be), because that made some sense when writing a variadic opIndex. For most code, you'd just write an opIndex with a single parameter for indexing an element, opSlice with two parameters for slicing the range or container, and then either opIndex or opSlice with no parameters to return a slice of the entire container (in which case, personally, I'd use opSlice, because semantically, that's what you're doing, but either should work IIRC). - Jonathan M Davis
Re: Straight Forward Arrays
On Sunday, October 1, 2023 11:13:43 AM MDT dhs via Digitalmars-d-learn wrote: > On Sunday, 1 October 2023 at 13:27:37 UTC, Adam D Ruppe wrote: > > On Sunday, 1 October 2023 at 09:01:53 UTC, dhs wrote: > >> When D creates a dynamic array, it returns a slice. Functions > >> that add or remove elements begin by asking the memory manager > >> for the dynamic array that the slice belongs to. Only then can > >> they go on and add elements. > > > > Why is this a problem? It is convenient and usually works fine. > > > > I use the built in arrays very very often for a lot of things. > > It may not be a problem in practice. My concern was performance, > because each time we add an element to the array, the garbage > collector has to map the slice to the allocation it belongs to. In general, this is a non-issue. Usually, the only time that you might need to worry about it is when you're building an array with a bunch of elements, in which case, std.array.Appender gives you a wrapper which avoids a lot of that overhead (since it keeps track of the capacity separately): https://dlang.org/phobos/std_array.html#appender However, most code ends up using arrays without appending, and appending to an array here and there doesn't really impact performance. In addition, because D's dynamic arrays are slices of memory rather than owning their memory, passing them around is extremely cheap in comparison to std::vector. You're basically just passing around DynamicArray(T) { size_t length; T* ptr; } so you don't end up with a bunch of unnecessary copies, whereas in C++, you have to be careful about passing by reference or const reference (or worrying about move constructors) in order to avoid copying when you don't actually want a copy. So, unless you're doing a _lot_ of appending to dynamic arrays in D, and you're doing it a lot outside of when a dynamic array is first created, the way that D's arrays work will easily beat out how std::vector works in terms of performance. Of course, the exact performance characteristics are going to depend on what you're doing in your program, and whether the approach of D's dynamic arrays or C++'s std::vector is better depends on what your code is doing, but for most code, D's approach works extremely well. It just tends to take some getting used to, because the way that D's arrays work work is kind of unique. - Jonathan M Davis
Re: Is it possible to create a kernel for an operating system in D?
On Monday, September 25, 2023 9:31:36 PM MDT I come from chill. via Digitalmars-d-learn wrote: > It seems very obvious, but I have not been able to find any > information on the subject to confirm this. So I'm wondering if > it's possible. > > ** Maybe I shouldn't have created the account, literally this > will be one of the few doubts I'll have about D :u, but it'll be > worth it I guess **. Yes. It's been done before. In fact, there was a talk at the most recent dconf from someone who has been working on one: https://dconf.org/2023/index.html#zachy The video isn't up yet though, since the videos are currently in the process of getting rendered and uploaded to youtube. https://forum.dlang.org/thread/rgevjorzaoeylhwii...@forum.dlang.org - Jonathan M Davis
Re: std.file: read, readText and UTF-8 decoding
On Friday, September 22, 2023 12:28:39 AM MDT Uranuz via Digitalmars-d-learn wrote: > OK. Thanks for response. I wish that there it was some API to > handle it "out of the box". Do I need to write some issue or > something in order to not forget about this? You can open an issue if you want, though I don't know how much that will help it be remembered given how many issues ther are to sort through. I'll probably get around to writing something eventually (particularly since this issue is more likely to come up when using dxml than with many other use cases), but I have a variety of items on my todo list. - Jonathan M Davis
Re: parallelism with delegate
On Thursday, September 21, 2023 10:33:44 PM MDT Vitaliy Fadeev via Digitalmars-d-learn wrote: > On Friday, 22 September 2023 at 04:24:19 UTC, Vitaliy Fadeev > > wrote: > > ... > > Skip this thread. I see solution. > > How to delete missed posts on this forum ? This forum is esentially just a web client for some D-specific newsgroups (and a number of folks access it via either the newsgroup interface or its associated mailing list rather than through the web interface). So, you can't edit or remove posts. Admins can remove spam from the newsgroup (and thus the forum), but that's pretty much it, and even then, that doesn't remove it from the mailing list, because you can't get an e-mail back once it's been sent. So, once you send something to the forum, it's out there forever. - Jonathan M Davis
Re: std.file: read, readText and UTF-8 decoding
On Thursday, September 21, 2023 9:29:17 AM MDT Uranuz via Digitalmars-d-learn wrote: > Hello! > I have some strange problem. I am trying to parse XML files and > extract some information from it. > I use library dxml for it by Jonathan M Davis. But I have a > probleme that I have multiple XML files made by different people > around the world. Some of these files was created with Byte Order > Mark, but some of them without BOM. dxml expects no BOM at the > start of the string. > At first I tried to read file with std.file.readText. Looks like > it doesn't decode file at any way and doesn't remove BOM, so dxml > failed to parse it then. This looks strange for me, because I > expect that "text" function must decode data to UTF-8. Then I > read that this behavior is documented at least: > """ > ...However, no width or endian conversions are performed. So, if > the width or endianness of the characters in the given file > differ from the width or endianness of the element type of S, > then validation will fail. > """ > So it's OK. But I understood that this function "readText" is not > usefull for me. > So I tried to use plain "read" that returns "void[]". Problemmme > is that I still don't understand which method I should use to > convert this to string[] with proper UTF-8 decoding and remove > BOM and etc. > Could you help me, please to make some clearance. > P.S. Function readText looks odd in std.file, because you cannot > specify any encoding to decode this file. And logic how it > decodes is unclear... readText works great as long as you know that you're dealing with files with a specific encoding and without a BOM (which is very often true when dealing with text files on *nix systems where they're using UTF-8), but it's not so great when you're reading files where you have no clue what their encoding is going to be (and it's worse on Windows where they unfortunately are much more likely to be UTF-16). Phobos does give you the tools to solve the problem, but it doesn't currently make it as easy as it arguably should be. std.encoding has the pieces that you're missing here. https://dlang.org/phobos/std_encoding.html#BOM https://dlang.org/phobos/std_encoding.html#getBOM You'll need to do something like import std.encoding : BOM, getBOM; import std.file : read; auto data = read(file); immutable bom = getBOM(cast(ubyte[])data).schema; to get the BOM. Then you can compare the BOM against BOM.utf8, BOM.utf16le, etc. so that you know what type to cast the data array to (string, wstring, etc.). Then you can remove the BOM with something like R stripBOM(R)(R range) if(isForwardRange!R && isSomeChar!(ElementType!R)) { import std.utf : decodeFront, UseReplacementDchar; if(range.empty) return range; auto orig = range.save; immutable c = range.decodeFront!(UseReplacementDchar.yes)(); return c == '\uFEFF' ? range : orig; } And then you either operate on the array with its current encoding type, convert it to the desired string type (e.g. to!string) or wrap it in a type that converts it as you parse it (e.g. std.utf.byChar). Alternatively, you can just read the very beginning of the file and grab the BOM that way and then call readText with the correct type after you've figured out the file's encoding. readText should currently handle the BOM correctly insofar as it checks whether you made the correct choice when you told it whether you wanted a string, wstring, etc., but since it reads in the entire file, it's not a great plan to try it with each encoding (catching each exception in turn) until you get the right one, and it doesn't strip the BOM off for you. So, Phobos probably should get some new functionality to handle this better, but it's at least possible to make it work with what's there. - Jonathan M Davis
Re: Weird floating point rounding - Bug or how to control it correctly
On Wednesday, September 13, 2023 9:23:48 PM MDT An Pham via Digitalmars-d- learn wrote: > import std.stdio; > > void main() > { > float f = 6394763.345f; > > import std.format : sformat; > > char[80] vBuffer = void; > writeln("6394763.345 = ", sformat(vBuffer[], "%.4f", f)); > > } > > Output > 6394763.345 = 6394763.5000 The nature of floating point numbers is such that there a bunch of values that they can't actually represent, and they get rounded pretty easily depending on the exact numbers involved. So, in general, you shouldn't expect floating point numbers to be exact. You will generally do better with increased precision though, and in this case, it looks like your number will stay the same if you use double or real instead of float. I would suggest that you watch this video from dconf 2016 which discusses floating point values: https://www.youtube.com/watch?v=YEUAUnamQiA - Jonathan M Davis
Re: Is sizeof() available in D language?
On Monday, September 4, 2023 2:34:08 PM MDT Olivier Pisano via Digitalmars-d- learn wrote: > On Monday, 4 September 2023 at 09:41:54 UTC, BoQsc wrote: > > I've seen everyone using **datatype**`.sizeof` property. > > > > https://dlang.org/spec/property.html#sizeof > > > > It's great, but I wonder if it differ in any way from the > > standard C function `sizeof()`. > > Technically speaking, in C, sizeof is not a function, it is an > operator. This is why it is not available in D (replaced by the > .sizeof property). > > > https://www.geeksforgeeks.org/sizeof-operator-c/ > > https://en.cppreference.com/w/cpp/language/sizeof > > > > I'm seeking for some speed/performance, so that's why the > > question. > > Overall I'm alright with continuing using it. > > There is absolutely no difference in terms of runtime > performance. In both cases, the compiler replaces it by the size > of the type at compile-time. Yeah. You can pretty much just think of C's sizeof and D's sizeof as being the same thing with different syntaxes. In both cases, it's a compile-time value that gives the size of a type in bytes. In neither case does how it is calculated have any impact on the performance of the program. - Jonathan M Davis
Re: I don't understand betterC
On Saturday, September 2, 2023 4:38:41 AM BST confused via Digitalmars-d-learn wrote: > On Friday, 1 September 2023 at 13:45:05 UTC, evilrat wrote: > > It is shadowing default implicit "import object;", here a > > demonstration > > > > ```d > > // this example shows default implicit import of "object" module > > // compile this example: > > // ldc2 -c test.d > > // output: > > // tuple("object", "core", "main", "thisModule") > > > > // just a random import > > import core.stdc.stdio; > > > > void main() { } > > > > alias thisModule = __traits(parent, main); > > pragma(msg, __traits(allMembers, thisModule)); // has > > implicitly imported 'object' module > > ``` > > Is there no way for the two to coexist? If you put it into a package, then you could have your own object module that then isn't at the top level - e.g. mypkg/object.d with module mypkg.object; but you can't have more than one module in your program with the same full module name. So, in the case of the top-level module, object, you can only declare your own if you replace the default one, which you might do in some special situations, but it's not something that you would normally do, and you can never have both the normal object module and your own in the same program. - Jonathan M Davis
Re: Function to get the current hostname for both Windows and Posix
On Sunday, August 27, 2023 10:02:35 AM MDT vino via Digitalmars-d-learn wrote: > Hi All, > > May i know whether these is function to find the current > hostname both in windows and Posix. > > From, > Vino It looks like std.socket's Socket.hostName will do the trick. https://dlang.org/phobos/std_socket.html#.Socket.hostName - Jonathan M Davis
Re: Cool pattern or tragic?
On Friday, August 25, 2023 3:00:08 PM MDT Guillaume Piolat via Digitalmars-d- learn wrote: > The idea is to deliberately mark @system functions that need > special scrutiny to use, regardless of their memory-safety. > Function that would typically be named `assumeXXX`. > > > > ```d > class MyEncodedThing > { > Encoding encoding; > > /// Unsafe cast of encoding. > void assumeEncoding (Encoding encoding) /* here */ @system /* > here */ > { > this.encoding = encoding; > } > } > > char* assumeZeroTerminated(char[] str) @system > { > return str.ptr; > } > > ``` > > That way, @safe code will still need to manually @trust them. Well, if no attribute inference is involved, then @system isn't required. However, explicitly marking it @system makes it so that you won't accidentally make it @safe via later introducing attribute inference or by adding something like @safe: or @safe {} to the code. It also makes it clear that the @system is intentional rather than it being the case that no one decided to put @safe or @trusted on it. So, it arguable is good practice to mark functions @system if they're intended to be @system rather than leaving it up to the defaults. Either way, if the code using those functions are going to be able to use @trusted correctly, the documentation should probably be very clear about what the @system function is doing - at least if you're not in an environment where everyone is expected to look at the code itself rather than at documentation. - Jonathan M Davis
Re: Mach status support
On Monday, August 21, 2023 2:54:02 PM MDT Sergey via Digitalmars-d-learn wrote: > When I worked with one C code translation, I found that command > clock_gettime, that available in POSIX systems is not implemented > in MacOS. > This SO thread > > https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac > -os-x > > suggested some workaround implementations, which using some mach > headers like mach.h and mach_time.h > > But when I’ve checked dmd sources > > https://github.com/dlang/dmd/tree/master/druntime/src/core/sys/darwin/mach > > I didn’t find it. So currently I’ve used MonoTime as temporary > solution (however I’m not sure if it is a proper alternative). MonoTime is the system-agnostic D solution for getting the time from the system's monotonic clock, whereas Clock.currTime / Clock.currStdTime in std.datetime.systime is the system-agnostic D solution for getting the current wall clock time. Unless you're doing something really specific, you almost certainly should just be using those and not trying to call system-specific functions to get the time. In the case of Darwin systems, core.time declares the appropriate C bindings for the mach stuff so that MonoTime can use them, but they really should be moved to the appropriate place in druntime for Darwin-specific bindings. > How full is dmd specific implementation of Darwin and Mach > headers? > Any plans to improve it (in case it is not full right now)? In general, system-specific bindings get added to druntime, because someone who needed them took the time to add them to the right place in druntime rather than there being an organized effort to add bindings to druntime. - Jonathan M Davis
Re: Setting up a final switch from a list
On Wednesday, August 2, 2023 12:19:55 PM MDT Cecil Ward via Digitalmars-d- learn wrote: > Am I right in thinking that final switch can be used to check > that all the elements in an enum are handled in cases? Thinking > that this is a worthwhile safety check, I’d like to convert an > existing list into an enum for use in a final switch. I have an > existing list which I use elsewhere, > enum terminators = [ '%', ':', '?' ]; > > I pass that to some routine. I am wondering how to safely create > either an enum with values or have the enum with values declared > first and then create the list from it. That is, maybe start the > other way around > enum terminators_t = { percent = '%', colon = ':', qmark = '?' }; > and then automatically generate the list from that. The idea is > to avoid duplication in the array list and enum value list, so > that if I ever add one, I will not be able to update the other to > match. > > Any suggestions? I need some good compile-time stuff. It doesn’t > matter which direction we go in, from one to the other, but my > money is on the all-values enum generating the array list. import std.traits : EnumMembers; enum Terminator { percent = '%', colon = ':', qmark = '?' } enum terminators = [EnumMembers!Terminator]; - Jonathan M Davis
Re: Why is GC.collect `pure`
On Wednesday, August 2, 2023 12:02:35 PM MDT Nick Treleaven via Digitalmars-d- learn wrote: > On Wednesday, 2 August 2023 at 17:55:12 UTC, Nick Treleaven wrote: > > On Wednesday, 2 August 2023 at 17:52:00 UTC, Nick Treleaven > > > > wrote: > >> Now I'm wondering why those functions are marked `pure` - they > >> must affect the GC's bookkeeping state. > > I guess it was because the GC's internal state is not supposed to > be observable outside internal GC functions. I find it harder to > accept some of those than `GC.malloc` being pure, because > GC.disable and GC.enable will affect how long future allocations > will take. That latency can be significant and observed by the > program. Also conceptually they are changing GC state. Well, affecting how long something takes doesn't have anything to do with pure. Another process running on the box could have the same effect. Whether a function can be pure or not is strictly a matter of whether it's possible for it to access any non-immutable data that wasn't passed to it via its arguments. Of course, the whole question of pure gets weird with the GC, because we want to be able to treat GC allocations as pure when in fact they do mutate the GC's state when the GC was not passed in via a function argument. So, when dealing with the GC, purity becomes a question of maintaining the guarantees that the compiler expects with regards to pure and allocations rather than the more straightforward question of whether the function can access any non-immutable data from anything outside of itself via anything other than its arguments. In general, the GC's state is essentially treated as being separate from that of the program itself and thus irrelevant to stuff like pure. As far as the state of the program itself is concerned, the GC could allocate with new and then never bother to free anything, or it could be running a collection every single time new is called - or anything in between. As far as D is concerned, none of that matters to the state of the actual program. It's just a GC concern. That being said, of course, we do need to be careful when dealing directly with GC functions, because we don't want what the compiler does based on pure to end up having undesirable side effects with regards to the GC. As such, whenever deciding whether such functions can be pure or not, we need to carefully consider what the compiler will potentially do based on pure. So, remember that the most that the compiler will do with pure is optimize out multiple calls to the same strongly pure function within a single expression where each call has the exact same arguments. The compiler will also use that information to determine whether a value might be unique or not so that it can determine whether it's safe to convert mutable data to immutable, but that's primarily a type system concern rather than a runtime one. As such, the question of whether it's safe to make a GC function pure essentially comes down to the question of what would happen if you have an expression such as foo(12) * foo(12) which ends up being optimized down to one call to foo instead of two, because foo is strongly pure. And remember that because foo is strongly pure, its arguments are immutable (or were implicitly converted to immutable), and thus its execution in both cases would be identical. So, the exact same sequence of calls to GC functions would occur in each call to foo. So, we don't have to worry about something like the GC being enabled in one call to foo but disabled in the other. The functions that you referred to were GC.collect, GC. minimize, GC.enable, and GC.disable. So, the question becomes how (if at all) it affects the state of the program itself if the number of calls to those functions changes due to a call to foo being optimized out. And it shouldn't take much to see that it doesn't matter. Calling enable or disable multiple times in a row would just result in extraneous calls that do nothing, so optimizing that down to a single call wouldn't matter. Calling minimize multiple times would similarly not matter at all. It's highly unlikely that multiple calls to minimize within a short period of time would make any difference over a single call, and even if it did, it would just be affecting how much free memory the GC had. It would have no effect on the state of the program itself (and remember that as far as the rest of the program is concerned, the state of the GC doesn't even exist; the program's semantics would be the same even if new always grabbed more memory from the OS, and collections never did anything). Now, GC.collect is a bigger question, because that can affect when objects are actually destroyed, which obviously can affect the state of the program outside of the GC based on what the destructors involved do. So, that _can_ affect the program outside of memory allocations. However, when that happens is already effectively random, and it isn't even guaranteed that it will ever
Re: How can overloads be distinguished on attributes alone?
On Monday, July 31, 2023 4:55:44 AM MDT Quirin Schroll via Digitalmars-d-learn wrote: > Apparently, functions can be overloaded solely distinguished by > attributes: > ```d > void f(ref int x) pure { x = 1; } > void f(ref int x) { x = 2; static int s; ++s; } > ``` > > I thought that, maybe, a `pure` context calls the `pure` function > and an impure context calls the impure function, but no: Calling > `f` leads to an ambiguity error in both contexts. Even if that > worked, what about inferred contexts, i.e. templates? In simple > cases, they could forward the contexts in which they are called, > but you can instantiate a template without calling it. > > What am I missing here? As things stand, the context in which a function is called is irrelevant. All that matters is the arguments. And actually, allowing it would complicate any functions that infer attributes, potentially in a way that wouldn't work. For instance, if you have a templated function that's trying to infer purity, which one should it call? If it calls the pure one, it could be pure, but if it doesn't, it can't be. Either way, because the context isn't yet pure or not, the context can't be used to determine which should be called. Potentially, the compiler could just choose the pure function in that case, but the problem gets worse as you add more attributes. For instance, what happens when you have a function that's pure but not @safe and one that's @safe but not pure? void f() pure {...} void f() @safe {...} Should the compiler favor calling the pure one or the @safe one? And what if you then add something to the function that isn't @safe? If it was calling the @safe version before, should it switch to the pure one? And if the functions were @safe pure and @system and not pure instead void f() @safe pure {...} void f() @system {...} then changing the @safety or purity of some of the other code in the templated function could result in the loss of both attributes. And the more attributes are involved, the more complex the situation gets. In effect, we'd be making the attribute inference process have to go in two directions instead of just going from the bottom up, with the added complication that it would potentially need to choose between sets of attributes when choosing which function overload to call. It's not necessarily the case that we couldn't sort all of this out and come up with a clean set of rules that allowed functions that infer their attributes to call the correct function, but it does get pretty complicated, and it comes with the serious downside that there's no guarantee that the overloads even do something similar to one another. And when you consider that it's pretty easy for a change in one part of the code to change which attributes are inferred in another part of the code, you could easily end up having a change in one part of your program resulting in drastically different behavior in a seemingly unrelated part of your program. And even worse, that change could be because of a library update, making it that much less obvious which parts of your program could suddenly change behavior due to a change in attributes. And I'm probably forgetting other issues that this would add to the mix. So, while it may very well be possible to do something along the lines of what you're looking for, I strongly suspect that it's simply not worth it. - Jonathan M Davis
Re: AA vs __gshared
On Thursday, July 27, 2023 9:57:51 AM MDT IchorDev via Digitalmars-d-learn wrote: > I've been getting a lot of segfaults from using associative > arrays recently. The faults happen seemingly at random, and from > pretty mundane stuff like `if(auto x = y in z)` that run very > often: > ``` > Segmentation fault. > #0 0x55670f4a in rt.aaA.Impl.findSlotLookup(ulong, scope > const(void*), scope const(TypeInfo)) inout () > #1 0x55661662 in _aaInX () > ``` > > I suspect that this is because they've all been placed inside > `__ghared` structs. Are DRuntime's AAs simply incompatible with > `__gshared`? Do they need to be marked as `shared` to prevent > these shenanigans? Strictly speaking, __gshared is really only intended for stuff like C globals (which can't be shared due to name-mangling issues). Using it on anything else can at least potentially cause problems due to the fact that the compiler will assume that the variable is thread-local. So, I would strongly advise against using __gshared in a case like this. In practice, you can often get away with it, because the compiler doesn't do much in the way of optimizing stuff based on objects being thread-local right now, but it's definitely risking problems with the type system if you used __gshared when you're not trying to do something like bind to a C global. What should normally be happening is that you use shared, and then when you've protected the object so that you know that it can only be accessed on the current thread by the section of code that you're in (e.g. by locking a mutex), you temporarily cast away shared to operate on the object via a thread-local reference. Then, before exiting that section of code and removing the protections that are preventing other threads from accessing the object (e.g. by unlocking the mutex), you make sure that you've gotten rid of all of the thread-local references to the object so that only the shared reference exists. That way, you don't accidentally mutate the object while it's not protected from access by other threads. Now, as to what's happening in your code that's causing segfaults, the most likely culprit would be that you're accessing the AA without actually having done anything to prevent other threads from accessing it at the same time (or your protections were inadequate). And because the object is being treated as thread-local by the compiler, it would be easy to have accidentally let a reference to it leak somewhere that wasn't being protected by whatever mutex you're using, whereas if the AA were shared, the only sections of code where you would have to worry about thread-local references escaping would be in the sections of code where you've cast away shared after locking the relevant mutex. So, similar to what happens with @safe and @trusted, using shared allows you to limit the code that you have to examine to find the problem. - Jonathan M Davis
Re: array index out of bound may not throw exception?
On Friday, July 21, 2023 3:27:45 PM MDT mw via Digitalmars-d-learn wrote: > Hi, > > I have thought array index out of bound always throw exceptions. > > However, I just debugged a case, where out of bound array index > didn't throw exception, and just hang the thread, which is much > harder to debug (than exception which tells the exact error and > source line location). > > So my question: array index out of bound may not throw exception > in D? > > I tried DMD and LDC, both have this problem. > > Is there any flag I can pass to the compiler to let it always > throw exception? > > Thanks. Well, strictly speaking, it throws a RangeError, which is an Error, not an Exception (they both derive from Throwable). And it's not guaranteed that stuff like scope statements, and catches, and the like get run with Errors. Errors are basically supposed to kill your program rather than be recoverable (though they should normally still be printed out by the runtime like with an Exception). However, it at least used to be the case that all of the stack unwinding was in place for them even if it's not guaranteed by the language. So, I don't know what exactly happens right now if a RangeError gets thrown in a separate thread. It would not entirely surprise me if having the thread hang is normal at the moment, though ideally, what should be happening is that the program as a whole would be killed after printing out the RangeError. Either way, threads certainly complicate the matter. However, ignoring the issue of it happening on a separate thread, whether bounds checking occurs in a piece of code depends on a variety of factors - a key one being whether the code in question is @safe or not. If no compiler flags are used, then you should be getting bounds checking in all code. If -release is used, then you should be getting bounds checking in @safe code, but it will not happen in @system or @trusted code. So, if you're not marking code with @safe, and it's not in templated or auto functions where @safe is inferred, then you won't be getting bounds checking in that code if you use -release. If you use -boundscheck=on, then you should be getting bounds checking in all code. If you use -boundscheck=safeonly, then you should be getting bounds checking in @safe code (but only @safe code) like with -release. If you use -boundscheck=off, then you shouldn't be getting bounds checking anywhere. So, if I had to guess, you did something like use -release with code that isn't @safe, and so bounds checking was turned off, but it's also possible that you ran into some issue with how threads are handled and didn't get info on the RangeError when you should have. Unfortunately, it's been long enough since I dealt with a program that ran into a problem like this on a separate thread that I don't know exactly what the current state of that is. But the first step for you would to be sure of which compiler flags that you're using and whether the code in question is @safe. That will tell you whether a RangeError should be being thrown or not when an array index is out-of-bounds. - Jonathan M Davis
Re: Recommendation on plotting library
On Friday, July 21, 2023 11:40:25 AM MDT Greggor via Digitalmars-d-learn wrote: > >> So as far as I can tell, python pip originally only dealt with > >> python code, but eventually wheels were added for binary > >> support. > >> > >> Just as a wild guess, do you see dub ever evolving in that > >> > >> direction? All the reasons for not supporting pre-compiled > >> binaries in pip apply to dub, but yet support was added anyway, > >> and it's been wildly successful. > >> > >> I know it's hard to make predictions (especially about the > >> future), but I'd be interesting in your opinion on the matter. > > > > I'd be very surprised if dub added support for pre-compiled > > binaries - particularly since D isn't generally binary > > compatible across releases - but I really don't know what the > > folks working on dub want to do with it. > > > > - Jonathan M Davis > > Dependency management sucks for windows and I understand wanting > the ability to just do dub run and have it “just work tm”. > > Up to date versions of Windows 10 should have curl included and > dub can run commands before building, so you could try > downloading a prebuilt lib for windows via curl. > https://everything.curl.dev/get/windows Well, from what I recall (though it's been a while since I messed with anything like it), it's possible with dub to run more or less arbitrary stuff (e.g. use cmake from dub). It's a pain, but it can be done. And if that's the case, then you should be able to design a dub project that pulled in pretty much whatever you want with curl. And simply being able to build and run a D program as part of the build would be enough to be able to use curl in general without Windows having added it, since dmd comes with it because of std.net.curl. But of course, that's a rather different thing from dub providing a clean way to handle that sort of thing. dub can do a lot, but because it's really not designed around doing much beyond pulling in dependencies from code.dlang.org and then building all of the code, doing stuff beyond that gets to be problematic even if it can be done. So, if we wanted something more fully featured, then dub would need a bit of an overhaul. And maybe dub will get that at some point (I don't know), but as things stand, if anyone really wants to do complex stuff with their builds, dub can be pretty awkward to use. And that's why projects such as https://code.dlang.org/packages/reggae exist, though it's geared more towards providing a more fully-feature build system than providing a way to pull in pre-built binaries and the like. What additional features we get in the future will likely be highly dependent on how motivated the people are who want dub to be able to do more. - Jonathan M Davis
Re: Recommendation on plotting library
On Friday, July 21, 2023 1:03:47 AM MDT Chris Piker via Digitalmars-d-learn wrote: > On Friday, 21 July 2023 at 06:15:10 UTC, Jonathan M Davis wrote: > > On Thursday, July 20, 2023 10:57:22 PM MDT Chris Piker via > > Digitalmars-d-learn wrote: > > > > Regardless though, dub really isn't designed with packaging > > anything in mind. Rather, it's designed to build your code as > > well as pull in D libraries that it usees and build those too. > > Anyone looking to actually package stuff would create a package > > from what was built with dub (e.g. with deb, rpm, flatpacks, > > etc.). > > So as far as I can tell, python pip originally only dealt with > python code, but eventually wheels were added for binary support. > Just as a wild guess, do you see dub ever evolving in that > direction? All the reasons for not supporting pre-compiled > binaries in pip apply to dub, but yet support was added anyway, > and it's been wildly successful. > > I know it's hard to make predictions (especially about the > future), but I'd be interesting in your opinion on the matter. I'd be very surprised if dub added support for pre-compiled binaries - particularly since D isn't generally binary compatible across releases - but I really don't know what the folks working on dub want to do with it. - Jonathan M Davis
Re: Recommendation on plotting library
On Thursday, July 20, 2023 10:57:22 PM MDT Chris Piker via Digitalmars-d-learn wrote: > (Warning, possible ill-informed opinions ahead...) > > In a way there is a need to reinvent the wheel. With python I > can run `pip install matplotlib` and get whatever binaries I need > to get the job done. D runs on dub, which I only see handling > source code, and as far as I can tell, only D source code at > that. So unless it's D code, it can't be packaged and delivered > easily within the D ecosystem. > > If dub supports either pre-built binaries, or C code (such as > libcairo2), I'd be interested in seeing how that's done. With > the wizardry I've see around here, it's probably easy, I just > don't know about it. Well, dub is certainly designed around building projects that are pure D without anything fancy going on, but with effort, it's possible to do far more complicated stuff (though it's certainly far more of a pain than would be desirable). That being said, if the C libraries are already on your system, it's trivial to have a dub project just use them via bindings in the D code. And there are libraries on code.dlang.org which are basically just bindings for C libraries. Regardless though, dub really isn't designed with packaging anything in mind. Rather, it's designed to build your code as well as pull in D libraries that it usees and build those too. Anyone looking to actually package stuff would create a package from what was built with dub (e.g. with deb, rpm, flatpacks, etc.). > Going wy out on a limb for a minute, I think D shines as a > scripting language replacement. Most of my programs are single > file projects these days with dub set as the interpreter. Also > Rust seems to be crowding the system level space and so focusing > on it's "compiled scripts" capability avoids that competition. > > (If any of the statements above are faulty, I invite correction.) D does work quite well as a scripting language replacement for a lot stuff. In fact, the compiler and standard library have largely replaced their Makefiles with D scripts. It's definitely worse if you want to write a script that needs to pull in dependencies that aren't part of the standard library, but if Phobos has what you need, it works quite well - and of course, you can always run other shell commands from within a D program. - Jonathan M Davis
Re: Pre-import version statements
On Wednesday, July 19, 2023 10:56:26 PM MDT Chris Piker via Digitalmars-d- learn wrote: > Hi D > > In my C code I used to typically put the line: > ``` > #define _POSIX_C_SOURCE 200112L > ``` > in the source before importing any standard library headers. Is > there something equivalent for phobos? Say > `version(phobos2.100)` or similar? > > I don't particularly need this functionality, just checking to > see if there as a formal way to denote what library standard the > application code is expect to work against. > > Thanks, D has nothing equivalent to that. You compile your code with whichever version of dmd (or ldc, gdc, etc.) that you want, and it either compiles or it doesn't. The features of the compiler and the standard library go hand in hand, and you can't do something like use dmd version X while saying that you want Phobos version Y. They're both going to be the same version. The compiler may have additional switches to turn features on and off (e.g. to enable checks for functionality that's being added as part of a DIP but isn't the default behavior yet), but the compiler and standard library are versioned together. The closest thing to what you're talking about that I can think of in D would be __VERSION__, which simply gives you access to the version of the compiler that you're compiling with. So, it's compiler-specific, and all it gives you is an integer value. As such, outside of very rare cases, it would likely be a bad idea to actually use it for any form of conditional compilation. https://dlang.org/spec/lex.html#specialtokens Typically, the approach that people use it to just always use the latest compiler. And outside of deprecations being removed or bad luck with bug fixes, code usually just continues to work. And if they need to stick to an older version of the compiler for some reason, they just use that version of the compiler. But writing code that explicitly depends on a particular version of the compiler is not something that many projects are likely to be doing. - Jonathan M Davis
Re: Debugging by old fashioned trace log printfs / writefln
On Thursday, June 29, 2023 12:27:22 PM MDT Cecil Ward via Digitalmars-d-learn wrote: > I’m trying to debug my D program with old-fashioned printfs stuck > in various strategic places, actually using writefln(). My > problem is that the addition of printf fights with the existing > declarations for pure nothrow @nogc @safe and I have to adjust > them, then put them back correctly when the writefln() trace > statements are later removed. > > Is there something else I could be using, something that is > allowed to violate the checking rules for purity, nothrow, @nogc? > Would pragma( msg, "…" ) do the trick? Is that what I should be > using? pragma(msg, ...) and writeln are fundamentally different. pragmas are run when code is compiled. When you calling a function during CTFE, you are calling that function. You are not compiling it. It has already been compiled at that point. That function may be being called as part of compiling another function, but function that you're calling has already been compiled. So, something like string foo() { return "foo"; } void bar(int i) { pragma(msg, foo()); } will compile just fine, and it will print out "foo" at compile time, whereas void bar(int i) { pragma(msg, i); } will not compile. void bar(int i) { writeln(i); } will compile, but it won't print anything when it's compiled, and it cannot be called with CTFE. However, it will of course print out if called at runtime. If you need to print out a message during testing, and the function in question has attributes that writeln does not satisfy, then you can use debug statements and compile the code with the -debug flag. https://dlang.org/spec/version.html#debug e.g. void foo() pure @safe { debug { writeln("hello"); } } Of course, you have to be very careful when you do that, since you'll get undefined behavior if the debug statements have side effects which violate the guarantees that those attributes are supposed to make (e.g. mutating a global variable in a pure function or throwing an exception from a nothrow function), but simply printing out stuff shouldn't be a problem unless generating the strings to print has side effects. - Jonathan M Davis
Re: pragma msg field name?
On Monday, June 26, 2023 10:25:13 PM MDT Chris Katko via Digitalmars-d-learn wrote: > inside a static foreach I can do > > ``` > enum rep; > > class myObject{ > int field1, field2, field3; > > static foreach(field; getSymbolsByUDA!(typeof(this), rep)) > { > pragma(msg, field); // fails > pragma(msg, fullyQualifiedName!field); // works > } > } > ``` > > error for pragma(msg, field) > ``` > source/app.d(33,16): Error: value of `this` is not known at > compile time > source/app.d(33,4):while evaluating `pragma(msg, field)` > > [repeating for every variable in the class] > ``` > > How do I get just the field name? And why does it think this is a > run-time value? I need to wrap it in some sort of template? > > All I see in std.traits docs are: fullyQualifiedName mangledName > moduleName packageName Well, on my machine, once the import for std.traits is added, that code compiles but prints nothing, so I suspect that you paired it down too much (likely related to the fact that none of the fields in question actually have UDAs on them). However, I would point out that getSymbolsByUDA gives you symbols, not strings, whereas pragma(msg, ...) wants a string. fullyQualifiedName works, because it results in a string. You can see what field is by using typeof on it, but presumably, it's complaining about being a runtime value, because when you use it, it's trying to evaluate the symbol (e.g. get the value of field1). Using .stringof on field will give you a string, though I don't know if it'll give you what you're looking for. In general, FieldNameTuple would probably be what you would want for getting the names of the fields in a struct or class, though obviously, that wouldn't be just getting the ones with a specific UDA. - Jonathan M Davis
Re: Counting an initialised array, and segments
On Monday, June 26, 2023 1:09:24 PM MDT Cecil Ward via Digitalmars-d-learn wrote: > No, point taken, a sloppy example. I don’t in fact do that in the > real code. I use dchar everywhere appropriate instead of uint. In > fact I have aliases for dstring and dchar and successfully did an > alternative build with the aliases renamed to use 16-bits wchar / > w string instead of 32-bits and rebuilt and all was well, just to > test that it is code word size-independent. I would need to do > something different though if I ever decided to change to use > 16-bit code words in memory because I would still be wanting to > manipulate 32-bit values for char code points when they are being > handled in registers, for efficiency too as well as code > correctness, as 16-bit ‘partial words’ are bad news for > performance on x86-64. I perhaps ought to introduce a new alias > called codepoint, which is always 32-bits, to distinguish dchar > in registers from words in memory. It turns out that I can get > away with not caring about utf16, as I’m merely _scanning_ a > string. I couldn’t ever get away with changing the in-memory code > word type to be 8-bit chars, and then using utf8 though, as I do > occasionally deal with non-ASCII characters, and I would have to > either preconvert the Utf8 to do the decoding, or parse 8-bit > code words and handle the decoding myself on the fly which would > be madness. If I have to handle utf8 data I will just preconvert > it. Well, I can't really comment on the details of what you're doing, since I don't know them, but I would point out that a dchar is a code point by definition. That is its purpose. char is a UTF-8 code unit, wchar is a UTF-16 code unit, and dchar is both a UTF-32 code unit and a code point, since UTF-32 code units are code points by definition. It is possible for a dchar to be an invalid code point if you give it bad data, but code points are 32-bit, and dchar is intended to represent that. Actual characters, of course, can be multiple code points, annoyingly enough, so all of that Unicode stuff is of course an annoyingly complicated mess, but D and Phobos do have a pretty good set of primitives for handling code units and code points without programmers needing to come up with their own types for those. char is a UTF-8 code unit, wchar is a UTF-16 code unit, and dchar is both a UTF-32 code unit and a code point, since UTF-32 code units are code points by definition. The primary mistake in what D has is that strings are all ranges of dchar with the code units automatically being decoded to dchar by front, popFront, etc. (at the time, Andrei thought that that would ensure correctness, since he didn't understand that you could have characters that were multiple code points). We'd like to get rid of that, but it's difficult to do so without breaking code. std.utf.byCodeUnit helps work around that, and of course, you can do so by simply operating on the strings as arrays without using the range primitives, but the range primitives do decode to dchar, unfortunately. However, in spite of that quirk, the tools are there to operate on Unicode correctly in a way that don't exist out of the box with many languages. So, in general, you shouldn't need to be creating new types for Unicode primitives. The language already has that. - Jonathan M Davis
Re: Counting an initialised array, and segments
On Monday, June 26, 2023 5:08:06 AM MDT Cecil Ward via Digitalmars-d-learn wrote: > On Monday, 26 June 2023 at 08:26:31 UTC, Jonathan M Davis wrote: > > On Sunday, June 25, 2023 4:08:19 PM MDT Cecil Ward via > > > > Digitalmars-d-learn wrote: > >> I recently had some problems > >> > >> dchar[] arr = [ ‘ ‘, TAB, CR, LF … ]; > >> > >> and I got errors from the compiler which led to me having to > >> count the elements in the initialiser and declare the array > >> with > >> an explicit size. I don’t want the array to be mutable so I > >> later > >> added immutable to it, but that didn’t help matters. At one > >> point, because the array was quite long, I got the arr[ > >> n_elements ] number wrong, it was too small and the remainder > >> of > >> the array was full of 0xffs (or something), which was good, > >> helped me spot the bug. > >> > >> Is there any way to get the compiler to count the number of > >> elements in the initialiser and set the array to that size ? > >> And it’s immutable. > > > > Without seeing the errors, I can't really say what the problem > > was, but most character literals are going to be char, not > > dchar, so you may have had issues related to the type that the > > compiler was inferring for the array literal. I don't recall at > > the moment how exactly the compiler decides the type of an > > array literal when it's given values of differing types for the > > elements. > > > > Either way, if you want a static array, and you don't want to > > have to count the number of elements, then > > https://dlang.org/phobos/std_array.html#staticArray should take > > care of that problem. > > > > - Jonathan M Davis > > Where I used symbolic names, such as TAB, that was defined as an > int (or uint) > enum TAB = 9; > or > enum uint TAB = 9; > I forget which. So I had at least one item that was typed > something wider than a char. > > I tried the usual sizeof( arr )/ sizeof dchar, compiler wouldn’t > have that for some reason, and yes I know it should be D syntax, > god how I long for C sizeof()! sizeof is a property in D. So, you can do char.sizeof or varName.sizeof. But regardless, there really is no reason to use sizeof with D arrays under normal circumstances. And in the case of dynamic arrays, sizeof will give you the size of the dynamic array itself, not the slice of memory that it refers to. You're essentially using sizeof on struct DynamicArray(T) { size_t length; T* ptr; } which is not going to tell you anything about the memory it points to. The length property of an array already tells you the length of the array (be it static or dynamic), so using sizeof like you're talking about really does not apply to D. And I wouldn't advise using uint for a character in D. That's what char, wchar, and dchar are for. Depending on the circumstances, you get implicit conversions between character and integer types, but they are distinct types, and mixing and matching them willy-nilly could result in compilation errors depending on what your code is doing. - Jonathan M Davis
Re: Counting an initialised array, and segments
On Sunday, June 25, 2023 4:08:19 PM MDT Cecil Ward via Digitalmars-d-learn wrote: > I recently had some problems > > dchar[] arr = [ ‘ ‘, TAB, CR, LF … ]; > > and I got errors from the compiler which led to me having to > count the elements in the initialiser and declare the array with > an explicit size. I don’t want the array to be mutable so I later > added immutable to it, but that didn’t help matters. At one > point, because the array was quite long, I got the arr[ > n_elements ] number wrong, it was too small and the remainder of > the array was full of 0xffs (or something), which was good, > helped me spot the bug. > > Is there any way to get the compiler to count the number of > elements in the initialiser and set the array to that size ? And > it’s immutable. Without seeing the errors, I can't really say what the problem was, but most character literals are going to be char, not dchar, so you may have had issues related to the type that the compiler was inferring for the array literal. I don't recall at the moment how exactly the compiler decides the type of an array literal when it's given values of differing types for the elements. Either way, if you want a static array, and you don't want to have to count the number of elements, then https://dlang.org/phobos/std_array.html#staticArray should take care of that problem. - Jonathan M Davis
Re: A couple of questions about arrays and slices
On Saturday, June 24, 2023 8:43:00 AM MDT Cecil Ward via Digitalmars-d-learn wrote: > I started out looking into a number of runtime library routines, > but in the end it seemed quicker to roll my own code for a crude > recursive descent parser/lexer that parses part of D’s grammar > for expressions, and (again partial grammar) parser for string > literal expressions and so on. I find certain special elements > and execute actions which involve doing the AA lookup and > replacing variable names with ordinal numbers in decimal in the > output stream. Admission: The parsing is the thing that has to be > fast, even though again the size of the D language text is not > likely to be huge at all. But 40 years ago, I came from a world > with 2k RAM and 0.9 MHz clock rates so I have developed a habit > of always thinking about speed before I do anything, needful or > not, to be honest. I once wrote a program that took 35 mins to > evaluate 2+2 and print out the answer, so I’m now ashamed of > writing slow code. Those were bad days, to be honest. 4 GHz+ and > ILP is nicer. Well, dmd is open source (and Boost-licensed, so it doesn't really have any restrictions), so depending on what you're doing, it might make sense to just take code from that (and it's very fast). IIRC, it pulls some fun tricks like replacing identical strings with pointers to the same string so that it can just compare pointers. - Jonathan M Davis
Re: A couple of questions about arrays and slices
On Saturday, June 24, 2023 1:43:53 AM MDT Cecil Ward via Digitalmars-d-learn wrote: > On Saturday, 24 June 2023 at 07:36:26 UTC, Cecil Ward wrote: > > Jonathan, is it possible that I wanted one thing and got > > another? My description in the earlier post was of the _aim_ of > > the program. What I ended up with might be something else? I > > wanted an array of uints whose values are the results/outputs > > of the mapping function. Since it is keyed by strings I assumed > > that the runtime generates some kind of hash for fast lookup > > when I ask it to retrieve an entry by the string (key) > > associated with it. I assumed that in some sense the hashing > > was sort of separate with some degree of independence from the > > underlying array, if that makes sense. The lookup is just > > assumed to be fast but how it is done we don’t really care. I > > just wanted to expand the array as I did successfully elsewhere > > with reserve, as I built this structure by successive additions > > of data. I have a number of strings and the map is meant to > > output the ordinal number in which I first saw them, > > zero-based. Then I want to come back and randomly look up one > > ordinal given a string preferably with a very fast lookup. The > > number of entries can not practically be more than 30, and even > > that would be highly unusual, maybe ten is the practical limit > > in my particular case, so it’s hardly MySQL. > > I just realised something, your point about altering the table > and having to rehash, is well taken. I hadn’t considered that. > The reason for my foolishness in failing to realise that I’m > asking the impractical is my pattern of usage. I add all the > entries into the mapping table and have no interest in any > lookups until it is fully built. Then a second function starts to > do lookups while the data remains unchanging and that usage > pattern can be guaranteed. I could even idup it if that would > help, as copying < 32 uints wouldn’t take forever. A typical > value would be a mere 5 or less. I only picked 32 to be > completely safely ott. Well, if the key were a struct or a class, the hashing function would be opHash. For built-in types, the runtime has hashing functions that it uses. Either way, with AAs, you really don't worry about managing the memory, because it's completely outside of your control. You just put the elements in there using their associated keys, and if you want to try to speed it up after you've populated it, you use rehash so that the runtime can try to move the elements around within the container so that lookup speeds will be closer to optimal. As such, for the most part, when dealing with AAs and worrying about efficiency, the question really becomes whether AAs are the correct solution rather than much of anything having to do with how you manage their memory. With so few elements, it's also possible that using std.algorithm.searching.find would be faster - e.g. having a dynamic array of strings where the matching int is at the same index in a dynamic array of ints - or you could use std.typecons.Tuple!(string, int)[] with something like arr.find!(a => a[0] == key)() to find the tuple with the int you want. Simply comparing a small number of strings like that might be faster than what goes on with hashing the string and then finding the corresponding element within the AA - or it might not be. You'd have to test that to know. The AA would definitely be faster with a large number of elements, but with a small number of elements, the algorithmic complexity doesn't really matter, and the extra overhad with the AA lookups could actually mean that the search through the dynamic array is faster even though it's O(n). But you can only know which is faster by testing it out with the actual data that you're dealing with. Regardless, you need to remember that associative arrays are not arrays in the C sense. Rather, they're hash tables, so they function very differently from dynamic arrays, and the rehash function is the closest that you're going to get to affecting how the elements are laid out internally or how much memory the AA is using. - Jonathan M Davis
Re: A couple of questions about arrays and slices
On Friday, June 23, 2023 7:02:12 PM MDT Cecil Ward via Digitalmars-d-learn wrote: > I just had a fight with LDC over the following code when I tried > out reserve. I have an associative array that maps strings to > ‘ordinals’ ie uints that are unique, and the compiler hates the > call to reserve. > > == > > > > struct decls_t > { > uintn_entries = 0; > uint[ dstring ] ordinals; // Associative array maps variable > names to ordinals > } > > static decls_t Decls; > > enum NPreAllocEntries = 32; > Decls.ordinals.reserve( NPreAllocEntries ); > > source>(82): Error: none of the overloads of template > `object.reserve` are callable using argument types > `!()(uint[dstring], ulong)` > /opt/compiler-explorer/ldc1.32.1/ldc2-1.32.1-linux-x86_64/bin/../import/obje > ct.d(3983):Candidate is: `reserve(T)(ref T[] arr, size_t > newcapacity)` Compiler returned: 1 Associative arrays and dynamic arrays are completely different things. Associative arrays are hash tables, and reserve really doesn't make sense for them. reserve is for telling the GC to make sure that a dynamic array has at least a specific amount of room to grow into before the GC needs to do a reallocation so that the dynamic array refers to a different memory block with enough memory to hold the data, whereas if and when associative arrays have to reallocate any of their internals is largely implementation-defined. Any time that you add or remove elements from an AA, it might reallocate some of its internals depending on its current state and what the key of the element is - and that could be different between different compiler releases (though it's unlikely to change very often, since I don't think that the AA implementation gets messed with much). You can use the rehash function on AAs to tell the GC to try to reorder how it's structured all of its buckets so that lookups are more efficient with the data that's currently in there, and you can call clear to remove all its elements, but in general, you don't do much to manage an AA's memory. It's a much more complicated data structure than an array. https://dlang.org/spec/hash-map.html - Jonathan M Davis
Re: A couple of questions about arrays and slices
On Wednesday, June 21, 2023 7:05:28 PM MDT Paul Backus via Digitalmars-d-learn wrote: > On Thursday, 22 June 2023 at 00:10:19 UTC, Cecil Ward wrote: > > Is .reserve()’s argument scaled by the entry size after it is > > supplied, that is it is quoted in elements or is it in bytes? > > I’m not sure whether the runtime has a knowledge of the element > > type so maybe it doesn’t know anything about scale factors, not > > sure. > > length, reserve, and capacity all use the same unit, which is > elements. > > reserve passes a TypeInfo instance to the runtime so that it > knows the size of the elements. You can see the implementation > here: > > https://github.com/dlang/dmd/blob/v2.104.0/druntime/src/object.d#L3910 To add to that, it _has_ to know the element type, because aside from anything related to a type's size, it bit-blits the type's init value onto the new elements when it increases the length of the dynamic array. You'd probably be dealing with bytes if you were explicitly asking for memory and the like (e.g. with malloc), but a dynamic array is properly typed, and everything you do with it in @safe code is going to deal with it as properly typed. For it to be otherwise would require @system casts. - Jonathan M Davis
Re: A couple of questions about arrays and slices
On Tuesday, June 20, 2023 8:09:26 PM MDT Cecil Ward via Digitalmars-d-learn wrote: > First is an easy one: > > 1.) I have a large array and a sub-slice which I want to set up > to be pointing into a sub-range of it. What do I write if I know > the start and end indices ? Concerned about an off-by-one error, > I have start_index and past_end_index (exclusive). When slicing, the end index is exclusive. e.g. auto a = "0123456789abcdef"; assert(a[3 .. 8] == "34567"); As a consequence of that, the end index minus the start index is the length of the resulting slice. It also means that $ always refers to one past the end of the array. > 2.) I have a dynamic array and I wish to preinitialise its alloc > cell to be a certain large size so that I don’t need to > reallocate often initially. I tell myself that I can set the > .length property. Is that true? Well, dynamic arrays have a length and a capacity. The length is the actual length of the slice that you're operating on. e.g. auto a = new int[](100); assert(a.length == 100); or int[] a; a.length = 100; assert(a.length == 100); However, the underlying memory that the dynamic array is a slice of will generally be larger than the length of the array - in part because of how the allocator works and in part so that appending to the array will be more efficient. So, you can see how much space the dynamic array has to grow before it has to be reallocated when appending to it by using the capacity property. E.G. When I run this on my system auto a = new int[](100); writeln(a.length); writeln(a.capacity); I get 100 127 This means that I could append 27 more elements to the array (or otherwise increase its length by up to 27 elements) without needing a reallocation. But if the array tries to grow beyond that, then the GC will allocate a new block of memory, copy the elements over to that, and adjust the slice to point to the new block of memory (of course leaving any other slices pointing to the old block of memory). One thing to note about that is that a dynamic array can only have a capacity that's larger than its length if it's the furthest slice into that block of memory, and no other slices have gone further into it (since it's only safe for the runtime to allow you to append to a dynamic array in-place when no other dynamic array can possibly refer to the memory after the array that you're trying to append to). If the dynamic array is not at the end, then it ends up with a capacity of 0. E.G. auto a = new int[](100); auto b = a[0 .. $ - 1]; assert(b.length == 99); assert(b.capacity == 0); So, capacity is a bit more complex than the max length that the array could grow to without needing a reallocation, but you can use it see if appending to a dynamic array will result in a reallocation. Another part of this that you of course need to remember is that dynamic arrays can refer to memory that is not GC-allocated for dynamic arrays (be it stack-allocated or malloc-allocated or even GC-allocated for a different purpose), in which case, the capacity will be 0, because the underlying memory block is not a GC-allocated memory block for dynamic arrays, and appending anything to the array then has to result in a reallocation so that it then does point to a GC-allocated block of memory for dynamic arrays. If you want to create a dynamic array of a specific length while also ensuring that that there is at least a specific amount of memory in the underlying memory block for it to grow into, then you need the reserve function. It allows you to tell the GC how much memory you would like to be allocated underneath the hood. E.G. int[] a; a.reserve(500); assert(a.length == 0); writeln(a.capacity); prints 511 on my system. https://dlang.org/spec/arrays.html#capacity-reserve All that being said, if you're trying to minimize reallocations when appending to a dynamic array, you should consider using https://dlang.org/phobos/std_array.html#Appender since it has some other tricks to make it so that it queries the GC less and speeds the whole process up. But the basic idea of reserving capacity is the same, and when you're done appending, you get a normal dynamic array out of the deal. > 2a.) And what happens when the cell is extended, is the remainder > zero-filled or remaining full of garbage, or is the size of the > alloc cell something separate from the dynamic array’s knowledge > of the number of valid elements in it ? A dynamic array is basically this: struct DynamicArray(T) { size_t length; T* ptr; } It's just a slice of memory and has no clue whatsoever about what it points to. All of the logic for that comes from the druntime functions that you call to operate on dynamic arrays (e.g. ~= or capacity). It could be a slice of a static array, GC-allocated memory, malloced memory, etc. And the GC doesn't do anything to keep track of all of the dynamic arrays. They're basically just simple structs sitting on the stack or inside of the memory
Re: How does D’s ‘import’ work?
On Sunday, June 18, 2023 2:24:10 PM MDT Cecil Ward via Digitalmars-d-learn wrote: > I wasn’t intending to use DMD, rather ldc if possible or GDC > because of their excellent optimisation, in which DMD seems > lacking, is that fair? (Have only briefly looked at dmd+x86 and > haven’t given DMD’s back end a fair trial.) In general, dmd is fantastic for its fast compilation speed. So, it works really well for developing whatever software you're working on (whereas ldc and gdc are typically going to be slower at compiling). And depending on what you're doing, the code is plenty fast. However, if you want to maximize the efficiency of your code, then you definitely want to be building the binaries that you actually use or release with ldc or gdc. - Jonathan M Davis
Re: C++'s this() equivalent?
On Thursday, June 15, 2023 7:54:22 PM MDT Jonathan M Davis via Digitalmars-d- learn wrote: > On Thursday, June 15, 2023 7:18:25 PM MDT zjh via Digitalmars-d-learn wrote: > > On Friday, 16 June 2023 at 01:00:05 UTC, Steven Schveighoffer > > > > wrote: > > > B b = B.make(); // call factory function > > > > > > -Steve > > > > Thank you for your tip. > > If could simplify it a bit more, it would be even better. It's > > really uncomfortable without `this()`. > > The reasons for it have to do with how D in general is set up to require > that the value of all types be known at compile-time. This should say that it's set up to require that the _default_ value of all types be known at compile-time. - Jonathan M Davis