April 07, 2006
Sean Kelly wrote:
> Ares currently uses fprintf for this purpose, which is essentially the same thing.  And as you say, it would be easy enough to hook the function if this behavior isn't desirable.
> 
> I'm glad the compiler issues came to light however.  I don't suppose this will have any impact on the template code generation problem in libraries?

Unfortunately, no, as as I recall that is a limitation in the object file format.
April 07, 2006
Anders F Björklund wrote:
> Sean Kelly wrote:
> 
>>> Errors should be printed on stderr (fprintf), not on stdout (printf)...
>>
>> An easy change.  Just use fprintf and specify stderr as the output file. 
> 
> For some reason this change has been rejected earlier. I don't know why.
> Just thought that if the file is revised, then maybe it can be included?
> 
> http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D.bugs/2001
> http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D.bugs/4368

No idea.  I do suggest using fprintf instead of fwritefln, but I can't think of a reason not to do this.  I made the change to Ares quite a while ago.


Sean
April 07, 2006
Walter Bright wrote:
> 
> I know you're still bothered by the issue of C's IO being pulled in. I have another utility for you - \dm\bin\libunres. Libunres will identify any symbols unresolved by a library.

For reference, here's the result of running this utility on the Ares version of the DMD GC and DMD runtime, respectively.  The dependencies should be roughly equivalent to the appropriate portions of Phobos. Note that "_gc_*" "_thread_*" and "_on*" are defined by the GC or standard library:


C:\bin\dmd\lib>libunres dmdgc.lib
Unresolved externals:
_D6object6Object5opCmpFC6ObjectZi
_D6object6Object6toHashFZk
_D6object6Object8opEqualsFC6ObjectZi
_D6object6Object8toStringFZAa
__Class_6Object
__d_framehandler
__d_local_unwind2
__d_monitorenter
__d_monitorexit
__end
__except_list
__imp__GetCurrentThreadId@0
__imp__VirtualAlloc@16
__imp__VirtualFree@12
__nullext
__vtbl_9ClassInfo
__xi_a
_calloc
_free
_malloc
_memcpy
_memmove
_memset
_onOutOfMemory
_realloc
_thread_init
_thread_needLock
_thread_resumeAll
_thread_scanAll
_thread_suspendAll


C:\bin\dmd\lib>libunres dmdrt.lib
Unresolved externals:
__Ccmp
__Dmain
__LCMP@
__LDIV@
__ModuleInfo_3std1c4math
__ModuleInfo_3std1c5ctype
__ModuleInfo_3std1c5stdio
__ModuleInfo_3std1c6stdarg
__ModuleInfo_3std1c6stddef
__ModuleInfo_3std1c6stdlib
__ModuleInfo_3std1c6string
__ModuleInfo_3std1c7stdbool
__ULDIV@
___alloca
___fpclassify_d
___fpclassify_f
___fpclassify_ld
__assert
__except_list
__fltused
__global_unwind
__imp__DeleteCriticalSection@4
__imp__EnterCriticalSection@4
__imp__InitializeCriticalSection@4
__imp__LeaveCriticalSection@4
__imp__QueryPerformanceCounter@4
__imp__QueryPerformanceFrequency@4
__imp__RaiseException@16
__iob
__local_except_handler
_calloc
_exit
_fclose
_fgetc
_fopen
_fprintf
_free
_gc_calloc
_gc_free
_gc_init
_gc_malloc
_gc_realloc
_gc_setFinalizer
_gc_sizeOf
_gc_term
_isalpha
_isgraph
_isspace
_malloc
_memcmp
_memcpy
_memicmp
_memmove
_memset
_onArrayBoundsError
_onAssert
_onOutOfMemory
_onSwitchError
_onUnicodeError
_printf
_qsort
_sprintf
_strlen
_strtoul
_strtoull
_unmangle_ident


The ModuleInfo dependencies could be eliminated by explicitly declaring forward references to the appropriate C routines instead of importing the D header modules.  The remaining dependency list is really pretty minimal.  I am somewhat surprised to see printf in there, though--I must have missed a "debug" prefix somewhere.


Sean
April 07, 2006
Sean Kelly wrote:
> I am somewhat surprised to see printf in there, though--I must have missed a "debug" prefix somewhere.

Quick follow-up.  All printf calls in the DMD runtime are either commented out or are prefixed by a debug qualifier.  And the library was built without -debug set.  Any ideas why it would be listed as a dependency?  A grep of dmdrt.lib listed this:

._fprintf
._printf
._fprintf
._sprintf

so perhaps calling fprintf creates the other dependencies as well?  I'll give the map a look and see if it ofers any clues.


Sean
April 07, 2006
kris wrote:
> If you'll read the posts again, sans prejudice, I hope you'll find that they are about decoupling (like the title says).

What I did is ask what is being coupled that needs decoupling, i.e. trying to drill down to find the *real* issue with printf and std.string.toString. I don't agree with the notion that printf and its 4K of code is in the same category as Java's entire runtime library. In other words, I do not agree with absolutes like "coupling is always bad." Each case should be looked at individually on its merits.

For printf, the actual coupling problem is:

