Re: Static constructor

2021-01-06 Thread SealabJaster via Digitalmars-d-learn

On Thursday, 7 January 2021 at 01:55:07 UTC, SealabJaster wrote:

...


And on a side note, I don't believe this code is working as the 
author intends.


Someone correct me if I'm wrong, but `static` variables in D 
arethread-local, so a `static Object mutex` wouldn't actually be 
visible between threads unless passed via some other means, and 
would instead act as a mutex-per-thread, sort of defying the 
point?


However `__gshared Object mutex` would likely be what they want?

I don't do much multithreaded stuff in D, so that information may 
or may not be correct.


Re: Static constructor

2021-01-06 Thread SealabJaster via Digitalmars-d-learn

On Wednesday, 6 January 2021 at 17:05:02 UTC, ludo wrote:

...


Using a static class like this seems to mostly be a design 
decision.


i.e. instead of using something like

```
openalGetMutex();

// OR

OpenAL openal = ...;
openal.getMutex();

// OR I guess even this

g_openal.getMutex();
```

They specifically want the interface to look like:

```
OpenAL.getMutex();
```

So in otherwords, this class is essentially being used as a 
unique namespace for all of the other functions. Another way to 
look at it is just a singleton without a `.instance` getter.


As for some of the other stuff, Associative Array literals in D 
can't actually be used at compile-time (at least, last time I 
tried), so they have to be created inside of the static 
constructor.


Next is the mutex. `Object` is the base class that every class in 
D will implicitly inherit from, like in C# for example. Object 
has a `.monitor` field which is basically just a mutex, so is 
used with multi-threading synchronisation. I find it a bit odd 
that they're just returning an object instead of a Mutex (module 
core.sync.mutex), but I'm sure there's a reason why.


(The README of this project states that this is a resurrection of 
some old project, and even refers to D as "Digital Mars", so this 
code may or may not have been built on a very old version of D 
originally.)


A better explanation of the monitor field can be found: 
https://forum.dlang.org/post/op.vqi9vrqueav7ka@steve-laptop


Hope this answers some questions.


Re: DConf talk : Exceptions will disappear in the future?

2021-01-06 Thread sighoya via Digitalmars-d-learn

On Wednesday, 6 January 2021 at 21:27:59 UTC, H. S. Teoh wrote:
It must be unique because different functions may return 
different sets of error codes. If these sets overlap, then once 
the error propagates up the call stack it becomes ambiguous 
which error it is.


Contrived example:

enum FuncAError { fileNotFound = 1, ioError = 2 }
enum FuncBError { outOfMem = 1, networkError = 2 }

int funcA() { throw FuncAError.fileNotFound; }
int funcB() { throw FuncBError.outOfMem; }

void main() {
try {
funcA();
funcB();
} catch (Error e) {
// cannot distinguish between FuncAError and
// FuncBError
}
}



Thanks, reminds on swift error types which are enum cases.
So the type is the pointer to the enum or something which 
describes the enum uniquely and the context is the enum value, or 
does the context describe where to find the enum value in the 
statically allocated object.



Using the typeid is no good because: (1) typeid in D is a


Sorry, I misspelled it, I meant the internal id in which type is 
turned to by the compiler, not the RTTI structure of a type at 
runtime.


If you're in @nogc code, you can point to a 
statically-allocated block that the throwing code updates with 
relevant information about the error, e.g., a struct that 
contains further details about the error


But the amount of information for an error can't be statically 
known. So we can't pre-allocate it via a statically allocated 
block, we need some kind of runtime polymorphism here to know all 
the fields considered.


You don't need to box anything.  The unique type ID already 
tells you what type the context is, whether it's integer or 
pointer and what the type of the latter is.


The question is how can a type id as integer value do that, is 
there any mask to retrieve this kind of information from the type 
id field, e.g. the first three bits say something about the 
context data type or did we use some kind of log2n hashing of the 
typeid to retrieve that kind of information.



