Re: Why does Nullable implicitly casts when assigning a variable but not when returning from a function?

2024-04-10 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-04-06 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-04-06 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-04-06 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-04-02 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-03-29 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-03-28 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-03-24 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-03-23 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-03-23 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-03-23 Thread Jonathan M Davis via Digitalmars-d-learn
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.

2024-03-13 Thread Jonathan M Davis via Digitalmars-d-learn
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.

2024-03-13 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-03-11 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-03-11 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-03-06 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-02-16 Thread Jonathan M Davis via Digitalmars-d-learn
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 ?

2024-02-14 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-02-13 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-02-10 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-02-10 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-02-10 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-02-10 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-25 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-23 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-19 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-01-18 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-01-18 Thread Jonathan M Davis via Digitalmars-d-learn
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.

2024-01-17 Thread Jonathan M Davis via Digitalmars-d-learn
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.

2024-01-17 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-17 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-16 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-16 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2024-01-15 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-09 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-09 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-09 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-02 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-02 Thread Jonathan M Davis via Digitalmars-d-learn
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

2024-01-01 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-12-17 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-12-08 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-12-05 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-11-27 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-11-23 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-11-23 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-11-19 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-11-17 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-11-16 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-11-16 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-11-05 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-11-04 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-11-04 Thread Jonathan M Davis via Digitalmars-d-learn
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"

2023-11-03 Thread Jonathan M Davis via Digitalmars-d-learn
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"

2023-11-02 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-31 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-31 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-12 Thread Jonathan M Davis via Digitalmars-d-learn
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!

2023-10-12 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-10-09 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-10-08 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-10-08 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-10-07 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-03 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-03 Thread Jonathan M Davis via Digitalmars-d-learn
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()

2023-10-01 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-10-01 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-09-25 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-09-22 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-09-22 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-09-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-09-13 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-09-04 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-09-02 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-08-27 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-08-25 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-08-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-08-02 Thread Jonathan M Davis via Digitalmars-d-learn
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`

2023-08-02 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-07-31 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-07-27 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-07-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-07-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-07-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-07-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-07-20 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-30 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-06-26 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-26 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-26 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-26 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-24 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-24 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-23 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-21 Thread Jonathan M Davis via Digitalmars-d-learn
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

2023-06-20 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-06-18 Thread Jonathan M Davis via Digitalmars-d-learn
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?

2023-06-15 Thread Jonathan M Davis via Digitalmars-d-learn
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





  1   2   3   4   5   6   7   8   9   10   >