1) It pulls in floating point formatting code. This turns out to not be correct.
2) It's bloated. The bloat turns out to be 4K code - a big problem on an 8 bit machine to be sure, but not on a 32 bit one.
3) It pulls in the entire C I/O system. This turns out to also not be correct. It pulls in a reasonable portion of it.
4) It should be replaceable/hookable. Yes, it is.

On the other hand, the advantages of having a print in Object are:

1) Every object can be relied upon to have some sort of print method.
2) Substituting a toString() is problematic because it usually requires extra allocation and hence double buffering - so most I/O systems avoid such designs. Also, the print is often used for debugging, and having it be forced to use an allocation can upset what one is trying to debug - by causing a gc collection cycle, for example.
3) It being synchronized with C's IO means it doesn't screw up when mixed with normal IO code.

If printf pulled in a megabyte graphics library, sure, that's unreasonable coupling. But it doesn't. So, in my judgment, its benefits outweigh its disadvantages. You're free to disagree, but disagreement on a judgment call doesn't mean I or you are secretly convinced by the other's argument and lying about it.

Let's look at the 'coupling' problem of typeinfo calling std.string.toString:

1) It's assumed to link in all of std.string, and everything std.string references. This is not correct.
2) std.string does pull in the floating point code, but this is a compiler problem and easily (and already in my working version) fixed.
3) There's another problem where it pulled in std.uni, but again, this is a compiler problem and easily fixed. So what std.string.toString actually pulls in (given the compiler fixes) is just std.string.toString and a few bytes of static data.

So, in the end, the coupling turns out to be nonexistent. The assumption that calling one routine in a module brings in the entire module is not correct (and hasn't been since the late 80's). The proposed solution, calling C's itoa(), is problematic because it requires another allocation (itoa expects a 0 terminated string). Furthermore, itoa() is not a standard C function, so such dependency won't be portable. Writing another itoa unique to typeinfo kinda defeats the purpose of writing a library with reusable code.


> You made it implicitly clear there was no way you'd consider isolating object.d from std.string in order to make the former more amenable to alternate libraries. Thus, that aspect was completely ignored also.

No, I did not ignore it. I don't feel it's productive to rewrite the functions in std.string in each module. std.string is not a burdensome piece of code. Should I also rewrite memcpy() in every module? How far do you want to go to pursue decoupling for decoupling's sake? And suppose Fred discovers a way to double the speed of std.string.toString - pursuing your approach would mean none of the rest of the library would benefit from that. In my not-so-humble (!) opinion, when you're using copy/paste across modules, that's a red flag something is wrong with the design.

I'll agree with you that pointless and gratuitous coupling should be avoided, but that is not the case with the two examples we're discussing.

> Looking again at your recital of adamant examples, I'm rather sorry, and entirely disappointed that's all you got from this exchange.

I used the word adamant because you haven't acknowledged that I've addressed the underlying __fltused and std.uni issues, you haven't acknowledged that they are solvable compiler issues rather than library design issues, and just keep pushing the same solution.


> Yes, there's certainly truth there; but it apparently makes a point of purging all mention of decoupling ~ that's where frugality lies.

I shall reiterate that what I was doing was addressing what the underlying issue with coupling was. If those can be successfully addressed, then there is no coupling issue.


> You've again omitted a crucial part. The C runtime itself apparently has all kinds of interdependencies (the console startup/exit code is a prime example).

I did not omit it. I addressed that in my last post.

> Thus, the lists you show are simply the tip of the iceberg. I imagine you already know this quite intimately, so will suggest you do a step-by-step examination of the .map file for Derek's example:
> 
> void main() {}
> and ask yourself just why and where the kitchen-sink is linked?

And as I've already told you, I've already done that and that there are good reasons for the code that is linked in.
April 07, 2006
Sean Kelly wrote:
> Quick follow-up.  All printf calls in the DMD runtime are either commented out or are prefixed by a debug qualifier.  And the library was built without -debug set.  Any ideas why it would be listed as a dependency?  A grep of dmdrt.lib listed this:
> 
> ._fprintf
> ._printf
> ._fprintf
> ._sprintf
> 
> so perhaps calling fprintf creates the other dependencies as well?  I'll give the map a look and see if it ofers any clues.

You can see which module(s) is referencing them by using grep across the .obj files.
April 07, 2006
Sean Kelly wrote:
> Fredrik Olsson wrote:
>> And besides, is it wise to depend on what a linker "should do"? If current build chain nicely throws out what is not needed, does that make it right to assume that all build chains should behave as such?
> 
> I think this is a reasonable assumption, as to do otherwise necessitates design compromises to keep modules as small and isolated as possible. And while this may be reasonable for small projects, I can't see it working very well for large ones.

This capability of linkers (eliminating unreferenced functions) first appeared in the late 80's, and quickly became standard practice. If you've got a linker that doesn't support that, you're likely to have many other serious problems with it, as D (and C++) depend on other linker features introduced in the late 80's.

D doesn't require anything of a linker that C++ doesn't already realistically require.
April 07, 2006
Walter Bright schrieb am 2006-04-06:
> Although there is a lot of code in std.string, unreferenced free functions in it should be discarded by the linker. A check of the generated .map file should verify this - it is certainly supposed to work that way.