When you `throw` something, this is what is returned from the 
function. To propagate it, you just return it, using the usual 
function return mechanisms.  It's "zero-cost" because it the 
cost is exactly the same as normal returns from a function.


Except that bit check after each call is required which is 
neglectable for some function calls, but it's summing up rapidly 
for the whole amount of modularization.
Further, the space for the return value in the caller needs to be 
widened in some cases.


Only if you want to use traditional dynamically-allocated 
exceptions. If you only need error codes, no polymorphism is 
needed.


Checking the bit flag is runtime polymorphism, checking the type 
field against the catches is runtime polymorphism, checking what 
the typeid tells about the context type is runtime polymorphism. 
Checking the type of information behind the context pointer in 
case of non error codes is runtime polymorphism.
The only difference is it is coded somewhat more low level and is 
a bit more compact than a class object.
What if we use structs for exceptions where the first field is 
the type and the second field the string message pointer/or error 
code?



The traditional implementation of stack unwinding bypasses 
normal function return mechanisms.  It's basically a glorified 
longjmp() to the catch block, augmented with the automatic 
destruction of any objects that might need destruction on the 
way up the call stack.


It depends. There are two ways I know, either jumping or 
decrementing the stack pointer and read out the information in 
the exception tables.




Turns out, the latter is not quite so simple in practice.  In 
order to properly destroy objects on the way up to the catch 
block, you need to store information about what to destroy 
somewhere.


I can't imagine why this is different in your case, this is 
generally the problem of exception handling independent of the 
underlying mechanism. Once the pointer of the first landing pad 
is known, the control flow continues as known before until the 
next error is thrown.
You also need to know where the catch blocks are so that you 
know where to land. Once you land, you need to know how to 
match the exception type to what the catch block expects, etc.. 
To implement this, every function needs to setup standard stack 
frames so that libunwind knows how to unwind the stack.


Touché, that's better in case of error returns.

It also requires exception tables, an LSDA (language-specific 
data area) for each function, personality functions, etc..  A 
whole bunch of heavy machinery just to get things to work 
properly.



Why would you want to insert it into a list?  The context field 
is a type-erased pointer-sized value. It may not even be a 
pointer.




Good point, I don't know 

Re: DConf talk : Exceptions will disappear in the future?

2021-01-06 Thread Ola Fosheim Grøstad via Digitalmars-d-learn

On Wednesday, 6 January 2021 at 21:27:59 UTC, H. S. Teoh wrote:
It must be unique because different functions may return 
different sets of error codes. If these sets overlap, then once 
the error propagates up the call stack it becomes ambiguous 
which error it is.


I don't think this is the case. If you analyse the full program 
then you know the functions that interact. All you need to do is 
dataflow analysis.


I also don't think there should be a specific error-code, I think 
that should be left implementation defined. The program should 
just specify a set of errors. Then it is up to the compiler if 
that for a given call can be represented using some free bits in 
another return value as a nullpointer or whatever.


If speed is what is sought, well, then design for it. :-)



Re: DConf talk : Exceptions will disappear in the future?

2021-01-06 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Jan 06, 2021 at 05:36:07PM +, sighoya via Digitalmars-d-learn wrote:
> On Tuesday, 5 January 2021 at 21:46:46 UTC, H. S. Teoh wrote:
> > 4) The universal error type contains two fields: a type field and a
> > context field.
> > 
> > a) The type field is an ID unique to every thrown exception --
> > uniqueness can be guaranteed by making this a pointer to some
> > static global object that the compiler implicitly inserts per
> > throw statement, so it will be unique even across shared
> > libraries. The catch block can use this field to determine what
> > the error was, or it can just call some standard function to
> > turn this into a string message, print it and abort.
> 
> Why it must be unique? Doesn't it suffice to return the typeid here?

It must be unique because different functions may return different sets
of error codes. If these sets overlap, then once the error propagates up
the call stack it becomes ambiguous which error it is.

