December 16, 2011
On Friday, December 16, 2011 22:41:14 Timon Gehr wrote:
> On 12/16/2011 09:31 PM, Jonathan M Davis wrote:
> You don't need a C function if you just factor out every variable it
> initializes to the separate D module. __fileinit.d works that way. I
> don't see why stdiobase.d could not do the same.

That only works if the variable being initialized is in the new module instead of the original module, which you can't always do.

> > It works, but it's not pretty (and it doesn't always work
> > - e.g. std.datetime), and it would be _far_ better if you could just
> > mark a static constructor as not depending on anything or mark it as
> > not depending on a specific module or something similar.
> 
> How would that be checked?

It wouldn't be. It wouldn't need to be. The programmer is telling the compiler that there isn't a dependency. It's up to the programmer to make sure that it's right, and it's wrong, it's their fault. There are plenty of other features like that in D - just not SafeD.

> > annoying issues in D IMHO.
> Adding a language construct that turns off the checking entirely (as you seem to suggest) is not at all better than having to create a few additional source files.

I completely disagree. For instance, it's impossible to move the singleton instances of UTC and LocalTime from std.datetime into another module without breaking encapsulation, and it's definitely impossible to do it and leave them as members of their respective classes. Those static constructors clearly don't rely on any other modules except for the one which gives the declaration for tzset (and has no static constructors). But if std.file needed a module constructor, we'd end up with a circular dependency between std.datetime and std.file when clearly nothing in std.datetime's static constructor relies on std.file in any way shape or form. It would be a huge improvement to be able to just mark those static constructors as not relying on any other modules having their static constructors run first. As it stands, it's a royal pain to deal with any circular dependencies which pop up and because of that, it quickly becomes best practice to avoid static constructors as much as possible, which is a big problem IMHO.

Factoring out the static constructor's contents into a separate module is not always possible, and it's an ugly solution IMHO. I'd _much_ rather have a feature where I can tell the compiler that there is no circular dependency so that it can appropriately order the loading of the modules.

- Jonathan M Davis
December 16, 2011
On 16/12/2011 18:29, Andrei Alexandrescu wrote:
>
> Here's a list of all files in std using static cdtors:
>
> std/__fileinit.d
> std/concurrency.d
> std/cpuid.d
> std/cstream.d
> std/datebase.d
> std/datetime.d
> std/encoding.d
> std/internal/math/biguintcore.d
> std/internal/math/biguintx86.d
> std/internal/processinit.d
> std/internal/windows/advapi32.d
> std/mmfile.d
> std/parallelism.d
> std/perf.d
> std/socket.d
> std/stdiobase.d
> std/uri.d
>

On a slightly related note: http://d.puremagic.com/issues/show_bug.cgi?id=5614

Basically, do the static constructors in __fileinit and mmfile need to exist on a (hypothetical) 64bit Windows build?
December 16, 2011
On Friday, December 16, 2011 23:30:44 torhu wrote:
> On 16.12.2011 22:28, Steven Schveighoffer wrote:
> > In short, dlls will solve the problem, let's work on that instead of shuffling around code.
> 
> How exactly do they solve the problem? An exe plus a DLL version of the library will usually be larger than just a statically linked exe.

You have to stick it all in the DLL anyway (since you can't know which parts will and won't be used), so the whole issue of not including used functionality goes away completely. There's no point in worrying about how much unused functionality gets included when you have no choice but to include everything regardless of whether it's actually used.

- Jonathan M Davis
December 16, 2011
On 12/16/2011 1:41 PM, Timon Gehr wrote:
> Adding a language construct that turns off the checking entirely (as you seem to
> suggest) is not at all better than having to create a few additional source files.

I also don't really see how turning off checking is even slightly more elegant than using a dirty cast.

The additional source file thing is best because it fits in with the guarantees of the language - it is not a hack nor does it require trust in the programmer to get it right. It's not going to have heisenbugs where it working or not depends on arbitrary link order.