That's not what is happening on Linux:

> extern(C) int printf(char* x, ...){
>	*(cast(char*)0) = 'a';
> }
>
> int main(){
>	return 0;
> }

Symbols present in the executeable from std.string:
_D3std6string10capitalizeFAaZAa
_D3std6string10countcharsFAaAaZk
_D3std6string10expandtabsFAaiZAa
_D3std6string10splitlinesFAaZAAa
_D3std6string11removecharsFAaAaZAa
_D3std6string12replaceSliceFAaAaAaZAa
_D3std6string15StringException5_ctorFAaZC3std6string15StringException
_D3std6string2trFAaAaAaAaZAa
_D3std6string3cmpFAaAaZi
_D3std6string4atofFAaZe
_D3std6string4atoiFAaZl
_D3std6string4chopFAaZAa
_D3std6string4findFAaAaZi
_D3std6string4findFAawZi
_D3std6string4icmpFAaAaZi
_D3std6string4joinFAAaAaZAa
_D3std6string4succFAaZAa
_D3std6string4wrapFAaiAaAaiZAa
_D3std6string5chompFAaAaZAa
_D3std6string5countFAaAaZk
_D3std6string5entabFAaiZAa
_D3std6string5ifindFAaAaZi
_D3std6string5ifindFAawZi
_D3std6string5rfindFAaAaZi
_D3std6string5rfindFAawZi
_D3std6string5splitFAaAaZAAa
_D3std6string5splitFAaZAAa
_D3std6string5stripFAaZAa
_D3std6string5zfillFAaiZAa
_D3std6string6abbrevFAAaZHAaAa
_D3std6string6centerFAaiZAa
_D3std6string6columnFAaiZi
_D3std6string6formatFYAa
_D3std6string6insertFAakAaZAa
_D3std6string6irfindFAaAaZi
_D3std6string6irfindFAawZi
_D3std6string6repeatFAakZAa
_D3std6string6striplFAaZAa
_D3std6string6striprFAaZAa
_D3std6string7iswhiteFwZi
_D3std6string7replaceFAaAaAaZAa
_D3std6string7sformatFAaYAa
_D3std6string7soundexFAaAaZAa
_D3std6string7squeezeFAaAaZAa
_D3std6string7toCharzFAaZPa
_D3std6string7tolowerFAaZAa
_D3std6string7toupperFAaZAa
_D3std6string8capwordsFAaZAa
_D3std6string8ljustifyFAaiZAa
_D3std6string8rjustifyFAaiZAa
_D3std6string8toStringFPaZAa
_D3std6string8toStringFaZAa
_D3std6string8toStringFcZAa
_D3std6string8toStringFdZAa
_D3std6string8toStringFeZAa
_D3std6string8toStringFfZAa
_D3std6string8toStringFgZAa
_D3std6string8toStringFhZAa
_D3std6string8toStringFiZAa
_D3std6string8toStringFjZAa
_D3std6string8toStringFkZAa
_D3std6string8toStringFlZAa
_D3std6string8toStringFlkZAa
_D3std6string8toStringFmZAa
_D3std6string8toStringFmkZAa
_D3std6string8toStringFoZAa
_D3std6string8toStringFpZAa
_D3std6string8toStringFqZAa
_D3std6string8toStringFrZAa
_D3std6string8toStringFsZAa
_D3std6string8toStringFtZAa
_D3std6string8toStringFxZAa
_D3std6string9inPatternFwAAaZi
_D3std6string9inPatternFwAaZi
_D3std6string9isNumericFAC8TypeInfoPvZx
_D3std6string9isNumericFAaxZx
_D3std6string9isNumericFYx
_D3std6string9maketransFAaAaZAa
_D3std6string9toStringzFAaZPa
_D3std6string9translateFAaAaAaZAa


dmd a.d -L--cref:

internal/arraycat.d:101
current:
> throw new Error(std.string.format("lengths don't match for array copy,
> %s = %s", to.length, from.length));

suggested:
> throw new Error("lengths don't match for array copy," ~
> toString(to.length) ~ " = " ~ toString(from.length));

Thomas


April 07, 2006
Thomas Kuehne wrote:
> Walter Bright schrieb am 2006-04-06:
>> Although there is a lot of code in std.string, unreferenced free functions in it should be discarded by the linker. A check of the generated .map file should verify this - it is certainly supposed to work that way.
> 
> That's not what is happening on Linux:
...
> dmd a.d -L--cref:

Should compile with -O -release to check this.


> internal/arraycat.d:101
> current:
>> throw new Error(std.string.format("lengths don't match for array copy,
>> %s = %s", to.length, from.length));
> 
> suggested:
>> throw new Error("lengths don't match for array copy," ~
>> toString(to.length) ~ " = " ~ toString(from.length));

Thanks, I'll do that.
April 07, 2006
Walter Bright wrote:
> 
> You can see which module(s) is referencing them by using grep across the .obj files.

Thanks.  Turns out it was a debug printf I'd left in place by accident.


Sean