Contrived example:

enum FuncAError { fileNotFound = 1, ioError = 2 }
enum FuncBError { outOfMem = 1, networkError = 2 }

int funcA() { throw FuncAError.fileNotFound; }
int funcB() { throw FuncBError.outOfMem; }

void main() {
try {
funcA();
funcB();
} catch (Error e) {
// cannot distinguish between FuncAError and
// FuncBError
}
}

Using the typeid is no good because: (1) typeid in D is a gigantic
historic hack containing cruft that even Walter doesn't fully
understand; (2) when all you want is to return an integer return code,
using typeid is overkill.


> > b) The context field contains exception-specific data that gives
> > more information about the nature of the specific instance of
> > the error that occurred, e.g., an integer value, or a pointer to
> > a string description or block of additional information about
> > the error (set by the thrower), or even a pointer to a
> > dynamically-allocated exception object if the user wishes to use
> > traditional polymorphic exceptions.
> 
> Okay, but in 99% you need dynamically allocated objects because the
> context is most of the time simply unknown.

If the context is sufficiently represented in a pointer-sized integer,
there is no need for allocation at all. E.g., if you're returning an
integer error code.

If you're in @nogc code, you can point to a statically-allocated block
that the throwing code updates with relevant information about the
error, e.g., a struct that contains further details about the error.

If you're using traditional polymorphic exceptions, you already have to
allocate anyway, so this does not add any overhead.


> But yes, in specific cases a simple error code suffice, but even then
> it would be better to be aware that an error code is returned instead
> of a runtime object. It sucks to me to box over the context
> pointer/value to find out if it is an error code or not when I only
> want an error code.

You don't need to box anything.  The unique type ID already tells you
what type the context is, whether it's integer or pointer and what the
type of the latter is.


> > c) The universal error type is constrained to have trivial move
> > semantics, i.e., propagating it up the call stack is as simple
> > as blitting the bytes over. (Any object(s) it points to need not
> > be thus constrained, though.)
> > 
> > The value semantics of the universal error type ensures that there
> > is no overhead in propagating it up the call stack.  The
> > universality of the universal error type allows it to represent
> > errors of any kind without needing runtime polymorphism, thus
> > eliminating the overhead the current exception implementation
> > incurs.
> 
> So it seems the universal error type just tells me if there is or
> isn't error and checking for it is just a bitflip?

No, it's a struct that represents the error. Basically:

struct Error {
size_t type;
size_t context;
}

When you `throw` something, this is what is returned from the function.
To propagate it, you just return it, using the usual function return
mechanisms.  It's "zero-cost" because it the cost is exactly the same as
normal returns from a function.


> > The context field, however, still allows runtime polymorphism to be
> > supported, should the user wish to.
> 
> Which in most of the cases will be required.

Only if you want to use traditional dynamically-allocated exceptions. If
you only need error codes, no polymorphism is needed.


[...]
> > Of course, this was proposed for C++, so a D implementation will
> > probably be somewhat different.  But the underlying thrust is:
> > exceptions become value types by default, thus eliminating most of
> > the overhead associated with the current exception 

Re: DConf talk : Exceptions will disappear in the future?

2021-01-06 Thread Dukc via Digitalmars-d-learn

