digitalmars.D - DIP 45 - approval discussion
- Benjamin Thaut (22/22) Nov 10 2013 Attached you will find a e-mail conversation about the discussion of DIP...
- Benjamin Thaut (3/6) Nov 10 2013 experience. I approve with the mention that I'm not an expert in the
- Benjamin Thaut (7/15) Nov 10 2013 practical experience. I approve with the mention that I'm not an expert
- Benjamin Thaut (17/25) Nov 10 2013 symbols and not others. This is all or nothing.
- Benjamin Thaut (60/68) Nov 10 2013 -lib switch proposed behavior:
- Benjamin Thaut (1/20) Nov 10 2013
- Timon Gehr (5/14) Nov 10 2013 Using the export attribute on some member exports the entire module
- Benjamin Thaut (5/20) Nov 10 2013 AFAIK the module info only contains information about the (shared)
- Timon Gehr (2/24) Nov 10 2013 What about Object.factory?
- Benjamin Thaut (4/29) Nov 10 2013 That would obviously work. But why would you want to restrict that for
- deadalnix (2/3) Nov 11 2013 I always though of it as a misfeature.
- Timon Gehr (2/5) Nov 11 2013 Me too.
- Andrei Alexandrescu (3/9) Nov 11 2013 ... well time to substantiate.
- deadalnix (18/28) Nov 11 2013 OK, but first, it has to be noted that this is putting the burden
- Andrei Alexandrescu (14/43) Nov 11 2013 That really proves nothing.
- deadalnix (27/57) Nov 11 2013 That doesn't prove anything. But that doesn't smell good for the
- Andrei Alexandrescu (25/74) Nov 12 2013 My code doesn't use that, either. But it also doesn't use things like
- Benjamin Thaut (4/9) Nov 12 2013 Wait. Whats the actual issue here? This DIP does not prevent
- Andrei Alexandrescu (4/17) Nov 12 2013 You're right. I was meaning that in the abstract - generally feature
- deadalnix (13/22) Nov 12 2013 That isn't the same. toString itself rely on typeid. And the
- Jacob Carlborg (5/12) Nov 11 2013 I used it in my serialization library, Orange, and it will be used in
- Jacob Carlborg (4/6) Nov 12 2013 Well, technically I'm using ClassInfo.find.
- Benjamin Thaut (3/10) Nov 12 2013 But what is the actual argument? What problem do you see regarding
- Timon Gehr (10/20) Nov 12 2013 - Every class in any imported module will need to show up in the
- Andrei Alexandrescu (13/35) Nov 12 2013 Correct. On the other hand, a lot of unused classes in used modules
- deadalnix (7/21) Nov 12 2013 The serialization is a good example. You'll have to note that if
- Andrei Alexandrescu (8/25) Nov 12 2013 I think there is some confusion here. We're talking about polymorphic
- Jacob Carlborg (7/10) Nov 13 2013 No, not necessarily. If you serialize an object through a base class
- deadalnix (11/22) Nov 13 2013 OK, I see your problem here.
- Jacob Carlborg (6/14) Nov 13 2013 Might be solveable with AST macros :). Although you can still have
- Timon Gehr (16/39) Nov 12 2013 It is not necessary that there are a lot. Anything transitively
- Andrei Alexandrescu (10/54) Nov 12 2013 Wait, doesn't Object.factory call the default constructor of the created...
- Timon Gehr (6/30) Nov 12 2013 Yes, I was listing some ways to do this that I think are better than
- Andrei Alexandrescu (4/21) Nov 12 2013 OK I think I figure you are confused. You can't call the default
- Timon Gehr (8/32) Nov 12 2013 Indeed. There was a premise stating that I do. :o)
- Andrei Alexandrescu (15/51) Nov 12 2013 Then how do you figure doing this:
- Jacob Carlborg (7/16) Nov 13 2013 This requires Object.factory (or equivalent) and that all subclasses
- Andrei Alexandrescu (8/25) Nov 13 2013 Again there is a confusion here. The idea was to create an object with
- Jacob Carlborg (50/56) Nov 13 2013 No, you need to register the subclasses as well. The static type
- Andrei Alexandrescu (4/9) Nov 13 2013 You didn't have to register for find to work, which was my point.
- Andrei Alexandrescu (22/24) Nov 13 2013 [snip]
- Jacob Carlborg (26/44) Nov 13 2013 No, I have worked on this library for several years. I don't see how it
- Andrei Alexandrescu (3/5) Nov 13 2013 OK that convinced me. My post remains as an explanation of my point.
- Benjamin Thaut (6/11) Nov 13 2013 Would you mind moving this discussion to another thread? Everytime I see...
- Martin Nowak (2/5) Nov 16 2013 Yes please, OT discussions ruin every focus.
- Dmitry Olshansky (14/32) Nov 13 2013 I have to chime in.
- Andrei Alexandrescu (9/40) Nov 13 2013 Of course not. It does give you access to an object with the correct
- Dmitry Olshansky (25/55) Nov 13 2013 Allow me to retort, see below.
- Jacob Carlborg (6/8) Nov 13 2013 Yes, but you use ClassInfo.find and _d_newclass, which is exactly the
- Jacob Carlborg (5/10) Nov 13 2013 From a (de)serialization point of view, I like that it's not necessary
- Jakob Ovrum (57/67) Nov 12 2013 + 1.
- Andrei Alexandrescu (7/29) Nov 12 2013 Stopped reading here, and everybody should. There is a thorough
- Jakob Ovrum (7/46) Nov 12 2013 Sure, it has dependency issues, it needs to know all the types
- Dicebot (6/7) Nov 12 2013 Well, D compile-time reflection allows to define factory that
- Andrei Alexandrescu (6/11) Nov 12 2013 I don't think all classes in a system are discoverable during
- Dicebot (8/13) Nov 12 2013 Sure, this topic was already discussed in __traits(getUnittest)
- Benjamin Thaut (4/8) Nov 10 2013 Addition: It is necessary to export the module info. Because as soon as
- Walter Bright (4/7) Nov 10 2013 Actually, it isn't entirely clear to me why the moduleinfo needs to be e...
- Benjamin Thaut (17/27) Nov 10 2013 Well if the dll initializes all the modules, it has to initialize _all_
- Walter Bright (11/37) Nov 10 2013 Yes, and that's also the way shared library support has been implemented...
- Benjamin Thaut (3/7) Nov 10 2013 Still, how do you want to stop the compiler from referencing the module
- Walter Bright (6/16) Nov 11 2013 The compiler needs to know which modules are coming from a dll.
- Martin Nowak (6/12) Nov 12 2013 That depends on whether using a module might require to link
- Walter Bright (3/14) Nov 14 2013 Right, so it shouldn't need to list it as an imported module.
- Martin Nowak (3/8) Nov 12 2013 Yes, the module infos provide basic runtime reflection which can
- Benjamin Thaut (14/16) Nov 10 2013 who export global variables should be burned at the stake anyway, why
- Walter Bright (13/28) Nov 10 2013 I understand the consistency argument, but I'll also argue that if one r...
- Benjamin Thaut (13/26) Nov 10 2013 You completely ignored my inlining argument. Lets assume there is some
- Walter Bright (11/20) Nov 10 2013 The compiler would benefit from knowing which modules are from a shared ...
- Benjamin Thaut (17/30) Nov 10 2013 Then you didn't understand the DIP correctly. One of the main points of
- Walter Bright (4/37) Nov 11 2013 I do understand that. I just have strong doubts about whether that is th...
- Benjamin Thaut (2/5) Nov 11 2013 So we throw 90% of the DIP away and you do a counter proposal?
- Martin Nowak (7/9) Nov 12 2013 We made a slight adjustment on how exported data symbols should
- Martin Nowak (5/8) Nov 12 2013 We didn't found any reasonably simple solution to pass this
- Walter Bright (5/13) Nov 14 2013 One possibility is modules listed on the command line are regarded as
- Benjamin Thaut (7/13) Nov 14 2013 That doesns't work for the case where a dll "A" uses a dll "B".
- Walter Bright (3/15) Nov 14 2013 I don't follow. If you're compiling A, you're specifying A modules on th...
- Benjamin Thaut (9/26) Nov 15 2013 Ok now I understand what you suggest. So basically you want to do the
- Walter Bright (7/33) Nov 17 2013 I thought this did cover it. "export" in imported modules that are not l...
- Benjamin Thaut (11/14) Nov 14 2013 Also our suggested approach would not have this downside.
- deadalnix (7/22) Nov 14 2013 This thread have reached some end.
- Benjamin Thaut (23/43) Nov 14 2013 No, linkers can not get of this indirection. Only whole program
- deadalnix (2/2) Nov 14 2013 OK, I missed the point where everything started back again. This
- Walter Bright (7/19) Nov 14 2013 It's not that bad. Phobos can be built by specifying all the files on th...
- Daniel Murphy (7/12) Nov 15 2013 The symbol in the import library just translates to an import table
- Walter Bright (3/13) Nov 15 2013 Yes, meaning the compiler doesn't have to do it if the import library is...
- Daniel Murphy (3/19) Nov 15 2013 Right, I was saying the indirection still exists.
- Walter Bright (3/24) Nov 17 2013 No, not really, at least not on Windows. Try calling a function in the W...
- Daniel Murphy (25/31) Nov 17 2013 You sure about that?
- Walter Bright (10/42) Nov 18 2013 Try this:
- Daniel Murphy (17/74) Nov 18 2013 So now we get:
- Benjamin Thaut (10/17) Nov 15 2013 I know that. And we are using that fact in DIP 45. For data symbols we
- Walter Bright (3/19) Nov 17 2013 I'm very much against the suggested rewriting of obj files to strip the ...
- Benjamin Thaut (7/32) Nov 20 2013 That can easily be avoided by suppling a compiler switch which either
- Martin Nowak (5/7) Dec 01 2013 That is only one particular aspect of the DIP. It's not essential and
- Manu (22/31) Dec 01 2013 Sorry, I'm just casually following this thread. I'm very interested to h...
- Rainer Schuetze (15/24) Dec 02 2013 For function imports, not using dllimport magically works, but adding it...
- Martin Nowak (3/5) Dec 01 2013 Changed
- Martin Nowak (9/11) Nov 16 2013 That's the essential trade-off we have to make.
- Walter Bright (3/15) Nov 17 2013 That's a pretty big issue (to me, anyway).
- Martin Nowak (10/11) Dec 01 2013 It's not the time argument, but correctly listing all modules that go
- Martin Nowak (3/5) Nov 16 2013 We've considered this and it doesn't work for separate compilation.
- Martin Nowak (4/7) Nov 12 2013 That's not an issue, you can't inline exported functions.
- Martin Nowak (4/7) Nov 16 2013 One might chose to ignore this possibly incorrect behaviour and still
- Martin Nowak (4/15) Nov 12 2013 Yes, data interfaces (be it TLS or __gshared) are bad and make it
- Rainer Schuetze (21/43) Nov 12 2013 I think the DIP is very reasonable. Some random comments:
- Benjamin Thaut (10/16) Nov 13 2013 That is a very good point I also thought about. But because I'm not
- Rainer Schuetze (33/49) Nov 13 2013 As far as I understand, the optimization avoids generating code for
- Benjamin Thaut (17/39) Nov 14 2013 I actually like this idea. Everytime I actually had to dllexport /
- Rainer Schuetze (30/78) Nov 14 2013 According to the rule above "export" on the template definition has no
- Benjamin Thaut (11/18) Nov 14 2013 I don't think this is a good idea. It should be possible to put
- Rainer Schuetze (12/32) Nov 15 2013 I don't follow. What does this have to do with template instances?
- Benjamin Thaut (3/6) Nov 15 2013 Then I misunderstood your use of the word "declaration". With
- Rainer Schuetze (11/20) Nov 15 2013 I actually meant declaration inside template definitions as in your exam...
- Benjamin Thaut (3/14) Nov 15 2013 Sounds good to me. It gives the ability to percisely select what gets
Attached you will find a e-mail conversation about the discussion of DIP 45. I moved the discussion here so everyone can contribute. Hi, DIP 45 is waiting for some offical approval / comment so it can be implemented. It would be great if one of you could look into the topic and give official approval or improvement suggestions as the DIP has been in limbo for quite some time now. I'm not willing to start working on it before there is official approval, because I made bad experiences with this in the past. In my opinion the DIP is in a state where further discussion will not lead to any improvements. The DIP should be implemented to see which problems arrise during implementation to then discuss how to solve them. I've also seen a couple of questions to the state of Windows DLL support and shared libraries on the newsgroup. A working export keyword is required to actually implement that support on windows. The DIP: http://wiki.dlang.org/DIP45 The latest discussion on the NG: http://forum.dlang.org/post/kvhu2c$2ikq$1 digitalmars.com Bug associated with the problem: http://d.puremagic.com/issues/show_bug.cgi?id=9816 Kind Regards Benjamin Thaut
Nov 10 2013
Am 03.11.2013 20:32, schrieb Andrei Alexandrescu:This looks good to me and solidly motivated and anchored in practicalexperience. I approve with the mention that I'm not an expert in the matters involved so for the most part I trust the authors with the details.Walter? Andrei
Nov 10 2013
Am 05.11.2013 00:32, schrieb Martin Nowak:On 11/03/2013 08:32 PM, Andrei Alexandrescu wrote:practical experience. I approve with the mention that I'm not an expert in the matters involved so for the most part I trust the authors with the details.This looks good to me and solidly motivated and anchored infor Windows so thatWalter?I think most of the DIP is sane. We came up with some complicationsexport can replace both __declspec(dllimport) and__declspec(dllexport). I'd like to hearwhat Walter thinks about this. Otherwise I agree with Benjamin that it's mainly an issue ofimplementation now.
Nov 10 2013
Am 08.11.2013 20:32, schrieb Walter Bright:It looks pretty good, except I have serious reservations about the-lib switch proposed behavior:1. It's too blunt. A user could conceivably want to export somesymbols and not others. This is all or nothing.2. It requires the compiler to rewrite the .obj files, even ones thatdmd did not create. I would consider this very surprising behavior. The user will be "WTF the object files in the lib are different from on my disk! D sux!" -lib is a simple-minded creature that currently does exactly what one would expect it to.3. I've never heard anyone complain about the issue of a DLLreferencing another DLL and the combined list of exports being the concatenation of both. It can be annoying, but it would be a minor issue. If it really is one, we can write a separate simple tool that strips exports from object files.And: Exporting of TLS variables is a minor issue. Frankly, I think peoplewho export global variables should be burned at the stake anyway, why should we make it even easier? :-) Anyhow, if the user really, really wants to get himself immolated and export a TLS variable, he can always manually write the thunk (it is, after all, trivial).(Perhaps we could take this one step further - export doesn't applyto global variables, either, unless they are extern(C)?)I do like the idea that this proposal enables better code performanceon Linux - better than C!
Nov 10 2013
Am 08.11.2013 20:32, schrieb Walter Bright:It looks pretty good, except I have serious reservations about the-lib switch proposed behavior: I'm glad you like the proposal.1. It's too blunt. A user could conceivably want to export somesymbols and not others. This is all or nothing. A user is already able to control which symbols are exported and which are not by using the "export" attribute. Talking about expectations, when you come from C/C++ you usually expect that static libraries do _not_ contain any dll-exported symbols. At least I have never seen a single C or C++ static library that does contain dll-exported symbols. The -lib switch just deactivates dll-exporting symbols. I would also be fine with adding a additional compiler switch "--disable-export" or something like that. But I would strongly prefer a solution where I don't have to run additional tools on the generted object files to get rid of dll-exported symbols I didn't want in the first place, because I was compiling my code for a static library.2. It requires the compiler to rewrite the .obj files, even ones thatdmd did not create. I would consider this very surprising behavior. The user will be "WTF the object files in the lib are different from on my disk! D sux!" -lib is a simple-minded creature that currently does exactly what one would expect it to. I can agree with that. The consensus in the newsgroup dicussion was though, that this is better then adding another compiler switch for disabeling dll-exported symbols, because that compiler switch would need to be added manually, whereas the -lib solution would work "magically". So the -lib solution whas thought to have better usability.3. I've never heard anyone complain about the issue of a DLLreferencing another DLL and the combined list of exports being the concatenation of both. It can be annoying, but it would be a minor issue. If it really is one, we can write a separate simple tool that strips exports from object files. I don't think you understood the issue correctly. The issue is that you don't want to have dll-exported symbols from a static library inside a DLL. Static libraries are usually expected to not have any dll-exported smybols at all. For example when creating a DLL and you link against phobos you will always have all symbols from phobos & druntime inside your DLL, if we follow your suggested behaviour. This not only will lead to linker errors if you use two dlls that both link against phobos statically. But it will also make debugging and inspecting dll-exports harder as the dll will always have tons of exports you have to filter out.And: Exporting of TLS variables is a minor issue. Frankly, I think peoplewho export global variables should be burned at the stake anyway, why should we make it even easier? Anyhow, if the user really, really wants to get himself immolated and export a TLS variable, he can always manually write the thunk (it is, after all, trivial). I don't agree with that. It should be possible to create shared libraries which use all the capbailites of the language. And not restirct them to a arbitrary subset. In D TLS is a special issue because so many variables end up in TLS automatically. I'm especially thinking about inlining of small methods across dll boundaries. Also a shared library should really work the same way as a static one. Requiering a different design for a static versus a shared library seems inconsistent to me.(Perhaps we could take this one step further - export doesn't applyto global variables, either, unless they are extern(C)?)I do like the idea that this proposal enables better code performanceon Linux - better than C!I fully agree here, although this basically happened by accident. I was really astonished when I learned how much mostly useless indirections are added when compiling with -fPIC. Windows Dlls seem to have the better solution here regarding performance. We should really continue this discussion on the newsgroup. If this is ok with you, just copy the e-mail conversation to a new newsgroup thread, or send me a message and I will do so. Kind Regards Benjamin Thaut
Nov 10 2013
Am 09.11.2013 20:26, schrieb Martin Nowak:On 11/09/2013 08:11 PM, code benjamin-thaut.de wrote:IIRC the main problem we're trying to solve is the following. If a static library contains object files with exported symbols those will be reexported from an exe/dll that links against the static library. Linking a static library into a dll is rare enough to justify a separate tool, but the exe case is problematic.2. It requires the compiler to rewrite the .obj files, even ones that dmd did not create. I would consider this very surprising behavior. The user will be "WTF the object files in the lib are different from on my disk! D sux!" -lib is a simple-minded creature that currently does exactly what one would expect it to.I can agree with that. The consensus in the newsgroup dicussion was though, that this is better then adding another compiler switch for disabeling dll-exported symbols, because that compiler switch would need to be added manually, whereas the -lib solution would work "magically". So the -lib solution whas thought to have better usability.
Nov 10 2013
On 11/10/2013 12:42 PM, Benjamin Thaut wrote:Am 08.11.2013 20:32, schrieb Walter Bright: > It looks pretty good, except I have serious reservations about the -lib switch proposed behavior: I'm glad you like the proposal. > > 1. It's too blunt. A user could conceivably want to export some symbols and not others. This is all or nothing. A user is already able to control which symbols are exported and which are not by using the "export" attribute. ...Using the export attribute on some member exports the entire module info. If I understand this right, the module info will contain information also about non-exported members of the module and therefore even non-exported members will be accessible from outside. Is this correct?
Nov 10 2013
Am 10.11.2013 13:03, schrieb Timon Gehr:On 11/10/2013 12:42 PM, Benjamin Thaut wrote:AFAIK the module info only contains information about the (shared) module constructors and destructors. And no, non exported symbols will not be acessible because the neccessary accessors are not generated for them (at least on windows).Am 08.11.2013 20:32, schrieb Walter Bright: > It looks pretty good, except I have serious reservations about the -lib switch proposed behavior: I'm glad you like the proposal. > > 1. It's too blunt. A user could conceivably want to export some symbols and not others. This is all or nothing. A user is already able to control which symbols are exported and which are not by using the "export" attribute. ...Using the export attribute on some member exports the entire module info. If I understand this right, the module info will contain information also about non-exported members of the module and therefore even non-exported members will be accessible from outside. Is this correct?
Nov 10 2013
On 11/10/2013 01:06 PM, Benjamin Thaut wrote:Am 10.11.2013 13:03, schrieb Timon Gehr:What about Object.factory?On 11/10/2013 12:42 PM, Benjamin Thaut wrote:AFAIK the module info only contains information about the (shared) module constructors and destructors. And no, non exported symbols will not be acessible because the neccessary accessors are not generated for them (at least on windows).Am 08.11.2013 20:32, schrieb Walter Bright: > It looks pretty good, except I have serious reservations about the -lib switch proposed behavior: I'm glad you like the proposal. > > 1. It's too blunt. A user could conceivably want to export some symbols and not others. This is all or nothing. A user is already able to control which symbols are exported and which are not by using the "export" attribute. ...Using the export attribute on some member exports the entire module info. If I understand this right, the module info will contain information also about non-exported members of the module and therefore even non-exported members will be accessible from outside. Is this correct?
Nov 10 2013
Am 10.11.2013 13:39, schrieb Timon Gehr:On 11/10/2013 01:06 PM, Benjamin Thaut wrote:That would obviously work. But why would you want to restrict that for shared libraries? Its not restricted for static libraries either. And esentially a shared library should behave exactly the same as a static one.Am 10.11.2013 13:03, schrieb Timon Gehr:What about Object.factory?On 11/10/2013 12:42 PM, Benjamin Thaut wrote:AFAIK the module info only contains information about the (shared) module constructors and destructors. And no, non exported symbols will not be acessible because the neccessary accessors are not generated for them (at least on windows).Am 08.11.2013 20:32, schrieb Walter Bright: > It looks pretty good, except I have serious reservations about the -lib switch proposed behavior: I'm glad you like the proposal. > > 1. It's too blunt. A user could conceivably want to export some symbols and not others. This is all or nothing. A user is already able to control which symbols are exported and which are not by using the "export" attribute. ...Using the export attribute on some member exports the entire module info. If I understand this right, the module info will contain information also about non-exported members of the module and therefore even non-exported members will be accessible from outside. Is this correct?
Nov 10 2013
On Sunday, 10 November 2013 at 12:39:35 UTC, Timon Gehr wrote:What about Object.factory?I always though of it as a misfeature.
Nov 11 2013
On 11/11/2013 11:02 PM, deadalnix wrote:On Sunday, 10 November 2013 at 12:39:35 UTC, Timon Gehr wrote:Me too.What about Object.factory?I always though of it as a misfeature.
Nov 11 2013
On 11/11/13 2:36 PM, Timon Gehr wrote:On 11/11/2013 11:02 PM, deadalnix wrote:... well time to substantiate. AndreiOn Sunday, 10 November 2013 at 12:39:35 UTC, Timon Gehr wrote:Me too.What about Object.factory?I always though of it as a misfeature.
Nov 11 2013
On Tuesday, 12 November 2013 at 05:46:19 UTC, Andrei Alexandrescu wrote:On 11/11/13 2:36 PM, Timon Gehr wrote:OK, but first, it has to be noted that this is putting the burden of proof on the wrong side of the argument. So I ran `grep -r * -e 'Object.factory'` on my d folder. I have various D project there. The only uses of Object.factory are the test suite of dmd, ldc and GDC. I have bunch of D code in there, not simply mine. So first thing : it is unused. Secondly, the only useful things you can do with that require to upcast (and break basic OOP principle, which is kind of ironic for a feature that produce objects). The design of the feature is unsound. It is not that surprising as different possible uses case are better solved with compile time reflection and metaprograming. Implementationwise, this feature require to pull into the application some unoptimizable bloat. It is also a tedious constraint for shared objects as we see in this thread.On 11/11/2013 11:02 PM, deadalnix wrote:... well time to substantiate. AndreiOn Sunday, 10 November 2013 at 12:39:35 UTC, Timon Gehr wrote:Me too.What about Object.factory?I always though of it as a misfeature.
Nov 11 2013
On 11/11/13 10:59 PM, deadalnix wrote:On Tuesday, 12 November 2013 at 05:46:19 UTC, Andrei Alexandrescu wrote:Well I'm not "burdening", just trying to figure how to do it better.On 11/11/13 2:36 PM, Timon Gehr wrote:OK, but first, it has to be noted that this is putting the burden of proof on the wrong side of the argument.On 11/11/2013 11:02 PM, deadalnix wrote:... well time to substantiate. AndreiOn Sunday, 10 November 2013 at 12:39:35 UTC, Timon Gehr wrote:Me too.What about Object.factory?I always though of it as a misfeature.So I ran `grep -r * -e 'Object.factory'` on my d folder. I have various D project there. The only uses of Object.factory are the test suite of dmd, ldc and GDC.That really proves nothing.I have bunch of D code in there, not simply mine. So first thing : it is unused. Secondly, the only useful things you can do with that require to upcast (and break basic OOP principle, which is kind of ironic for a feature that produce objects). The design of the feature is unsound.I think you're wrong here (even after one discounts for s/upcast/downcast/). There's no breakage of any basic OOP principle. There's no irony. Polymorphic creation aka the factory pattern is a classic in object-oriented design. You'd have a bad time arguing against it with anyone.It is not that surprising as different possible uses case are better solved with compile time reflection and metaprograming.These don't take care of the dynamic case, which is sometimes needed (wherever factories are useful: serialization, scripting, ...).Implementationwise, this feature require to pull into the application some unoptimizable bloat.This is a good point. But I presume the dedicated code is small in size.It is also a tedious constraint for shared objects as we see in this thread.My reading of the thread is that there are concerns about DIP 45 beyond support of factories for shared objects. Andrei
Nov 11 2013
On Tuesday, 12 November 2013 at 07:16:03 UTC, Andrei Alexandrescu wrote:That really proves nothing.That doesn't prove anything. But that doesn't smell good for the usefulness of the feature either.A Factory is always a factory of something. A factory fit nicely into the OOP paradigm as it allow the user of the object to not know what is the actual type of the object, but its interface. A factory is also in charge of all the work necessary to put the object in a useful state. Object.factory do not allow any of this. It doesn't make any sense to provide a factory of everything. The factory user will have to know what is built, at least partially to use it. This defeat the whole principle of a factory.I have bunch of D code in there, not simply mine. So first thing : it is unused. Secondly, the only useful things you can do with that require to upcast (and break basic OOP principle, which is kind of ironic for a feature that produce objects). The design of the feature is unsound.I think you're wrong here (even after one discounts for s/upcast/downcast/). There's no breakage of any basic OOP principle. There's no irony. Polymorphic creation aka the factory pattern is a classic in object-oriented design. You'd have a bad time arguing against it with anyone.It is actually quite easy to create runtime capabilities from these basic blocs. CTFE allow to create AA, and at this point, the user can get whatever is desired at runtime. This feature has more to do with runtime reflection than OOP.It is not that surprising as different possible uses case are better solved with compile time reflection and metaprograming.These don't take care of the dynamic case, which is sometimes needed (wherever factories are useful: serialization, scripting, ...).The code in itself isn't really the problem. The compiler have to emit a bunch of wiring to make it work. Wiring which is almost never used. Basically, the compiler have to emit runtime reflection informations, but just for classes. This seems to be the wrong tradeoff : we get the constraint of runtime reflection in term of design, but with very limited benefices.Implementationwise, this feature require to pull into the application some unoptimizable bloat.This is a good point. But I presume the dedicated code is small in size.This is A concern. Surely not the only one. That is why I didn't expanded myself on the subject in the first place. Simply that if we had to choose between this DIP and Object.factory, it is a braindead choice.It is also a tedious constraint for shared objects as we see in this thread.My reading of the thread is that there are concerns about DIP 45 beyond support of factories for shared objects.
Nov 11 2013
On 11/11/13 11:58 PM, deadalnix wrote:On Tuesday, 12 November 2013 at 07:16:03 UTC, Andrei Alexandrescu wrote:My code doesn't use that, either. But it also doesn't use things like .toString for classes. It's just that I don't do much OOP in D - doesn't mean OOP and/or factory etc are bankrupt.That really proves nothing.That doesn't prove anything. But that doesn't smell good for the usefulness of the feature either.Yes. Generalized, it stands to reason that Object (as the most inclusive class type) could plausibly have a factory.I think you're wrong here (even after one discounts for s/upcast/downcast/). There's no breakage of any basic OOP principle. There's no irony. Polymorphic creation aka the factory pattern is a classic in object-oriented design. You'd have a bad time arguing against it with anyone.A Factory is always a factory of something. A factory fit nicely into the OOP paradigm as it allow the user of the object to not know what is the actual type of the object, but its interface.A factory is also in charge of all the work necessary to put the object in a useful state. Object.factory do not allow any of this.But default class constructors can be elaborate, so one can assume the default constructor puts the object in a useful state. Granted, we could have added constructors with e.g. Variant parameters. But that would be just an enhancement.It doesn't make any sense to provide a factory of everything. The factory user will have to know what is built, at least partially to use it. This defeat the whole principle of a factory.I don't see any defeating here. I do see a desire to specialize factories for specific subhierarchies, which is entirely fine. I don't see how that doesn't make a factory dealing in Object bad.What's missing is a list of all class types in the system. This is a fundamental issue with factories.It is actually quite easy to create runtime capabilities from these basic blocs. CTFE allow to create AA, and at this point, the user can get whatever is desired at runtime.It is not that surprising as different possible uses case are better solved with compile time reflection and metaprograming.These don't take care of the dynamic case, which is sometimes needed (wherever factories are useful: serialization, scripting, ...).This feature has more to do with runtime reflection than OOP.Implementation-wise, yes. Conceptually, people often see them as virtual constructors (i.e. an OOP notion extended to construction).One question would be whether the same work is needed for e.g. typeid() or toString. If not, maybe there is a case against factory.The code in itself isn't really the problem. The compiler have to emit a bunch of wiring to make it work. Wiring which is almost never used. Basically, the compiler have to emit runtime reflection informations, but just for classes.Implementationwise, this feature require to pull into the application some unoptimizable bloat.This is a good point. But I presume the dedicated code is small in size.This seems to be the wrong tradeoff : we get the constraint of runtime reflection in term of design, but with very limited benefices.I don't see how the existing system prevents you from rolling your own.The time difference is crucial here. If this DIP came up in 2006 (when I estimate we introduced Object.factory) probably a lot of things would have come down differently. Proposals made in different epochs are, I think, very difficult to compare. AndreiThis is A concern. Surely not the only one. That is why I didn't expanded myself on the subject in the first place. Simply that if we had to choose between this DIP and Object.factory, it is a braindead choice.It is also a tedious constraint for shared objects as we see in this thread.My reading of the thread is that there are concerns about DIP 45 beyond support of factories for shared objects.
Nov 12 2013
Am 12.11.2013 09:50, schrieb Andrei Alexandrescu:The time difference is crucial here. If this DIP came up in 2006 (when I estimate we introduced Object.factory) probably a lot of things would have come down differently. Proposals made in different epochs are, I think, very difficult to compare. AndreiWait. Whats the actual issue here? This DIP does not prevent object.factory from working across DLL boundaries. This DIP does also not conflict with object.factory. So what is the actual problem at hand?
Nov 12 2013
On 11/12/13 1:30 AM, Benjamin Thaut wrote:Am 12.11.2013 09:50, schrieb Andrei Alexandrescu:You're right. I was meaning that in the abstract - generally feature creation is strongly conditioned by existing context. AndreiThe time difference is crucial here. If this DIP came up in 2006 (when I estimate we introduced Object.factory) probably a lot of things would have come down differently. Proposals made in different epochs are, I think, very difficult to compare. AndreiWait. Whats the actual issue here? This DIP does not prevent object.factory from working across DLL boundaries. This DIP does also not conflict with object.factory. So what is the actual problem at hand?
Nov 12 2013
On Tuesday, 12 November 2013 at 08:50:29 UTC, Andrei Alexandrescu wrote:One question would be whether the same work is needed for e.g. typeid() or toString. If not, maybe there is a case against factory.That isn't the same. toString itself rely on typeid. And the typeid object is stored with the virtual function table. The code can retrieve that information for any object, even the ones that the code never heard about. Object.factory require the inverse: finding the typeid from the name of the class. It require to build some structure that associate the name of a class and its typeid, and make it available for Object.factory.I don't see how the existing system prevents you from rolling your own.It doesn't, but I still need to pay for it.The time difference is crucial here. If this DIP came up in 2006 (when I estimate we introduced Object.factory) probably a lot of things would have come down differently. Proposals made in different epochs are, I think, very difficult to compare.I understand some design has been made for historical reasons. That happen to every system. That doesn't mean much.
Nov 12 2013
On 2013-11-12 07:59, deadalnix wrote:OK, but first, it has to be noted that this is putting the burden of proof on the wrong side of the argument. So I ran `grep -r * -e 'Object.factory'` on my d folder. I have various D project there. The only uses of Object.factory are the test suite of dmd, ldc and GDC. I have bunch of D code in there, not simply mine. So first thing : it is unused.I used it in my serialization library, Orange, and it will be used in std.serialization when I finish that project. -- /Jacob Carlborg
Nov 11 2013
On 2013-11-12 08:52, Jacob Carlborg wrote:I used it in my serialization library, Orange, and it will be used in std.serialization when I finish that project.Well, technically I'm using ClassInfo.find. -- /Jacob Carlborg
Nov 12 2013
Am 12.11.2013 07:59, schrieb deadalnix:On Tuesday, 12 November 2013 at 05:46:19 UTC, Andrei Alexandrescu wrote:But what is the actual argument? What problem do you see regarding object.factory and this DIP?... well time to substantiate. AndreiOK, but first, it has to be noted that this is putting the burden of proof on the wrong side of the argument.
Nov 12 2013
On 11/12/2013 06:46 AM, Andrei Alexandrescu wrote:On 11/11/13 2:36 PM, Timon Gehr wrote:- Every class in any imported module will need to show up in the generated code even if it is never used unless global static analysis is carried out on arguments to Object.factory. - If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor. - The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.On 11/11/2013 11:02 PM, deadalnix wrote:... well time to substantiate. AndreiOn Sunday, 10 November 2013 at 12:39:35 UTC, Timon Gehr wrote:Me too.What about Object.factory?I always though of it as a misfeature.
Nov 12 2013
On 11/12/13 2:36 PM, Timon Gehr wrote:On 11/12/2013 06:46 AM, Andrei Alexandrescu wrote:Correct. On the other hand, a lot of unused classes in used modules seems to indicate a problem with the system's design.On 11/11/13 2:36 PM, Timon Gehr wrote:- Every class in any imported module will need to show up in the generated code even if it is never used unless global static analysis is carried out on arguments to Object.factory.On 11/11/2013 11:02 PM, deadalnix wrote:... well time to substantiate. AndreiOn Sunday, 10 November 2013 at 12:39:35 UTC, Timon Gehr wrote:Me too.What about Object.factory?I always though of it as a misfeature.- If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor.What are the better ways? Note that most of the time you don't "know" the name of a class - you get it down the wire during some deserialization. So there must be some way to build an object from a token representation of the object.- The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.It's not quite trivial - somewhere there has to be a map and registration and lookup and whatnot. I don't see it why it's unbecoming for such functionality to be part of the standard library. I would agree, however, that it's a judgment call whether it should be wired in or provided on demand. Andrei
Nov 12 2013
On Tuesday, 12 November 2013 at 22:45:15 UTC, Andrei Alexandrescu wrote:What are the better ways? Note that most of the time you don't "know" the name of a class - you get it down the wire during some deserialization. So there must be some way to build an object from a token representation of the object.The serialization is a good example. You'll have to note that if the code has been able to serialize the data, it can generate at compile time the necessary scafolding to deserialize it.That s why this is a problem to add all that is the core of the language when it isn't used often.- The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.It's not quite trivial - somewhere there has to be a map and registration and lookup and whatnot. I don't see it why it's unbecoming for such functionality to be part of the standard library. I would agree, however, that it's a judgment call whether it should be wired in or provided on demand.
Nov 12 2013
On 11/12/13 4:36 PM, deadalnix wrote:On Tuesday, 12 November 2013 at 22:45:15 UTC, Andrei Alexandrescu wrote:I think there is some confusion here. We're talking about polymorphic creation, i.e. an object comes down the wire and you don't know its type a priori. Again: grab MC++D and read the chapter on Factory, it clarifies all such stuff.What are the better ways? Note that most of the time you don't "know" the name of a class - you get it down the wire during some deserialization. So there must be some way to build an object from a token representation of the object.The serialization is a good example. You'll have to note that if the code has been able to serialize the data, it can generate at compile time the necessary scafolding to deserialize it.That's a good point. I would tend to agree with that. But it doesn't make it a misfeature. It's a sensible feature. AndreiIt's not quite trivial - somewhere there has to be a map and registration and lookup and whatnot. I don't see it why it's unbecoming for such functionality to be part of the standard library. I would agree, however, that it's a judgment call whether it should be wired in or provided on demand.That s why this is a problem to add all that is the core of the language when it isn't used often.
Nov 12 2013
On 2013-11-13 01:36, deadalnix wrote:The serialization is a good example. You'll have to note that if the code has been able to serialize the data, it can generate at compile time the necessary scafolding to deserialize it.No, not necessarily. If you serialize an object through a base class reference you currently need to register that with the serializer. The static type information is lost and D doesn't provide enough runtime reflection to get the values of instance variables of an object at runtime. -- /Jacob Carlborg
Nov 13 2013
On Wednesday, 13 November 2013 at 08:48:09 UTC, Jacob Carlborg wrote:On 2013-11-13 01:36, deadalnix wrote:OK, I see your problem here. It is a much more general problem than the one solved by the factory. For instance, I wanted in the past to write some unittest that ensure Liskov's substitution principle (so run the unittest on subclasses). This is fundamentally the same issue : you want to write some code that is aware of subclasses. That require somehow to be able to mixin something automatically in all subclasses. Object.factory only solve poorly one instance of this problem.The serialization is a good example. You'll have to note that if the code has been able to serialize the data, it can generate at compile time the necessary scafolding to deserialize it.No, not necessarily. If you serialize an object through a base class reference you currently need to register that with the serializer. The static type information is lost and D doesn't provide enough runtime reflection to get the values of instance variables of an object at runtime.
Nov 13 2013
On 2013-11-13 23:25, deadalnix wrote:OK, I see your problem here. It is a much more general problem than the one solved by the factory. For instance, I wanted in the past to write some unittest that ensure Liskov's substitution principle (so run the unittest on subclasses). This is fundamentally the same issue : you want to write some code that is aware of subclasses. That require somehow to be able to mixin something automatically in all subclasses.Might be solveable with AST macros :). Although you can still have problems with subclasses you don't know of during compile time. Doing separate compilation, libraries and so on.Object.factory only solve poorly one instance of this problem.-- /Jacob Carlborg
Nov 13 2013
On 11/12/2013 11:45 PM, Andrei Alexandrescu wrote:It is not necessary that there are a lot. Anything transitively reachable from there will be pulled in.- Every class in any imported module will need to show up in the generated code even if it is never used unless global static analysis is carried out on arguments to Object.factory.Correct. On the other hand, a lot of unused classesin used modules seems to indicate a problem with the system's design.Those could be classes that are only used at compile time, like a builder for a grammar for a CTFE parser generator, for lack of a better example. Furthermore, library modules may be larger than their actively used interface. (Phobos?) It would be interesting to see how much Object.factory influences the size of hello world, even though Phobos does not use too many classesCall the default constructor. Call a delegate/virtual function that calls the default constructor.- If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor.What are the better ways?Note that most of the time you don't "know" the name of a class - you get it down the wire during some deserialization.You don't want to call the default constructor during deserialization and you need actual information about the Object layout, so seems to be an instance of the next point.So there must be some way to build an object from a token representation of the object.I think that if supported at all, it should be an UDA declared somewhere in druntime.- The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.It's not quite trivial - somewhere there has to be a map and registration and lookup and whatnot. I don't see it why it's unbecoming for such functionality to be part of the standard library. I would agree, however, that it's a judgment call whether it should be wired in or provided on demand.
Nov 12 2013
On 11/12/13 6:03 PM, Timon Gehr wrote:On 11/12/2013 11:45 PM, Andrei Alexandrescu wrote:Fair point.It is not necessary that there are a lot. Anything transitively reachable from there will be pulled in.- Every class in any imported module will need to show up in the generated code even if it is never used unless global static analysis is carried out on arguments to Object.factory.Correct. On the other hand, a lot of unused classesin used modules seems to indicate a problem with the system's design.Those could be classes that are only used at compile time, like a builder for a grammar for a CTFE parser generator, for lack of a better example. Furthermore, library modules may be larger than their actively used interface. (Phobos?) It would be interesting to see how much Object.factory influences the size of hello world, even though Phobos does not use too many classesWait, doesn't Object.factory call the default constructor of the created object?Call the default constructor. Call a delegate/virtual function that calls the default constructor.- If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor.What are the better ways?If not the default constructor, which constructor? Didn't you say above that calling the default constructor is good? I feel we're talking past each other.Note that most of the time you don't "know" the name of a class - you get it down the wire during some deserialization.You don't want to call the default constructor during deserialization and you need actual information about the Object layout, so seems to be an instance of the next point.That would be indeed nice. But I have the feeling a significant part of this dialog is marred by confusion. AndreiSo there must be some way to build an object from a token representation of the object.I think that if supported at all, it should be an UDA declared somewhere in druntime.- The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.It's not quite trivial - somewhere there has to be a map and registration and lookup and whatnot. I don't see it why it's unbecoming for such functionality to be part of the standard library. I would agree, however, that it's a judgment call whether it should be wired in or provided on demand.
Nov 12 2013
On 11/13/2013 03:15 AM, Andrei Alexandrescu wrote:...Yes, I was listing some ways to do this that I think are better than calling Object.factory.Wait, doesn't Object.factory call the default constructor of the created object? ...Call the default constructor. Call a delegate/virtual function that calls the default constructor.- If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor.What are the better ways?None, I guess you'd want to create the right object state in memory.If not the default constructor, which constructor?Note that most of the time you don't "know" the name of a class - you get it down the wire during some deserialization.You don't want to call the default constructor during deserialization and you need actual information about the Object layout, so seems to be an instance of the next point.Didn't you say above that calling the default constructor is good?If that is what you want to do, yes.I feel we're talking past each other.Maybe.
Nov 12 2013
On 11/12/13 6:26 PM, Timon Gehr wrote:On 11/13/2013 03:15 AM, Andrei Alexandrescu wrote:OK I think I figure you are confused. You can't call the default constructor of an object you don't know the type of. Andrei...Yes, I was listing some ways to do this that I think are better than calling Object.factory.Wait, doesn't Object.factory call the default constructor of the created object? ...Call the default constructor. Call a delegate/virtual function that calls the default constructor.- If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor.What are the better ways?
Nov 12 2013
On 11/13/2013 03:48 AM, Andrei Alexandrescu wrote:On 11/12/13 6:26 PM, Timon Gehr wrote:I'm not.On 11/13/2013 03:15 AM, Andrei Alexandrescu wrote:OK I think I figure you are confused....Yes, I was listing some ways to do this that I think are better than calling Object.factory.Wait, doesn't Object.factory call the default constructor of the created object? ...Call the default constructor. Call a delegate/virtual function that calls the default constructor.- If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor.What are the better ways?You can't call the default constructor of an object you don't know the type of. AndreiIndeed. There was a premise stating that I do. :o) In any case, the only use for string -> new object is when there is some kind of parsing going on (and then you should make sure in some way that only objects of types in some well-defined subset can be constructed), otherwise you can always pass around richer data structures, such as an actual factory object.
Nov 12 2013
On 11/12/13 7:05 PM, Timon Gehr wrote:On 11/13/2013 03:48 AM, Andrei Alexandrescu wrote:Then how do you figure doing this: class Streamable { ... } class Foo : Streamable { ... } class Bar : Streamable { ... } string className = stream.readln(); Streamable obj = ...; How do you create obj from className, when className could be either "Foo" or "Bar"? In the general case there could be any number of classes, in different modules.On 11/12/13 6:26 PM, Timon Gehr wrote:I'm not.On 11/13/2013 03:15 AM, Andrei Alexandrescu wrote:OK I think I figure you are confused....Yes, I was listing some ways to do this that I think are better than calling Object.factory.Wait, doesn't Object.factory call the default constructor of the created object? ...Call the default constructor. Call a delegate/virtual function that calls the default constructor.- If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor.What are the better ways?So you say Object.factory is a misfeature based on a use case that solves a very different problem?You can't call the default constructor of an object you don't know the type of. AndreiIndeed. There was a premise stating that I do. :o)In any case, the only use for string -> new object is when there is some kind of parsing going on (and then you should make sure in some way that only objects of types in some well-defined subset can be constructed), otherwise you can always pass around richer data structures, such as an actual factory object.No parsing needs to be involved, as in e.g. binary formats. And I'm talking data not code, which excludes passing around a factory object. Andrei
Nov 12 2013
On 2013-11-13 05:07, Andrei Alexandrescu wrote:Then how do you figure doing this: class Streamable { ... } class Foo : Streamable { ... } class Bar : Streamable { ... } string className = stream.readln(); Streamable obj = ...; How do you create obj from className, when className could be either "Foo" or "Bar"? In the general case there could be any number of classes, in different modules.This requires Object.factory (or equivalent) and that all subclasses have been registered as well. Since you don't know which subclass "className" represents you're forced to deserialize by doing this: Streamable obj = deserialize!(Streamable)(className); -- /Jacob Carlborg
Nov 13 2013
On 11/13/13 12:55 AM, Jacob Carlborg wrote:On 2013-11-13 05:07, Andrei Alexandrescu wrote:With Object.factory that's taken care of already.Then how do you figure doing this: class Streamable { ... } class Foo : Streamable { ... } class Bar : Streamable { ... } string className = stream.readln(); Streamable obj = ...; How do you create obj from className, when className could be either "Foo" or "Bar"? In the general case there could be any number of classes, in different modules.This requires Object.factory (or equivalent) and that all subclasses have been registered as well.Since you don't know which subclass "className" represents you're forced to deserialize by doing this: Streamable obj = deserialize!(Streamable)(className);Again there is a confusion here. The idea was to create an object with the dynamic type Foo if the class name was "Foo" and an object with the dynamic type Bar if the class name was "Bar". You are glossing over the gist of it all with a function call. Are you sure we are talking about the same thing? Andrei
Nov 13 2013
On 2013-11-13 10:27, Andrei Alexandrescu wrote:With Object.factory that's taken care of already.No, you need to register the subclasses as well. The static type information is lost for the subclasses.Again there is a confusion here. The idea was to create an object with the dynamic type Foo if the class name was "Foo" and an object with the dynamic type Bar if the class name was "Bar". You are glossing over the gist of it all with a function call. Are you sure we are talking about the same thing?Here is an excerpt of my serialization library, with the relevant code for this discussion: void function (in Object) [ClassInfo] registeredTypes; void register (T : Object) () { registeredTypes[T.classinfo] = &downcastSerialize!(T) } void downcastDeserialize (U : Object) (in Object value) { alias Unqual!(U) T; auto casted = cast(T) value; assert(casted); assert(casted.classinfo is T.classinfo); deserializeHelper(casted); } void deserializeHelper (T) (T value) { ... } bool isBaseClass (T) (T value) { return value.classinfo !is T.classinfo; } T deserialize (T) (string name) { T t; if (auto classInfo = ClassInfo.find(name)) { auto value = cast(T) _d_alloc(classInfo); if (isBaseClass(value)) { if (auto deserializer = value.classinfo in registeredTypes) (*serializer)(value); else throw new Exception("Unregistered subclass"); } else deserializeHelper(value); return value; } else { throw new Exception("The class " ~ name ~ " couldn't be found"); return null; } } For the full code see: https://github.com/jacob-carlborg/orange/blob/master/orange/serialization/Serializer.d -- /Jacob Carlborg
Nov 13 2013
On 11/13/13 2:46 AM, Jacob Carlborg wrote:On 2013-11-13 10:27, Andrei Alexandrescu wrote:We are definitely talking about different things.With Object.factory that's taken care of already.No, you need to register the subclasses as well. The static type information is lost for the subclasses.if (auto classInfo = ClassInfo.find(name))You didn't have to register for find to work, which was my point. Andrei
Nov 13 2013
On 11/13/13 2:46 AM, Jacob Carlborg wrote:Here is an excerpt of my serialization library, with the relevant code for this discussion:[snip] To drive the point home: I looked once more over the code and I think it's too complicated for what it does. It could be improved as follows. The key to simplification is using Object create() const; from ClassInfo (aka Typeinfo_Class) in addition to static const(TypeInfo_Class) find(in char[] classname); in the same type. The procedure would go as follows: 1. Retrieve the ClassInfo object for the class name. 2. Create a default-constructed object by calling create() against the object obtained at (1). 3. Cast that Object down to IDeserializable, which is an interface type that has methods such as deserialize(Archive). 4. If the cast failed, throw an exception - the object was not designed to be deserialized. 5. Otherwise, call deserialize against that object, passing it the archive. Voila! No need for runtime registration - all user code has to do is implement the appropriate interface. I hope this both clarifies my point about factory being a sensible feature, and helps you improve your library. Andrei
Nov 13 2013
On 2013-11-13 17:29, Andrei Alexandrescu wrote:To drive the point home: I looked once more over the code and I think it's too complicated for what it does. It could be improved as follows.No, I have worked on this library for several years. I don't see how it could be less complicated for what it does. It seems you don't understand exactly what it does.The key to simplification is using Object create() const;Requires a default constructor. Not good enough.static const(TypeInfo_Class) find(in char[] classname); in the same type. The procedure would go as follows: 1. Retrieve the ClassInfo object for the class name. 2. Create a default-constructed object by calling create() against the object obtained at (1).As above, requires a default constructor. Not good enough.3. Cast that Object down to IDeserializable, which is an interface type that has methods such as deserialize(Archive).* Requires a class to implement an interface. Not good enough. * Requires the class to manually serialize itself. Not good enough. A serialization library need to work with third party types you don't have any control over.4. If the cast failed, throw an exception - the object was not designed to be deserialized. 5. Otherwise, call deserialize against that object, passing it the archive.As above, requires the class to manually serialize itself. Not good enough.Voila! No need for runtime registration - all user code has to do is implement the appropriate interface. I hope this both clarifies my point about factory being a sensible feature, and helps you improve your library.Unfortunately it doesn't help at all. You clearly missed a lot of features this library has. To be able to automatically (de)serialize an object in D you need to know its static type. That requires registering the type. Note, this is only needed if you're serializing through a base class reference. You'll have to do a lot better than this to convince me. Note also that I'm not against Object.factory/create, I'm arguing for it. Serialization is one example of why it's needed. The biggest reasons for why it's looking like it does are: * Can (de)serialize arbitrary types, even third party types you don't have any control over * It automatically (de)serializes the objects. No need to implement a method. You can do, if you want to customize the (de)serialization -- /Jacob Carlborg
Nov 13 2013
On 11/13/13 11:20 AM, Jacob Carlborg wrote:A serialization library need to work with third party types you don't have any control over.OK that convinced me. My post remains as an explanation of my point. Andrei
Nov 13 2013
Am 13.11.2013 21:36, schrieb Andrei Alexandrescu:On 11/13/13 11:20 AM, Jacob Carlborg wrote:Would you mind moving this discussion to another thread? Everytime I see a update I think its something relevant and usually its just about object.factory. Kind Regards Benjamin ThautA serialization library need to work with third party types you don't have any control over.OK that convinced me. My post remains as an explanation of my point. Andrei
Nov 13 2013
On 11/13/2013 09:45 PM, Benjamin Thaut wrote:Would you mind moving this discussion to another thread? Everytime I see a update I think its something relevant and usually its just about object.factory.Yes please, OT discussions ruin every focus.
Nov 16 2013
13-Nov-2013 13:27, Andrei Alexandrescu пишет:On 11/13/13 12:55 AM, Jacob Carlborg wrote:I have to chime in. Serialization != calling default constructor by name. You have to work out fields somehow - Object.factory doesn't help with that. What serialization needs is some hook - be it delegete or whatnot: Object deserialize(SomeStreamingAbstraction here); Simply snatching a name is not serialization. Not to mention that far too many protocols are not text. Keeping built-in map of class-name --> "create via default constructor" is an awful example of ONE SIZE FITS ALL. It doesn't! Simply on the ground of "solves problem in too narrow scope for a hefty coast to the user" I'd drop it. -- Dmitry OlshanskyOn 2013-11-13 05:07, Andrei Alexandrescu wrote:With Object.factory that's taken care of already.Then how do you figure doing this: class Streamable { ... } class Foo : Streamable { ... } class Bar : Streamable { ... } string className = stream.readln(); Streamable obj = ...; How do you create obj from className, when className could be either "Foo" or "Bar"? In the general case there could be any number of classes, in different modules.This requires Object.factory (or equivalent) and that all subclasses have been registered as well.
Nov 13 2013
On 11/13/13 10:41 AM, Dmitry Olshansky wrote:13-Nov-2013 13:27, Andrei Alexandrescu пишет:Of course not. It does give you access to an object with the correct dynamic type, and interfaces and virtual calls take care of the rest.On 11/13/13 12:55 AM, Jacob Carlborg wrote:I have to chime in. Serialization != calling default constructor by name.On 2013-11-13 05:07, Andrei Alexandrescu wrote:With Object.factory that's taken care of already.Then how do you figure doing this: class Streamable { ... } class Foo : Streamable { ... } class Bar : Streamable { ... } string className = stream.readln(); Streamable obj = ...; How do you create obj from className, when className could be either "Foo" or "Bar"? In the general case there could be any number of classes, in different modules.This requires Object.factory (or equivalent) and that all subclasses have been registered as well.You have to work out fields somehow - Object.factory doesn't help with that. What serialization needs is some hook - be it delegete or whatnot: Object deserialize(SomeStreamingAbstraction here);Or a method.Simply snatching a name is not serialization. Not to mention that far too many protocols are not text.Method.Keeping built-in map of class-name --> "create via default constructor" is an awful example of ONE SIZE FITS ALL. It doesn't!It is: it achieves virtual construction, the only missing piece in a classic OOP scheme. Once you have the object, the rest is smooth sailing.Simply on the ground of "solves problem in too narrow scope for a hefty coast to the user" I'd drop it.It's good. Let's keep it. Andrei
Nov 13 2013
13-Nov-2013 22:46, Andrei Alexandrescu пишет:On 11/13/13 10:41 AM, Dmitry Olshansky wrote:Allow me to retort, see below. BTW the keywords here were: _default_ and _by name_13-Nov-2013 13:27, Andrei Alexandrescu пишет:Of course not. It does give you access to an object with the correct dynamic type, and interfaces and virtual calls take care of the rest.On 11/13/13 12:55 AM, Jacob Carlborg wrote:I have to chime in. Serialization != calling default constructor by name.On 2013-11-13 05:07, Andrei Alexandrescu wrote:With Object.factory that's taken care of already.Then how do you figure doing this: class Streamable { ... } class Foo : Streamable { ... } class Bar : Streamable { ... } string className = stream.readln(); Streamable obj = ...; How do you create obj from className, when className could be either "Foo" or "Bar"? In the general case there could be any number of classes, in different modules.This requires Object.factory (or equivalent) and that all subclasses have been registered as well.Oh, my... We've lost him :o) Okay. Let me iterate on some facts: 1. Serialization may or may not be intrusive. There are many cases out there to externalize serialization (see e.g. boost serialization). 2. Want speed - avoid virtuals. Requiring that serialization be always done via specific virtual call harms performance. 3. Something you seem to dodge - AA lookup by fully qualified name is not fast nor convenient for many serialization backends. 4. Not everything needs to be serialized yet it's registered (and slows down lookups). 5. Forcing a pattern - every class object must be default constructible. Btw what factory does with these not defining this() ? Sadly the reality is the other way around - many interesting objects don't have valid default states. Another point that is not a fact but rather a speculation. I _think_ that the idea of having user pay beforehand for something simply because it may one day need it is going against D ethos. Unlike Java or .NET we have definite trend of catering for specific needs and extreme flexibility at no extra cost. -- Dmitry OlshanskySimply on the ground of "solves problem in too narrow scope for a hefty coast to the user" I'd drop it.It's good. Let's keep it.
Nov 13 2013
On 2013-11-13 03:15, Andrei Alexandrescu wrote:Wait, doesn't Object.factory call the default constructor of the created object?Yes, but you use ClassInfo.find and _d_newclass, which is exactly the same thing, except it won't call the constructor. Object.factory uses these functions internally. -- /Jacob Carlborg
Nov 13 2013
On 2013-11-12 23:45, Andrei Alexandrescu wrote:It's not quite trivial - somewhere there has to be a map and registration and lookup and whatnot. I don't see it why it's unbecoming for such functionality to be part of the standard library. I would agree, however, that it's a judgment call whether it should be wired in or provided on demand.From a (de)serialization point of view, I like that it's not necessary to register every class that should be (de)serialized. -- /Jacob Carlborg
Nov 13 2013
On Tuesday, 12 November 2013 at 22:36:22 UTC, Timon Gehr wrote:- Every class in any imported module will need to show up in the generated code even if it is never used unless global static analysis is carried out on arguments to Object.factory. - If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor. - The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.+ 1. To test, I made a Factory template that works like this: --- unittest { import std.traits : fullyQualifiedName; interface I {} class A : I {} class B : I {} alias f = Factory!(I, A, B); static assert(is(f.Product == I)); I a = f.create(fullyQualifiedName!A); assert(cast(A)a); I b = f.create(fullyQualifiedName!B); assert(cast(B)b); } --- Some notes: * The first parameter, the Product parameter, which can be any class or interface, is required because CommonType is always Object when given a heterogeneous list of classes. I assume this is because of the difficulty of choosing a common interface when multiple interfaces are present. * All subsequent parameters, the factory provider classes, are of course checked that they actually implement the Product, whether as a base class or an interface. Is a bit of a gotcha because AliasThis has to be "worked around" in the checking code. * Implementation uses a sorted array instead of an AA because AAs can't be transferred from CT to RT yet. * The array is immutable, so the same list is used for the same Factory across threads (but I suppose that has no real significance, just an observation). * If the Product member is deemed unnecessary, it could just be a function template instead of a template with two members (Product and create). Advantages over Object.factory: * The opCall returns Product, not Object, so client code doesn't need to cast in most circumstances. * `fullyQualifiedName` is used in the example because it internally uses `TypeInfo_Class.create`, which requires the full, internal names of the A and B types, which of course, are nested in a unittest. However, since the range of implementations are known, it could be amended to statically figure out a less strict naming scheme. In this case, just "A" and "B" could be made valid without ambiguity. Object.factory cannot practically do this. * Interestingly, since it knows all the constructors, it could present an overload set of the constructors that all implementors support, with some implementation effort! * Lookup has the opportunity to be a lot faster than Object.factory due to the smaller set of providers. * The runtime list of providers does not contain types that aren't part of the set (duh), hence no bloat. The disadvantage is that all implementing types must be passed at compile-time. That is the only advantage of Object.factory, AFAICS. As it seems like a very niche need that everyone has to pay for, I don't think Object.factory carries its weight.
Nov 12 2013
On 11/12/13 3:05 PM, Jakob Ovrum wrote:On Tuesday, 12 November 2013 at 22:36:22 UTC, Timon Gehr wrote:Stopped reading here, and everybody should. There is a thorough treatment of object factories in "Modern C++ Design" which I recommend not because I wrote it, but because it's thorough. Part of it is to show why the approach above is marred by dependency issues and should often be avoided. Andrei- Every class in any imported module will need to show up in the generated code even if it is never used unless global static analysis is carried out on arguments to Object.factory. - If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor. - The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.+ 1. To test, I made a Factory template that works like this: --- unittest { import std.traits : fullyQualifiedName; interface I {} class A : I {} class B : I {} alias f = Factory!(I, A, B);
Nov 12 2013
On Tuesday, 12 November 2013 at 23:47:10 UTC, Andrei Alexandrescu wrote:On 11/12/13 3:05 PM, Jakob Ovrum wrote:Sure, it has dependency issues, it needs to know all the types then and there, with is the anti-thesis of many factories. So, call it something else, like TypeMap, and it's still a useful type. But alright, it is not a good factory.On Tuesday, 12 November 2013 at 22:36:22 UTC, Timon Gehr wrote:Stopped reading here, and everybody should. There is a thorough treatment of object factories in "Modern C++ Design" which I recommend not because I wrote it, but because it's thorough. Part of it is to show why the approach above is marred by dependency issues and should often be avoided.- Every class in any imported module will need to show up in the generated code even if it is never used unless global static analysis is carried out on arguments to Object.factory. - If I know the fully qualified name of a class, there are better ways to instantiate this class than passing a string containing that name to Object.factory in order to call the default constructor. - The functionality provided by Object.factory is trivially replaced by a solution more specifically tailored to the problem at hand using compile-time reflection.+ 1. To test, I made a Factory template that works like this: --- unittest { import std.traits : fullyQualifiedName; interface I {} class A : I {} class B : I {} alias f = Factory!(I, A, B);
Nov 12 2013
On Wednesday, 13 November 2013 at 00:03:49 UTC, Jakob Ovrum wrote:But alright, it is not a good factory.Well, D compile-time reflection allows to define factory that finds all classes that implement given interface and provides them for construction, with no explicit list. I have used this in D1 though because of limited compile-time reflection capabilities there.
Nov 12 2013
On 11/12/13 4:18 PM, Dicebot wrote:On Wednesday, 13 November 2013 at 00:03:49 UTC, Jakob Ovrum wrote:I don't think all classes in a system are discoverable during compilation. This is the part of Object.factory that is not easy to replace. There must be a global map somewhere and a per-module registration during initialization. AndreiBut alright, it is not a good factory.Well, D compile-time reflection allows to define factory that finds all classes that implement given interface and provides them for construction, with no explicit list.
Nov 12 2013
On Wednesday, 13 November 2013 at 00:25:36 UTC, Andrei Alexandrescu wrote:I don't think all classes in a system are discoverable during compilation. This is the part of Object.factory that is not easy to replace. There must be a global map somewhere and a per-module registration during initialization. AndreiSure, this topic was already discussed in __traits(getUnittest) context recently, similar problem. You can find all classes that transitively accessible from context via imports - this is a natural limitation of compile-time reflection. Sometimes it is enough, sometimes registration is necessary. But it is a nice tool to have.
Nov 12 2013
Am 10.11.2013 13:03, schrieb Timon Gehr:Using the export attribute on some member exports the entire module info. If I understand this right, the module info will contain information also about non-exported members of the module and therefore even non-exported members will be accessible from outside. Is this correct?Addition: It is necessary to export the module info. Because as soon as you import a module and use any part of it, the compiler will reference the module info.
Nov 10 2013
On 11/10/2013 4:13 AM, Benjamin Thaut wrote:Addition: It is necessary to export the module info. Because as soon as you import a module and use any part of it, the compiler will reference the module info.Actually, it isn't entirely clear to me why the moduleinfo needs to be exported. The moduleinfo is needed to run the static ctors/dtors, etc., but that is needed by the code internal to the dll that initializes the dll.
Nov 10 2013
Am 10.11.2013 20:33, schrieb Walter Bright:On 11/10/2013 4:13 AM, Benjamin Thaut wrote:Well if the dll initializes all the modules, it has to initialize _all_ the modules because it can't know which are used and which are not. This is most certenly the way to go when loading a D dll with the LoadLibrary windows api function(s). But when you link against a D dll using a genereated import library there would be the option to make it behave exactly like static libraries where only modules get initiliazed, which are actually used. Also lets assume you do not export module infos, how does the compiler know that the module is inside a dll so that the module info is not referenced? That goes against the basic design of this DIP. The entire idea was to make static / shared liraries more similar and require less effort from the user. And not the other way around. If this is not wanted we can go right back to the C/C++ way of defining both dllexport and dllimport. Kind Regards Benjamin ThautAddition: It is necessary to export the module info. Because as soon as you import a module and use any part of it, the compiler will reference the module info.Actually, it isn't entirely clear to me why the moduleinfo needs to be exported. The moduleinfo is needed to run the static ctors/dtors, etc., but that is needed by the code internal to the dll that initializes the dll.
Nov 10 2013
On 11/10/2013 1:25 PM, Benjamin Thaut wrote:Am 10.11.2013 20:33, schrieb Walter Bright:Yes, and that's also the way shared library support has been implemented in druntime.On 11/10/2013 4:13 AM, Benjamin Thaut wrote:Well if the dll initializes all the modules, it has to initialize _all_ the modules because it can't know which are used and which are not. This is most certenly the way to go when loading a D dll with the LoadLibrary windows api function(s).Addition: It is necessary to export the module info. Because as soon as you import a module and use any part of it, the compiler will reference the module info.Actually, it isn't entirely clear to me why the moduleinfo needs to be exported. The moduleinfo is needed to run the static ctors/dtors, etc., but that is needed by the code internal to the dll that initializes the dll.But when you link against a D dll using a genereated import library there would be the option to make it behave exactly like static libraries where only modules get initiliazed, which are actually used.I don't believe it is necessary to support this. I don't think the C or C++ standard shared runtime library does. The general idea with DLLs is they become an indivisible unit - if you want to load only part of a DLL, you should split it into multiple DLLs.Also lets assume you do not export module infos, how does the compiler know that the module is inside a dll so that the module info is not referenced? That goes against the basic design of this DIP. The entire idea was to make static / shared liraries more similar and require less effort from the user. And not the other way around. If this is not wanted we can go right back to the C/C++ way of defining both dllexport and dllimport.The trouble with dllexport and dllimport is the same module is both depending on how it is used - I don't think that is very viable (C/C++ deal with this using macros). The idea is to use export for both, and then use the context to figure out if it is dllimport or dllexport.
Nov 10 2013
Am 11.11.2013 05:04, schrieb Walter Bright:The trouble with dllexport and dllimport is the same module is both depending on how it is used - I don't think that is very viable (C/C++ deal with this using macros). The idea is to use export for both, and then use the context to figure out if it is dllimport or dllexport.Still, how do you want to stop the compiler from referencing the module infos without knowing the module is part of a dll?
Nov 10 2013
On 11/10/2013 10:02 PM, Benjamin Thaut wrote:Am 11.11.2013 05:04, schrieb Walter Bright:The compiler needs to know which modules are coming from a dll. The only reference is in the import dependency list, which is used for static constructor calling. A dll's initialization should be self-contained and fully performed upon loading of the dll. The compiler should skip adding it to the import dependencies.The trouble with dllexport and dllimport is the same module is both depending on how it is used - I don't think that is very viable (C/C++ deal with this using macros). The idea is to use export for both, and then use the context to figure out if it is dllimport or dllexport.Still, how do you want to stop the compiler from referencing the module infos without knowing the module is part of a dll?
Nov 11 2013
On Sunday, 10 November 2013 at 19:34:32 UTC, Walter Bright wrote:On 11/10/2013 4:13 AM, Benjamin Thaut wrote: Actually, it isn't entirely clear to me why the moduleinfo needs to be exported.That depends on whether using a module might require to link against the moduleinfo, currently it does. For example there is an importedModules property in ModuleInfo.The moduleinfo is needed to run the static ctors/dtors, etc., but that is needed by the code internal to the dll that initializes the dll.Initialization order is not an issue here, the whole DLL (and all modules) is initialized before any DLL/exe that depends on it.
Nov 12 2013
On 11/12/2013 1:46 PM, Martin Nowak wrote:On Sunday, 10 November 2013 at 19:34:32 UTC, Walter Bright wrote:That's only used for initialization order.On 11/10/2013 4:13 AM, Benjamin Thaut wrote: Actually, it isn't entirely clear to me why the moduleinfo needs to be exported.That depends on whether using a module might require to link against the moduleinfo, currently it does. For example there is an importedModules property in ModuleInfo.Right, so it shouldn't need to list it as an imported module.The moduleinfo is needed to run the static ctors/dtors, etc., but that is needed by the code internal to the dll that initializes the dll.Initialization order is not an issue here, the whole DLL (and all modules) is initialized before any DLL/exe that depends on it.
Nov 14 2013
On Sunday, 10 November 2013 at 12:03:53 UTC, Timon Gehr wrote:Using the export attribute on some member exports the entire module info. If I understand this right, the module info will contain information also about non-exported members of the module and therefore even non-exported members will be accessible from outside. Is this correct?Yes, the module infos provide basic runtime reflection which can be used to access non-exported members.
Nov 12 2013
Am 08.11.2013 20:32, schrieb Walter Bright:And: Exporting of TLS variables is a minor issue. Frankly, I think peoplewho export global variables should be burned at the stake anyway, why should we make it even easier? Anyhow, if the user really, really wants to get himself immolated and export a TLS variable, he can always manually write the thunk (it is, after all, trivial). In my opinion it should be possible to add "export:" to the top of every source file and then compiler a dynamic library which exatcly behaves like a static library. If you don't want TLS variables in interfaces this should also be forbidden for interfaces of static libraries, because this isn't really any difference. This really should be consistent. Either TLS variabels are not allowed at all within library interfaces or they are allowed and supported always. Kind Regards Benjamin Thaut
Nov 10 2013
On 11/10/2013 6:52 AM, Benjamin Thaut wrote:Am 08.11.2013 20:32, schrieb Walter Bright: > > And: > > Exporting of TLS variables is a minor issue. Frankly, I think people who export global variables should be burned at the stake anyway, why should we make it even easier? Anyhow, if the user really, really wants to get himself immolated and export a TLS variable, he can always manually write the thunk (it is, after all, trivial). In my opinion it should be possible to add "export:" to the top of every source file and then compiler a dynamic library which exatcly behaves like a static library. If you don't want TLS variables in interfaces this should also be forbidden for interfaces of static libraries, because this isn't really any difference. This really should be consistent. Either TLS variabels are not allowed at all within library interfaces or they are allowed and supported always.I understand the consistency argument, but I'll also argue that if one raises consistency as an overriding requirement, everything else suffers. Everything is a tradeoff. It's long been recognized that using global variables to communicate between interfaces is a bad idea. And it isn't even supported for dlls, because the proposed solution to making them work is to wrap them with a function (much like D properties). We'd be going out of our way to support a recognized bad paradigm. There is currently no existing D code that requires this. If we add it, then we'd be stuck with supporting it for backwards compatibility. If we don't add it, and it becomes some sort of crisis that it isn't supported, we can add it in later without breaking things.
Nov 10 2013
Am 10.11.2013 20:46, schrieb Walter Bright:On 11/10/2013 6:52 AM, Benjamin Thaut wrote: I understand the consistency argument, but I'll also argue that if one raises consistency as an overriding requirement, everything else suffers. Everything is a tradeoff. It's long been recognized that using global variables to communicate between interfaces is a bad idea. And it isn't even supported for dlls, because the proposed solution to making them work is to wrap them with a function (much like D properties). We'd be going out of our way to support a recognized bad paradigm. There is currently no existing D code that requires this. If we add it, then we'd be stuck with supporting it for backwards compatibility. If we don't add it, and it becomes some sort of crisis that it isn't supported, we can add it in later without breaking things.You completely ignored my inlining argument. Lets assume there is some small function that qualifies for inlining but accesses a TLS variable marked with the export attribute. The compiler now wants to cross-module inline it. How does it do that, if there is no way to access TLS variables across DLL boundaries? The obvious solution would be, not do it at all. That means to disqualify all functions that do TLS access for cross-module inlining, because the compiler can't know if they are linked in from a static or shared library. With D beeing a language that aims for performance, do we really want to do that? Kind Regards Benjamin Thaut
Nov 10 2013
On 11/10/2013 1:50 PM, Benjamin Thaut wrote:You completely ignored my inlining argument. Lets assume there is some small function that qualifies for inlining but accesses a TLS variable marked with the export attribute. The compiler now wants to cross-module inline it. How does it do that, if there is no way to access TLS variables across DLL boundaries? The obvious solution would be, not do it at all.Right.That means to disqualify all functions that do TLS access for cross-module inlining, because the compiler can't know if they are linked in from a static or shared library.The compiler would benefit from knowing which modules are from a shared library, and it needs to anyway in order to distinguish between dllimport and dllexport.With D beeing a language that aims for performance, do we really want to do that?The trouble is, the proposed solution for exporting TLS variables is to wrap the access in a function that is not inline-able anyway. For performance oriented code, the user would be better off to write his own wrapper, and then hoist the call out of the hot spot, and use a pointer to the TLS variable in the hot spot. As a general rule, if code has a "hot spot" that crosses a DLL boundary, there is going to be performance problems with it regardless. I don't think this is a problem that the language can solve without some effort from the user.
Nov 10 2013
Am 11.11.2013 05:17, schrieb Walter Bright:On 11/10/2013 1:50 PM, Benjamin Thaut wrote: The compiler would benefit from knowing which modules are from a shared library, and it needs to anyway in order to distinguish between dllimport and dllexport.Then you didn't understand the DIP correctly. One of the main points of this DIP is to provide a implementation of the export attribute that does _not_ require the compiler to know anything about, wether certain modules will end up in a DLL or not. With the proposed behaviour you could compile a module into an object file and then link it both into a DLL and static lirary. Both would just work. When using a library the compiler also doesn't have to know if the lirary is shared or static. Both will work and it will be decided at link time which path is coosen (static or shared). Generally this behaviour makes the export attribute easier to use and less error prone. In an early version of this DIP did involve giving the compiler the information which modules end up in an shared library, but it was rejected in the discussion. A solution which does not require this information was preferred by almost all users taking part in the discussion.The trouble is, the proposed solution for exporting TLS variables is to wrap the access in a function that is not inline-able anyway. For performance oriented code, the user would be better off to write his own wrapper, and then hoist the call out of the hot spot, and use a pointer to the TLS variable in the hot spot. As a general rule, if code has a "hot spot" that crosses a DLL boundary, there is going to be performance problems with it regardless. I don't think this is a problem that the language can solve without some effort from the user.Correct. But the compiler would still be able do other optimizations given the function is sligtly bigger then a simple getter.
Nov 10 2013
On 11/10/2013 10:19 PM, Benjamin Thaut wrote:Am 11.11.2013 05:17, schrieb Walter Bright:I do understand that. I just have strong doubts about whether that is the best approach or not.On 11/10/2013 1:50 PM, Benjamin Thaut wrote: The compiler would benefit from knowing which modules are from a shared library, and it needs to anyway in order to distinguish between dllimport and dllexport.Then you didn't understand the DIP correctly. One of the main points of this DIP is to provide a implementation of the export attribute that does _not_ require the compiler to know anything about, wether certain modules will end up in a DLL or not. With the proposed behaviour you could compile a module into an object file and then link it both into a DLL and static lirary. Both would just work. When using a library the compiler also doesn't have to know if the lirary is shared or static. Both will work and it will be decided at link time which path is coosen (static or shared). Generally this behaviour makes the export attribute easier to use and less error prone.In an early version of this DIP did involve giving the compiler the information which modules end up in an shared library, but it was rejected in the discussion. A solution which does not require this information was preferred by almost all users taking part in the discussion.That's possible, but I suspect much of the potential gains would be lost.The trouble is, the proposed solution for exporting TLS variables is to wrap the access in a function that is not inline-able anyway. For performance oriented code, the user would be better off to write his own wrapper, and then hoist the call out of the hot spot, and use a pointer to the TLS variable in the hot spot. As a general rule, if code has a "hot spot" that crosses a DLL boundary, there is going to be performance problems with it regardless. I don't think this is a problem that the language can solve without some effort from the user.Correct. But the compiler would still be able do other optimizations given the function is sligtly bigger then a simple getter.
Nov 11 2013
Am 11.11.2013 19:21, schrieb Walter Bright:On 11/10/2013 10:19 PM, Benjamin Thaut wrote: I do understand that. I just have strong doubts about whether that is the best approach or not.So we throw 90% of the DIP away and you do a counter proposal?
Nov 11 2013
On Monday, 11 November 2013 at 18:21:21 UTC, Walter Bright wrote:I do understand that. I just have strong doubts about whether that is the best approach or not.We made a slight adjustment on how exported data symbols should be accessed, always through a pointer indirection. Calling exported function already works transparently and accessing exported TLS variables isn't the most urgent issue. Given that exported data variables should be rare we're trading a tiny implementation change for a no worries export attribute.
Nov 12 2013
On Monday, 11 November 2013 at 04:18:09 UTC, Walter Bright wrote:The compiler would benefit from knowing which modules are from a shared library, and it needs to anyway in order to distinguish between dllimport and dllexport.We didn't found any reasonably simple solution to pass this information to the compiler. You'd have to explicitly list ALL modules that are supposed to be linked into the same DLL when compiling an object file.
Nov 12 2013
On 11/12/2013 2:23 PM, Martin Nowak wrote:On Monday, 11 November 2013 at 04:18:09 UTC, Walter Bright wrote:One possibility is modules listed on the command line are regarded as export==dllexport, and other modules as export==dllimport. This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.The compiler would benefit from knowing which modules are from a shared library, and it needs to anyway in order to distinguish between dllimport and dllexport.We didn't found any reasonably simple solution to pass this information to the compiler. You'd have to explicitly list ALL modules that are supposed to be linked into the same DLL when compiling an object file.
Nov 14 2013
Am 14.11.2013 11:28, schrieb Walter Bright:On 11/12/2013 2:23 PM, Martin Nowak wrote: One possibility is modules listed on the command line are regarded as export==dllexport, and other modules as export==dllimport. This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.That doesns't work for the case where a dll "A" uses a dll "B". In that case export has to mean "dllexport" for all modules of A but "dllimport" for all modules of B. -- Kind Regards Benjamin Thaut
Nov 14 2013
On 11/14/2013 3:37 AM, Benjamin Thaut wrote:Am 14.11.2013 11:28, schrieb Walter Bright:I don't follow. If you're compiling A, you're specifying A modules on the command line, and those will regard the B modules as dllimport.On 11/12/2013 2:23 PM, Martin Nowak wrote: One possibility is modules listed on the command line are regarded as export==dllexport, and other modules as export==dllimport. This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.That doesns't work for the case where a dll "A" uses a dll "B". In that case export has to mean "dllexport" for all modules of A but "dllimport" for all modules of B.
Nov 14 2013
Am 15/11/2013 08:27, schrieb Walter Bright:On 11/14/2013 3:37 AM, Benjamin Thaut wrote:Ok now I understand what you suggest. So basically you want to do the exact same as DIP 45 just giving the compiler parameter a different name. But you still didn't give a solution for the problem that the compiler does not know which modules are part of a shared library, which are part of a static library and which are part of the exeuctable. And please also consider single file compilation. Kind Regards Benjamin ThautAm 14.11.2013 11:28, schrieb Walter Bright:I don't follow. If you're compiling A, you're specifying A modules on the command line, and those will regard the B modules as dllimport.On 11/12/2013 2:23 PM, Martin Nowak wrote: One possibility is modules listed on the command line are regarded as export==dllexport, and other modules as export==dllimport. This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.That doesns't work for the case where a dll "A" uses a dll "B". In that case export has to mean "dllexport" for all modules of A but "dllimport" for all modules of B.
Nov 15 2013
On 11/15/2013 7:31 AM, Benjamin Thaut wrote:Am 15/11/2013 08:27, schrieb Walter Bright:I thought this did cover it. "export" in imported modules that are not listed on the compiler command line is regarded as "dllimport". "export" in modules listed on the commmand line is regarded as "dllexport".On 11/14/2013 3:37 AM, Benjamin Thaut wrote:Ok now I understand what you suggest. So basically you want to do the exact same as DIP 45 just giving the compiler parameter a different name. But you still didn't give a solution for the problem that the compiler does not know which modules are part of a shared library, which are part of a static library and which are part of the exeuctable.Am 14.11.2013 11:28, schrieb Walter Bright:I don't follow. If you're compiling A, you're specifying A modules on the command line, and those will regard the B modules as dllimport.On 11/12/2013 2:23 PM, Martin Nowak wrote: One possibility is modules listed on the command line are regarded as export==dllexport, and other modules as export==dllimport. This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.That doesns't work for the case where a dll "A" uses a dll "B". In that case export has to mean "dllexport" for all modules of A but "dllimport" for all modules of B.And please also consider single file compilation.And yes, this would mean that a function may wind up calling a function in its own dll through the dll import thunk -- however -- this thunk is often provided by the import library, and so wouldn't be there for intra-dll calls.
Nov 17 2013
Am 14.11.2013 11:28, schrieb Walter Bright:This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.Also our suggested approach would not have this downside. Think about phobos. All of phobos and druntime will be littered with export attributes. Do you really want all phobos and druntime from within the same dll to go through dll indirections? And what would happen when building phobos as static library? With your suggested behavior (export=dllimport if not specified otherwise) this would mean that even the phobos static library would have those indirections. -- Kind Regards Benjamin Thaut
Nov 14 2013
On Thursday, 14 November 2013 at 13:31:30 UTC, Benjamin Thaut wrote:Am 14.11.2013 11:28, schrieb Walter Bright:This thread have reached some end. It has been shown in the beginning that linkers know how to get rid of the indirection here. If we are going to discuss the same thing again and again without even doing some experiment for the reality check, then we can stop right here.This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.Also our suggested approach would not have this downside. Think about phobos. All of phobos and druntime will be littered with export attributes. Do you really want all phobos and druntime from within the same dll to go through dll indirections? And what would happen when building phobos as static library? With your suggested behavior (export=dllimport if not specified otherwise) this would mean that even the phobos static library would have those indirections.
Nov 14 2013
Am 14.11.2013 20:54, schrieb deadalnix:On Thursday, 14 November 2013 at 13:31:30 UTC, Benjamin Thaut wrote:No, linkers can not get of this indirection. Only whole program optimization can, and that won't happen in D (at least dmd) for quite some time. We had exactly that discussion in the old thread about the export attribute. See here: http://forum.dlang.org/thread/kvhu2c$2ikq$1 digitalmars.com#post-kvhu2c:242ikq:241:40digitalmars.com But because Walter does not agree with the way we solved the problem in DIP 45 we have to go through everything again from the beginning. And I'm certenly not going to accept a solution where we rely on some non implemented feature to remove indirections which will most likely not happen in the near future. The same already happend for virtual functions. The documentation states that the compiler might optimize them away, but in pratice this still does not happen, and most likely never will. With DIP 45 we setteled for additional indirections to achive better usability and I'm ok with that. But what Walter suggests esentially means even more indirections and worse usability. And I'm not ok with that. I also don't like that we have to go through everything again we already discussed in the old thread, but if Walter doesn't agree it has to be that way. So please try to contribute in a constructive manner, or leave out comments entierly. Kind Regards Benjamin ThautAm 14.11.2013 11:28, schrieb Walter Bright:This thread have reached some end. It has been shown in the beginning that linkers know how to get rid of the indirection here. If we are going to discuss the same thing again and again without even doing some experiment for the reality check, then we can stop right here.This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.Also our suggested approach would not have this downside. Think about phobos. All of phobos and druntime will be littered with export attributes. Do you really want all phobos and druntime from within the same dll to go through dll indirections? And what would happen when building phobos as static library? With your suggested behavior (export=dllimport if not specified otherwise) this would mean that even the phobos static library would have those indirections.
Nov 14 2013
OK, I missed the point where everything started back again. This is being ridiculous.
Nov 14 2013
On 11/14/2013 5:31 AM, Benjamin Thaut wrote:Am 14.11.2013 11:28, schrieb Walter Bright:It's not that bad. Phobos can be built by specifying all the files on the command line. Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.This of course means that functions may wind up going through the dllimport indirection even for calling functions in the same dll, but it should work.Also our suggested approach would not have this downside. Think about phobos. All of phobos and druntime will be littered with export attributes. Do you really want all phobos and druntime from within the same dll to go through dll indirections? And what would happen when building phobos as static library? With your suggested behavior (export=dllimport if not specified otherwise) this would mean that even the phobos static library would have those indirections.
Nov 14 2013
"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l64imh$27na$1 digitalmars.com...Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.The symbol in the import library just translates to an import table indirection. The trick you may have been thinking of is system dlls (kernel32.dll, user32.dll etc) are always loaded at the same address, so a little hackery will let you bypass the import table.
Nov 15 2013
On 11/15/2013 12:00 AM, Daniel Murphy wrote:"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l64imh$27na$1 digitalmars.com...Yes, meaning the compiler doesn't have to do it if the import library is set up correctly. (implib.exe should do it.)Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.The symbol in the import library just translates to an import table indirection.
Nov 15 2013
"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l64lji$2buh$1 digitalmars.com...On 11/15/2013 12:00 AM, Daniel Murphy wrote:Right, I was saying the indirection still exists."Walter Bright" <newshound2 digitalmars.com> wrote in message news:l64imh$27na$1 digitalmars.com...Yes, meaning the compiler doesn't have to do it if the import library is set up correctly. (implib.exe should do it.)Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.The symbol in the import library just translates to an import table indirection.
Nov 15 2013
On 11/15/2013 8:08 AM, Daniel Murphy wrote:"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l64lji$2buh$1 digitalmars.com...No, not really, at least not on Windows. Try calling a function in the Windows API and disassemble it. You'll see a direct call.On 11/15/2013 12:00 AM, Daniel Murphy wrote:Right, I was saying the indirection still exists."Walter Bright" <newshound2 digitalmars.com> wrote in message news:l64imh$27na$1 digitalmars.com...Yes, meaning the compiler doesn't have to do it if the import library is set up correctly. (implib.exe should do it.)Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.The symbol in the import library just translates to an import table indirection.
Nov 17 2013
"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l6c7gt$27fc$1 digitalmars.com...You sure about that? src: import core.sys.windows.windows; void main() { auto x = GetModuleHandleA(null); } obj: __Dmain PROC NEAR ; COMDEF __Dmain push 0 ; 0000 _ 6A, 00 call dword ptr [__imp__GetModuleHandleA 4] ; 0002 _ FF. 15, 00000000(segrel) xor eax, eax ; 0008 _ 31. C0 ret ; 000A _ C3 __Dmain ENDP exe: ?_0072 LABEL NEAR push 0 ; 00402010 _ 6A, 00 call dword ptr [?_0067] ; 00402012 _ FF. 15, 00401794(d) xor eax, eax ; 00402018 _ 31. C0 ret ; 0040201A _ C3Right, I was saying the indirection still exists.No, not really, at least not on Windows. Try calling a function in the Windows API and disassemble it. You'll see a direct call.
Nov 17 2013
On 11/17/2013 9:25 PM, Daniel Murphy wrote:"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l6c7gt$27fc$1 digitalmars.com...Try this: extern (Windows) int GetModuleHandleA(char*); void main() { auto x = GetModuleHandleA(null); } and it compiles and links and runs. No indirection (in the object file, the indirection is supplied by linker, triggered by an impdef record in kernel32.lib). The "export" isn't actually needed.You sure about that? src: import core.sys.windows.windows; void main() { auto x = GetModuleHandleA(null); } obj: __Dmain PROC NEAR ; COMDEF __Dmain push 0 ; 0000 _ 6A, 00 call dword ptr [__imp__GetModuleHandleA 4] ; 0002 _ FF. 15, 00000000(segrel) xor eax, eax ; 0008 _ 31. C0 ret ; 000A _ C3 __Dmain ENDP exe: ?_0072 LABEL NEAR push 0 ; 00402010 _ 6A, 00 call dword ptr [?_0067] ; 00402012 _ FF. 15, 00401794(d) xor eax, eax ; 00402018 _ 31. C0 ret ; 0040201A _ C3Right, I was saying the indirection still exists.No, not really, at least not on Windows. Try calling a function in the Windows API and disassemble it. You'll see a direct call.
Nov 18 2013
"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l6cm9d$2uq8$1 digitalmars.com...On 11/17/2013 9:25 PM, Daniel Murphy wrote:So now we get: ?_0072 LABEL NEAR push 0 ; 00402010 _ 6A, 00 call Unnamed_2_1A0DC ; 00402012 _ E8, 0001A0C5 xor eax, eax ; 00402017 _ 31. C0 ret ; 00402019 _ C3 Where Unnamed_2_1A0DC is a thunk in the import table: Unnamed_2_1A0DC LABEL NEAR jmp dword ptr [?_0067] ; 0041C0DC _ FF. 25, 00401794(d) That still appears to be a layer of indirection to me... So the difference is the compiler is unaware the function may be called through the import table? Why would it ever _want_ to use the first version, are there any cases that can't simply be rewritten as the second?"Walter Bright" <newshound2 digitalmars.com> wrote in message news:l6c7gt$27fc$1 digitalmars.com...Try this: extern (Windows) int GetModuleHandleA(char*); void main() { auto x = GetModuleHandleA(null); } and it compiles and links and runs. No indirection (in the object file, the indirection is supplied by linker, triggered by an impdef record in kernel32.lib). The "export" isn't actually needed.You sure about that? src: import core.sys.windows.windows; void main() { auto x = GetModuleHandleA(null); } obj: __Dmain PROC NEAR ; COMDEF __Dmain push 0 ; 0000 _ 6A, 00 call dword ptr [__imp__GetModuleHandleA 4] ; 0002 _ FF. 15, 00000000(segrel) xor eax, eax ; 0008 _ 31. C0 ret ; 000A _ C3 __Dmain ENDP exe: ?_0072 LABEL NEAR push 0 ; 00402010 _ 6A, 00 call dword ptr [?_0067] ; 00402012 _ FF. 15, 00401794(d) xor eax, eax ; 00402018 _ 31. C0 ret ; 0040201A _ C3Right, I was saying the indirection still exists.No, not really, at least not on Windows. Try calling a function in the Windows API and disassemble it. You'll see a direct call.
Nov 18 2013
Am 15/11/2013 08:32, schrieb Walter Bright:It's not that bad. Phobos can be built by specifying all the files on the command line. Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.I know that. And we are using that fact in DIP 45. For data symbols we did suggest a similar behaviour that has to be implemented by us first. And yes we need data symbols, because of all the implicit data symbols the D programming language generates. TypeInfos, vtables etc. I suggest that you read all the links I gave for further reading in DIP 45 and DIP 45 again, because you are pretty close to what we suggested in DIP 45 without actually realizing it. Kind Regards Benjamin Thaut
Nov 15 2013
On 11/15/2013 7:34 AM, Benjamin Thaut wrote:Am 15/11/2013 08:32, schrieb Walter Bright:I'm very much against the suggested rewriting of obj files to strip the export records :-)It's not that bad. Phobos can be built by specifying all the files on the command line. Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.I know that. And we are using that fact in DIP 45. For data symbols we did suggest a similar behaviour that has to be implemented by us first. And yes we need data symbols, because of all the implicit data symbols the D programming language generates. TypeInfos, vtables etc. I suggest that you read all the links I gave for further reading in DIP 45 and DIP 45 again, because you are pretty close to what we suggested in DIP 45 without actually realizing it.
Nov 17 2013
Am 18.11.2013 06:12, schrieb Walter Bright:On 11/15/2013 7:34 AM, Benjamin Thaut wrote:That can easily be avoided by suppling a compiler switch which either actives exporting (if it is off by default) or deactivating export (if it is on by default). -- Kind Regards Benjamin ThautAm 15/11/2013 08:32, schrieb Walter Bright:I'm very much against the suggested rewriting of obj files to strip the export records :-)It's not that bad. Phobos can be built by specifying all the files on the command line. Also, at least on Windows, you can call functions in a DLL without saying dllimport on them and suffering a layer of indirection. The magic happens in the import library, which provides the relevant thunk. It's about 15 years since I worked on this stuff, so I might be a bit fuzzy on the details.I know that. And we are using that fact in DIP 45. For data symbols we did suggest a similar behaviour that has to be implemented by us first. And yes we need data symbols, because of all the implicit data symbols the D programming language generates. TypeInfos, vtables etc. I suggest that you read all the links I gave for further reading in DIP 45 and DIP 45 again, because you are pretty close to what we suggested in DIP 45 without actually realizing it.
Nov 20 2013
On 11/18/2013 06:12 AM, Walter Bright wrote:I'm very much against the suggested rewriting of obj files to strip the export records :-)That is only one particular aspect of the DIP. It's not essential and maybe we'll find a different solution during implementation. The need to statically link a library into a DLL is rare enough to delegate this to an extra tool.
Dec 01 2013
On 2 December 2013 06:05, Martin Nowak <code dawg.eu> wrote:On 11/18/2013 06:12 AM, Walter Bright wrote:Sorry, I'm just casually following this thread. I'm very interested to have this mess resolved, but don't have a great deal of input. This comment surprised me a little though, you say it's rare to statically link a lib into a DLL? In my experience, I don't think that's rare at all, I'd say it's very common, in fact, in my experience, I can't think of many instances where I've built some code as a DLL which hasn't included other lib's (statically linked). Maybe it's just me, but if I'm building something as a DLL (rather than a static lib), it's precisely because it is quite large, and offers generally self-contained functionality (which often depends on other libs). The only exception in my experience is plugin functionality. Just a comment on other aspects of this thread... There's all this debate around dllimport vs dllexport. And this is probably my ignorance, but I have to wonder why dllimport even exists? I've been using DLL's for a long time, and I've never used dllimport (that I'm aware of). I either statically link an import lib (which binds to the exports in a nice efficient way), or I call LoadLibrary and find symbols manually... I'm not really even sure what dllimport does. Why is it so important, and why is it causing so much fuss?I'm very much against the suggested rewriting of obj files to strip the export records :-)That is only one particular aspect of the DIP. It's not essential and maybe we'll find a different solution during implementation. The need to statically link a library into a DLL is rare enough to delegate this to an extra tool.
Dec 01 2013
On 02.12.2013 05:59, Manu wrote:Just a comment on other aspects of this thread... There's all this debate around dllimport vs dllexport. And this is probably my ignorance, but I have to wonder why dllimport even exists? I've been using DLL's for a long time, and I've never used dllimport (that I'm aware of). I either statically link an import lib (which binds to the exports in a nice efficient way), or I call LoadLibrary and find symbols manually... I'm not really even sure what dllimport does. Why is it so important, and why is it causing so much fuss?For function imports, not using dllimport magically works, but adding it to the declaration can avoid one jump when calling the imported function. Imports to data variables don't have that magic, it can either be accessed directly, or it has to be accessed by an indirection through the import table. The dllimport specifies which code has to be generated. I actually have an implementation, that patches the data accesses as a secondary relocation step, circumventing the indirection through the import table. Unfortunately it does not work on 64-bit. The problem here is that dmd generated code only uses 32-bit relative addressing, but that disallows accessing data in another shared library directly. The respective discussion where this approach was rejected by Walter is supposed to be here: http://forum.dlang.org/thread/8bd42b4e-8c3b-42a6-9f00-2081b1329cd2 me.com , but it seems it is somehow reduced to a single post.
Dec 02 2013
On 11/18/2013 06:12 AM, Walter Bright wrote:I'm very much against the suggested rewriting of obj files to strip the export records :-)Changed http://wiki.dlang.org/?title=DIP45&diff=3201&oldid=3094
Dec 01 2013
On 11/15/2013 08:32 AM, Walter Bright wrote:It's not that bad. Phobos can be built by specifying all the files on the command line.That's the essential trade-off we have to make. Either we come up with a clumsy way to list all DLL modules during compilation (separate compilation) or we add some compiler logic to auto-import exported data symbols. In the latter case compiled object files can be used for a DLL AND a static library. We found the second approach to be more attractive. The only issue is that most of the time one wants to strip exports when using objects for a static library.
Nov 16 2013
On 11/16/2013 7:22 AM, Martin Nowak wrote:On 11/15/2013 08:32 AM, Walter Bright wrote:Why? dmd is very fast at compiling. I'm not sure what is being saved here.It's not that bad. Phobos can be built by specifying all the files on the command line.That's the essential trade-off we have to make. Either we come up with a clumsy way to list all DLL modules during compilation (separate compilation) or we add some compiler logic to auto-import exported data symbols. In the latter case compiled object files can be used for a DLL AND a static library. We found the second approach to be more attractive.The only issue is that most of the time one wants to strip exports when using objects for a static library.That's a pretty big issue (to me, anyway).
Nov 17 2013
On 11/18/2013 06:13 AM, Walter Bright wrote:Why? dmd is very fast at compiling. I'm not sure what is being saved here.It's not the time argument, but correctly listing all modules that go into the same DLL as additional compilation argument. You can see an earlier proposals of the DIP. http://wiki.dlang.org/?title=DIP45&oldid=2921 - export(exampleLib) dmd -m64 -export libA -of"libA.dll" dllmain.d libA.d -L/DLL -L/IMPLIB:"libA.lib" dmd -m64 -export libB -import libA -of"libB.dll" dllmain.d libB.d -L/DLL -L/IMPLIB:"libB.lib" -LlibA.lib dmd -m64 -import libB -import libA main.d -LlibA.lib -LlibB.lib
Dec 01 2013
On 11/14/2013 11:28 AM, Walter Bright wrote:One possibility is modules listed on the command line are regarded as export==dllexport, and other modules as export==dllimport.We've considered this and it doesn't work for separate compilation. This means I couldn't compile phobos dll with unittests.
Nov 16 2013
On Sunday, 10 November 2013 at 21:50:51 UTC, Benjamin Thaut wrote:You completely ignored my inlining argument. Lets assume there is some small function that qualifies for inlining but accesses a TLS variable marked with the export attribute.That's not an issue, you can't inline exported functions. This is because the actual implementation is only know at link-time (only at run-time on UNIX).
Nov 12 2013
On 11/12/2013 11:12 PM, Martin Nowak wrote:That's not an issue, you can't inline exported functions. This is because the actual implementation is only know at link-time (only at run-time on UNIX).One might chose to ignore this possibly incorrect behaviour and still inline in favour of speed, but I don't think we need to worry about this now.
Nov 16 2013
On Sunday, 10 November 2013 at 19:47:16 UTC, Walter Bright wrote:It's long been recognized that using global variables to communicate between interfaces is a bad idea. And it isn't even supported for dlls, because the proposed solution to making them work is to wrap them with a function (much like D properties). We'd be going out of our way to support a recognized bad paradigm. There is currently no existing D code that requires this. If we add it, then we'd be stuck with supporting it for backwards compatibility. If we don't add it, and it becomes some sort of crisis that it isn't supported, we can add it in later without breaking things.Yes, data interfaces (be it TLS or __gshared) are bad and make it more difficult to maintain a stable ABI. I also agree that TLS accesses shouldn't be a big problem.
Nov 12 2013
On 10.11.2013 12:38, Benjamin Thaut wrote:Attached you will find a e-mail conversation about the discussion of DIP 45. I moved the discussion here so everyone can contribute. Hi, DIP 45 is waiting for some offical approval / comment so it can be implemented. It would be great if one of you could look into the topic and give official approval or improvement suggestions as the DIP has been in limbo for quite some time now. I'm not willing to start working on it before there is official approval, because I made bad experiences with this in the past. In my opinion the DIP is in a state where further discussion will not lead to any improvements. The DIP should be implemented to see which problems arrise during implementation to then discuss how to solve them. I've also seen a couple of questions to the state of Windows DLL support and shared libraries on the newsgroup. A working export keyword is required to actually implement that support on windows. The DIP: http://wiki.dlang.org/DIP45 The latest discussion on the NG: http://forum.dlang.org/post/kvhu2c$2ikq$1 digitalmars.com Bug associated with the problem: http://d.puremagic.com/issues/show_bug.cgi?id=9816 Kind Regards Benjamin ThautI think the DIP is very reasonable. Some random comments: - when building a single object file or library, I would expect that it won't be torn apart later, so optimizations currently limited to accesses within the same module by the DIP could be extended to the modules included in current compilation target. - a better example where cross-DLL data accesses to globals are needed is TypeInfo, e.g. when a class info references it's base class or interfaces. - I'd be fine with reversing the default for stripping exports from a library, i.e. replacing "-libexports" with "-stripexports". - I agree that cross DLL TLS accesses should be possible, though it might be implemented in a second step. Last time I tried it with C++, it wasn't supported, but it is probably not missed that much as thread local data is not as ubiquitous as in D. - The DIP should state what happens to template instances. Assuming the definition is marked "export", I guess instances will be dllexport if they are compiled together with the module that contains the definition. What will happen if it is instantiated, but the defining module is merely imported? Rainer
Nov 12 2013
Am 13.11.2013 08:31, schrieb Rainer Schuetze:- The DIP should state what happens to template instances. Assuming the definition is marked "export", I guess instances will be dllexport if they are compiled together with the module that contains the definition. What will happen if it is instantiated, but the defining module is merely imported? RainerThat is a very good point I also thought about. But because I'm not familiar with how exactly template instanciation works I can't really provide a solution for that. Does Walters recent template instanciation optimization also take into account template instaces of templates that are defined outside of the module that contains the template definition? I think the behaviour of exported template should mainly satisfy the needs of this optimization. Kind Regards Benjamin Thaut
Nov 13 2013
On 13.11.2013 09:27, Benjamin Thaut wrote:Am 13.11.2013 08:31, schrieb Rainer Schuetze:As far as I understand, the optimization avoids generating code for template instances only _created_ (not defined) by imported modules, e.g. when needed for semantic analysis inside the imported module, but never actually referenced by generated code by the root modules. [OT: The problem of the current implementation is the detection of "only" in that definition, it fails too often leading to linker errors and the addition of that terrible -allinst switch. I had to disable the optimization for me before addition of that switch.] With respect to export I guess the optimization doesn't change much as multiple instances of the same template instance still can be found in different object files (maybe even less predictable). To avoid the trouble with templates, here is an idea for discussion: - templates definitions by themselves are never dllexport/dllimport - template instances are dllexport/dllimport with the proposed semantics of normal functions/variables if they are explicitely declared as "export" by an alias statement, e.g. export alias templ!int exported_tmpl_int; - implicitely created template instances don't create dllexport versions, and use dllimport semantics if the declaration above is found in an import. This would make it very explicit which module dllexports the template instance symbols. It is a bit like using typedef in C++ to ensure template instantiation. One problem that might appear is that there are some conflicts if some modules import the "export alias" declaration, and some do not: - In the dllexport case, it means that there are different COMDATs for the code and data of the template and it is undefined which one is chosen. (For Win64 COFF the exporting of the symbol is a separate directive, so it shouldn't be a problem, but I don't know for other object file formats.) - It's worse for the dllimport case, as data accesses to template variables will sometimes use the additional indirection, sometimes not.- The DIP should state what happens to template instances. Assuming the definition is marked "export", I guess instances will be dllexport if they are compiled together with the module that contains the definition. What will happen if it is instantiated, but the defining module is merely imported? RainerThat is a very good point I also thought about. But because I'm not familiar with how exactly template instanciation works I can't really provide a solution for that. Does Walters recent template instanciation optimization also take into account template instaces of templates that are defined outside of the module that contains the template definition? I think the behaviour of exported template should mainly satisfy the needs of this optimization.
Nov 13 2013
Am 14.11.2013 08:36, schrieb Rainer Schuetze:As far as I understand, the optimization avoids generating code for template instances only _created_ (not defined) by imported modules, e.g. when needed for semantic analysis inside the imported module, but never actually referenced by generated code by the root modules. [OT: The problem of the current implementation is the detection of "only" in that definition, it fails too often leading to linker errors and the addition of that terrible -allinst switch. I had to disable the optimization for me before addition of that switch.] With respect to export I guess the optimization doesn't change much as multiple instances of the same template instance still can be found in different object files (maybe even less predictable). To avoid the trouble with templates, here is an idea for discussion: - templates definitions by themselves are never dllexport/dllimport - template instances are dllexport/dllimport with the proposed semantics of normal functions/variables if they are explicitely declared as "export" by an alias statement, e.g. export alias templ!int exported_tmpl_int; - implicitely created template instances don't create dllexport versions, and use dllimport semantics if the declaration above is found in an import.I actually like this idea. Everytime I actually had to dllexport / dllimport templates in C++ I knew beforehand which types I need the template exported for. But what would should in a case like this: class WeakReferenced(T) { export __gshared WeakReferenced!T[] m_weakTable; } Note that the export is not applied to the template directly but instead to a specific member inside the template. Should this be an error?- It's worse for the dllimport case, as data accesses to template variables will sometimes use the additional indirection, sometimes not.Why exactly is that? Would that also be the case with the system proposed by the DIP? The dip never actually uses dllimport, so shouldn't it just work (tm)? Kind Regards Benjamin Thaut
Nov 14 2013
On 14.11.2013 18:25, Benjamin Thaut wrote:Am 14.11.2013 08:36, schrieb Rainer Schuetze:According to the rule above "export" on the template definition has no effect, only when it is instantiated with export alias WeakReferenced!int WeakRefIntArray;As far as I understand, the optimization avoids generating code for template instances only _created_ (not defined) by imported modules, e.g. when needed for semantic analysis inside the imported module, but never actually referenced by generated code by the root modules. [OT: The problem of the current implementation is the detection of "only" in that definition, it fails too often leading to linker errors and the addition of that terrible -allinst switch. I had to disable the optimization for me before addition of that switch.] With respect to export I guess the optimization doesn't change much as multiple instances of the same template instance still can be found in different object files (maybe even less predictable). To avoid the trouble with templates, here is an idea for discussion: - templates definitions by themselves are never dllexport/dllimport - template instances are dllexport/dllimport with the proposed semantics of normal functions/variables if they are explicitely declared as "export" by an alias statement, e.g. export alias templ!int exported_tmpl_int; - implicitely created template instances don't create dllexport versions, and use dllimport semantics if the declaration above is found in an import.I actually like this idea. Everytime I actually had to dllexport / dllimport templates in C++ I knew beforehand which types I need the template exported for. But what would should in a case like this: class WeakReferenced(T) { export __gshared WeakReferenced!T[] m_weakTable; }Note that the export is not applied to the template directly but instead to a specific member inside the template. Should this be an error?Maybe. Another rule might be that only the declarations actually annotated with "export" gets exported with the instantiation, so you could add "export" to the whole class or only some declaraations.The problem is that the template instantiation can exist without the compiler seeing the alias: ----------- module a; class tmpl(T) { static shared int var; } ----------- module b; import a; export alias tmpl!int tmpl_int; ----------- module c; import b; int func() { return tmpl!int.var; } // uses dllimport access with indirection ----------- module d; import a; int fund() { return tmpl!int.var; } // uses standard access without indirection compiling c and d as single files will silently generate different code, because when compiling d, the export alias is never seen. (this cannot happen with standard variables, only when declared multiple times, but differently, with extern(C/C++/System)).- It's worse for the dllimport case, as data accesses to template variables will sometimes use the additional indirection, sometimes not.Why exactly is that? Would that also be the case with the system proposed by the DIP? The dip never actually uses dllimport, so shouldn't it just work (tm)?
Nov 14 2013
Am 15.11.2013 00:38, schrieb Rainer Schuetze:Maybe. Another rule might be that only the declarations actually annotated with "export" gets exported with the instantiation, so you could add "export" to the whole class or only some declaraations.I don't think this is a good idea. It should be possible to put "export:" on top of a file and just export everything. If you limit it to decelerations the following would work: export __gshared int g_var; but the following wouldn't: export __gshared int g_var = 0; Although it would really produce equivalent code.compiling c and d as single files will silently generate different code, because when compiling d, the export alias is never seen. (this cannot happen with standard variables, only when declared multiple times, but differently, with extern(C/C++/System)).And do you already have a idea how we could work around this problem? Kind Regards Benjamin Thaut
Nov 14 2013
On 15.11.2013 08:02, Benjamin Thaut wrote:Am 15.11.2013 00:38, schrieb Rainer Schuetze:I don't follow. What does this have to do with template instances? I was referring to your example where you wanted to export just one symbol from a template class.Maybe. Another rule might be that only the declarations actually annotated with "export" gets exported with the instantiation, so you could add "export" to the whole class or only some declaraations.I don't think this is a good idea. It should be possible to put "export:" on top of a file and just export everything. If you limit it to decelerations the following would work: export __gshared int g_var; but the following wouldn't: export __gshared int g_var = 0; Although it would really produce equivalent code.It might be possible to produce some linker errors: e.g. the dllimport version drags in the __imp_var symbol that also provides a _var definition record that produces some link error (e.g. by referring to a non-existing symbol). If it links to a non-dllimport version that actually refers to _var, it bails out. Generated COMDAT records that define the _var symbol aswell might cause problems here, though, because the current COMDAT selection strategy is "pick any".compiling c and d as single files will silently generate different code, because when compiling d, the export alias is never seen. (this cannot happen with standard variables, only when declared multiple times, but differently, with extern(C/C++/System)).And do you already have a idea how we could work around this problem?
Nov 15 2013
Am 15/11/2013 18:04, schrieb Rainer Schuetze:I don't follow. What does this have to do with template instances? I was referring to your example where you wanted to export just one symbol from a template class.Then I misunderstood your use of the word "declaration". With declarations you did mean template declarations only?
Nov 15 2013
On 15.11.2013 18:18, Benjamin Thaut wrote:Am 15/11/2013 18:04, schrieb Rainer Schuetze:I actually meant declaration inside template definitions as in your example: class WeakReferenced(T) { export __gshared WeakReferenced!T[] m_weakTable; } The proposal was that export alias WeakReferenced!int exported_weak_int_array; would only export the symbol for m_weakTable, not any other class symbols like the class info. I'm not sure if this is a good idea, I'm just exploring possibilities here.I don't follow. What does this have to do with template instances? I was referring to your example where you wanted to export just one symbol from a template class.Then I misunderstood your use of the word "declaration". With declarations you did mean template declarations only?
Nov 15 2013
Am 15/11/2013 18:41, schrieb Rainer Schuetze:I actually meant declaration inside template definitions as in your example: class WeakReferenced(T) { export __gshared WeakReferenced!T[] m_weakTable; } The proposal was that export alias WeakReferenced!int exported_weak_int_array; would only export the symbol for m_weakTable, not any other class symbols like the class info. I'm not sure if this is a good idea, I'm just exploring possibilities here.Sounds good to me. It gives the ability to percisely select what gets exported and what not. Basically also matches was C++ does.
Nov 15 2013