December 16, 2011
On Dec 16, 2011, at 10:29 AM, Andrei Alexandrescu wrote:
> 
> But in experiments it seemed like program size would increase in sudden amounts when certain modules were included. After much investigation we figured that the following fateful causal sequence happened:
> 
> 1. Some modules define static constructors with "static this()" or "static shared this()", and/or static destructors.
> 
> 2. These constructors/destructors are linked in automatically whenever a module is included.
> 
> 3. Importing a module with a static constructor (or destructor) will generate its ModuleInfo structure, which contains static information about all module members. In particular, it keeps virtual table pointers for all classes defined inside the module.

What is gained from having class vtbls referenced by ModuleInfo?  Could we put them elsewhere?
December 16, 2011
On Dec 16, 2011, at 12:44 PM, Andrei Alexandrescu wrote:
> 
> Consider scope. Many arguments applicable to application code are not quite fit for the standard library. The stdlib is the connection between the compiler innards, the runtime innards, and the OS innards all meet, and the role of the stdlib is to provide nice abstractions to client code. Inside the stdlib it's entirely expected to find things like __traits most nobody heard of, casts, and other things that would be normally shunned in application code. I'd be more worried if there was no possibility to do what we need to do. The standard library is not a place to play it nice. We can't afford to say "well yeah everyone's binary is bloated and slower to start but we didn't like the cast that would have taken care of that".

I think this is a reasonable assertion about druntime, but the standard library itself should require very little black magic, though the use of obscure features (like __traits) could be commonplace.
December 16, 2011
On 12/16/2011 1:45 PM, Andrei Alexandrescu wrote:
> On 12/16/11 3:38 PM, Trass3r wrote:
>> A related issue is phobos being an intermodule dependency monster.
>> A simple hello world pulls in almost 30 modules!
>> And std.stdio is supposed to be just a simple wrapper around C FILE.
>
> In fact it doesn't (after yesterday's commit). The std code in hello, world is a
> minuscule 3KB. The rest of 218KB is druntime.

Another thing is to avoid using classes for things where one does not expect it to ever be derived from. Use a struct instead, as referencing parts of the struct implementation will not pull in the whole of it, nor is there a vtbl[] to pull it all in.

For example, in std.datetime there's "final class Clock". It inherits nothing, and nothing can be derived from it. The comments for it say it is merely a namespace. It should be a struct.
December 16, 2011
On 12/16/2011 11:39 PM, Jonathan M Davis wrote:
> [...]  For instance, it's impossible to move the singleton
> instances of UTC and LocalTime from std.datetime into another module without
> breaking encapsulation.

In what way would encapsulation be broken by just moving the class to a helper module?
December 16, 2011
On 12/16/11 2:58 PM, Jonathan M Davis wrote:
> Unfortunately, the necessity of tzset would remain however.

Why? From http://pubs.opengroup.org/onlinepubs/007904875/functions/tzset.html:

"The tzset() function shall use the value of the environment variable TZ to set time conversion information used by ctime(), localtime(), mktime(), and strftime(). If TZ is absent from the environment, implementation-defined default timezone information shall be used."

I'd expect a good standard library implementation for D would call tzset() once per process instance, lazily, inside the wrapper functions for the four functions above. Alternatively, people could call the stdc.* versions and expect tzet() to _not_ having been called.

That strikes the right balance between convenience, flexibility, and efficiency.


Andrei
December 16, 2011
On 12/16/11 4:55 PM, Sean Kelly wrote:
> On Dec 16, 2011, at 12:44 PM, Andrei Alexandrescu wrote:
>>
>> Consider scope. Many arguments applicable to application code are
>> not quite fit for the standard library. The stdlib is the
>> connection between the compiler innards, the runtime innards, and
>> the OS innards all meet, and the role of the stdlib is to provide
>> nice abstractions to client code. Inside the stdlib it's entirely
>> expected to find things like __traits most nobody heard of, casts,
>> and other things that would be normally shunned in application
>> code. I'd be more worried if there was no possibility to do what we
>> need to do. The standard library is not a place to play it nice. We
>> can't afford to say "well yeah everyone's binary is bloated and
>> slower to start but we didn't like the cast that would have taken
>> care of that".
>
> I think this is a reasonable assertion about druntime, but the
> standard library itself should require very little black magic,

"Very little" sounds almost enough :o).

> though the use of obscure features (like __traits) could be
> commonplace.

Absolutely.


Andrei