On Monday, 4 January 2021 at 15:39:50 UTC, ludo456 wrote:
Listening to the first visioconf of the Dconf 2020, titled 
Destroy All Memory Corruption, 
(https://www.youtube.com/watch?v=XQHAIglE9CU) Walter talks 
about not using exceptions any more in the future. He says 
something like "this is where languages are going" [towards no 
using exceptions any more].


I don't think exceptions are going anywhere. It might be that new 
libraries tend to avoid them (to work with @nothrow and @live), 
but there is no reason to banish them from the whole language - 
that would only result in huge breakage for limited benefit.


And I suspect Walter didn't mean all code -just the relatively 
low-level stuff that might want to use `@live`. Even if he did, 
community will force him to reconsider.





Re: DConf talk : Exceptions will disappear in the future?

2021-01-06 Thread sighoya via Digitalmars-d-learn

Citing Herb Sutter:
As noted in §1.1, preconditions, postconditions, and assertions 
are for identifying program bugs, they are never recoverable 
errors; violating them is always corruption, undefined behavior. 
Therefore they should never be reported via error reporting 
channels (regardless of whether exceptions, error codes, or 
another style is used). Instead, once we have contracts 
(expected in C++20), users should be taught to prefer expressing 
these as contracts, and we should consider using those also in 
the standard library.


Oh men, did you ever hear of non-determinism?
Why not just use compile time contracts and path dependent typing 
to solve those problems as well?

Because perfectionism is our enemy in productive development.
And terminating the whole program doesn't help either, exactly 
for this purpose we have error types or contexts, to know to 
which degree we are required to terminate and this should hold 
even for contracts.


Re: DConf talk : Exceptions will disappear in the future?

2021-01-06 Thread sighoya via Digitalmars-d-learn

On Tuesday, 5 January 2021 at 21:46:46 UTC, H. S. Teoh wrote:
4) The universal error type contains two fields: a type field 
and a context field.


a) The type field is an ID unique to every thrown exception 
--
uniqueness can be guaranteed by making this a pointer to 
some static

global object that the compiler implicitly inserts per throw
statement, so it will be unique even across shared 
libraries. The
catch block can use this field to determine what the error 
was, or
it can just call some standard function to turn this into a 
string

message, print it and abort.


Why it must be unique? Doesn't it suffice to return the typeid 
here?




b) The context field contains exception-specific data that 
gives
more information about the nature of the specific instance 
of the
error that occurred, e.g., an integer value, or a pointer 
to a
string description or block of additional information about 
the

error (set by the thrower), or even a pointer to a
dynamically-allocated exception object if the user wishes 
to use

traditional polymorphic exceptions.


Okay, but in 99% you need dynamically allocated objects because 
the context is most of the time simply unknown.


But yes, in specific cases a simple error code suffice, but even 
then it would be better to be aware that an error code is 
returned instead of a runtime object. It sucks to me to box over 
the context pointer/value to find out if it is an error code or 
not when I only want an error code.


c) The universal error type is constrained to have trivial 
move
semantics, i.e., propagating it up the call stack is as 
simple as
blitting the bytes over. (Any object(s) it points to need 
not be

thus constrained, though.)

The value semantics of the universal error type ensures that 
there is no overhead in propagating it up the call stack.  The 
universality of the universal error type allows it to represent 
errors of any kind without needing runtime polymorphism, thus 
eliminating the overhead the current exception implementation 
incurs.


So it seems the universal error type just tells me if there is or 
isn't error and checking for it is just a bitflip?


The context field, however, still allows runtime polymorphism 
to be supported, should the user wish to.


Which in most of the cases will be required.

The addition of the universal error type to return value is 
automated by the compiler, and the user need not worry about 
it.  The usual try/catch syntax can be built on top of it.


Of course, this was proposed for C++, so a D implementation 
will probably be somewhat different.  But the underlying thrust 
is: exceptions become value types by default, thus eliminating 
most of the overhead associated with the current exception 
implementation.


I didn't know exactly how this is implemented in D, but class 
objects are passed as simple pointer and pointers are likewise 
value types.
Using value types itself doesn't guarantee anything about 
performance, because the context field of an exception can be 
anything you need some kind of boxing involving runtime 
polymorphism anyway.


 Stack unwinding is replaced by normal function return 
mechanisms, which is much more optimizer-friendly.


I heard that all the time, but why is that true?


This also lets us support exceptions in @nogc code.


Okay, this would be optionally great. However, if we insert the 
context pointer into a List we may get a problem of cyclicity.


There is no need for a cascade of updates if you do it right. 
As I hinted at above, this enumeration does not have to be a 
literal enumeration from 0 to N; the only thing required is 
that it is unique *within the context of a running program*.  A 
pointer to a static global suffices to serve such a role: it is 
guaranteed to be unique in the program's address space, and it 
fits in a size_t.  The actual value may differ across different 
executions, but that's not a problem: any references to the ID 
from user code is resolved by the runtime dynamic linker -- as 
it already does for pointers to global objects.  This also 
takes care of any shared libraries or dynamically loaded .so's 
or DLLs.


What means unique, why is it important? Type ids aren't unique to 
distinguish exceptions and I don't know why we need this 
requirement.
The point in Rust or Java was to limit the plurality of error 
types a function call receive, but this is exactly the point 
where idiomatic and productive development differs. Assumptions 
change and there you are.



I've said this before, that the complaints about the current 
exception handling mechanism is really an issue of how it's 
implemented, rather than the concept of exceptions itself.


Okay, I think this is definitely debatable.

 If we implement Sutter's proposal, or something similar 
suitably adapted to D, it would eliminate the runtime overhead, 
solve the @nogc exceptions issue, and still support 

Re: Static constructor

2021-01-06 Thread Imperatorn via Digitalmars-d-learn

On Wednesday, 6 January 2021 at 17:05:02 UTC, ludo wrote:

I read in the documentation
"Static constructors are used to initialize static class 
members with values that cannot be computed at compile time"


[...]


Since this is not the complete code it's a bit hard to know, but 
I'd guess this is for use as a singleton and/or for thread safety.


Static constructor

2021-01-06 Thread ludo via Digitalmars-d-learn

I read in the documentation
"Static constructors are used to initialize static class members 
with values that cannot be computed at compile time"


I try to understand the design of the following code:

---
class OpenAL
{
static string[int] ALErrorLookup;
static Object mutex;

// Initialize static variables
static this ()
{   ALErrorLookup = [
0xA001: "AL_INVALID_NAME"[],
0xA002: "AL_ILLEGAL_ENUM",
0xA002: "AL_INVALID_ENUM",
0xA003: "AL_INVALID_VALUE",
0xA004: "AL_ILLEGAL_COMMAND",
0xA004: "AL_INVALID_OPERATION",
0xA005: "AL_OUT_OF_MEMORY"
];
mutex = new Object();
}

   static anotherfunc()
   {}

static Object getMutex()
{   return mutex;
}
}
---

At this point, I have not looked up Object, guess must be a 
class. It seems to me that ALErrorLookup can be computed at 
compile time... So the constructor is static because mutex "can 
not be computed at compiled time"?


The idea of the dev (from context) is to that this class will 
just be a wrapper, no instance necessary. So anotherfunc(), which 
does the main work, is static and everything goes this way.


Then getMutex returns the static mutex when necessary... Have not 
looked that up yet either.


But, I don't know, i have a feeling that this is over 
complicated. For example, can't we have AlErrorlook-up 
initialized another way in D, a static mutex in the getMutex 
function directly (with if (mutex == null) {mutex = new Object()} 
.


I don't know, is it the proper D way? And also, when we have 
those classes with everything static, does it even make sense to 
have a class? This module actually contains only this class 
(https://tinyurl.com/yxt2xw23) Shouldn't we have one module with 
normal functions?


ANY input is learning material for me. Thanks.






Re: dmd 2.093.1: duplicate method definitions ignored altogether

2021-01-06 Thread Jacob Carlborg via Digitalmars-d-learn

On 2021-01-05 03:02, kdevel wrote:

expected output: none. The compiler should have rejected the code after 
the duplicate definition def #2. dmd 2.093.1 ignores both definitions 
instead. Is this a bug or a bug?


DMD 2.095.0 now reports an error for this.

--
/Jacob Carlborg