digitalmars.D - D1 -> D2
- Fawzi Mohamed (15/15) Nov 18 2010 Is there any "porting" guide around in a wiki?
- Mike Parker (9/23) Nov 18 2010 In maintaining Derelict, which is nothing more than a simple collection
- Fawzi Mohamed (4/39) Nov 18 2010 that was my feeling too, but I wanted some confirmation from people
- =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= (10/28) Nov 18 2010 wxD had the same experience, with supporting Phobos/Tango and D1/D2.
- spir (10/40) Nov 18 2010 t?
- Steven Schveighoffer (31/42) Nov 18 2010 I have had two experiences moving code from D1 to D2. First, I tried to...
- Walter Bright (7/9) Nov 18 2010 From what you wrote, it appears that most of the difficulties were in d...
- Denis Koroskin (8/13) Nov 18 2010 No, it's not. From my experience, a bigger issue is that
- Walter Bright (11/19) Nov 18 2010 I agree that's an issue. Currently, the only way to deal with this is on...
- Rainer Deyke (5/17) Nov 18 2010 4. Use an external preprocessor to generate the D1 and D2 code from the
- Walter Bright (4/6) Nov 18 2010 There's a 4th method I almost hesitate to mention, but you can use a com...
- Denis Koroskin (3/21) Nov 18 2010 All these are ugly.
- Walter Bright (2/3) Nov 18 2010 I agree.
- %u (5/19) Nov 18 2010 Wow, that sucks!!
- Bernard Helyer (4/24) Nov 19 2010 No. The contents of a version block is still parsed. If the D2 code
- Walter Bright (4/7) Nov 19 2010 Yes. Now, if the D1 compiler is supposed to successfully lex & parse (an...
- Andrew Wiley (7/16) Nov 19 2010 I'm a fan of parsing false version blocks, but it seems like there shoul...
- Walter Bright (2/8) Nov 19 2010 How can it skip it without knowing the grammar?
- Denis Koroskin (11/19) Nov 19 2010 #ifdef D_Version2
- Walter Bright (3/18) Nov 19 2010 Yes, I know it can be done with a text preprocessor.
- Denis Koroskin (4/18) Nov 19 2010 Once again, it's NOT a text preprocessor. It's NOT a separate parsing
- Andrew Wiley (7/15) Nov 19 2010 It's probably too much work for too little gain, but would it be possibl...
- Walter Bright (2/24) Nov 19 2010 It would still have to know about all the new tokens.
- torhu (10/17) Nov 19 2010 But current D 1 compilers are useless for most people anyway, until
- Walter Bright (3/13) Nov 19 2010 I understand. You can use a text preprocessor right now to do it, m4. It...
- %u (8/32) Nov 19 2010 Well, actually I said the compiler wouldn't look at it, so it wouldn't g...
- Steven Schveighoffer (8/17) Nov 18 2010 But string is not always what is desired. It depends on the library.
- Walter Bright (13/35) Nov 18 2010 That's where refactoring comes in. I haven't looked at the Tango code, b...
- Steven Schveighoffer (26/62) Nov 18 2010 Yes, Tango obviously works on Linux, so it does not modify string litera...
- Walter Bright (4/12) Nov 18 2010 If you know the string is unique, you can use cast(string) rather than ....
- Steven Schveighoffer (24/31) Nov 18 2010 class obj
- Jonathan M Davis (5/42) Nov 18 2010 And, of course, the other issue is the ideally, most code which uses str...
- Michel Fortin (14/32) Nov 18 2010 If you're only going to store the string as immutable, I think it's
- Andrei Alexandrescu (4/33) Nov 18 2010 Yes, I think that's the best idiom (I use it in Phobos in a number of
- Walter Bright (3/14) Nov 18 2010 I agree that it just seems like good class design to have obj.name be im...
- Fawzi Mohamed (32/32) Nov 18 2010 From the discussion it seems that defining something like:
- Walter Bright (4/38) Nov 18 2010 The problem with this approach is it requires the D1 compiler to be able...
- Fawzi Mohamed (16/52) Nov 18 2010 `);
- Walter Bright (2/17) Nov 18 2010 Just remove the 'scope'.
- Fawzi Mohamed (4/18) Nov 19 2010 removing scope causes heap allocation in D2 that I want to avoid.
- Sean Kelly (5/37) Nov 21 2010 Make them string mixins. I had druntime working on both D1 and D2 for a
- Steven Schveighoffer (15/47) Nov 19 2010 BTW, this doesn't work as well as you would think. The problem is with ...
- spir (8/16) Nov 18 2010 e =20
- Steven Schveighoffer (24/36) Nov 18 2010 It's the source of the problem for which inout was invented to solve. H...
- Jacob Carlborg (13/27) Nov 18 2010 The experience I have with this is the Orange library which supports
Is there any "porting" guide around in a wiki? If not a page where to share the best tricks would be nice "D1->D2 conversion tricks"? In the short term I don't think that going D2 only is really an option for me, so how feasible it is to keep the code base compatible to both D1 and D2? I know that one can define some templates (for example Const(T),....), and maybe use mixins, but how much uglier does the code become as result? I choose D to have cleaner code, I am not interested in loosing all that just to be D1 and D2, then I prefer to wait, and convert everything at once. Well that is about it... thanks Fawzi
Nov 18 2010
On 11/18/2010 7:51 PM, Fawzi Mohamed wrote:Is there any "porting" guide around in a wiki? If not a page where to share the best tricks would be nice "D1->D2 conversion tricks"? In the short term I don't think that going D2 only is really an option for me, so how feasible it is to keep the code base compatible to both D1 and D2? I know that one can define some templates (for example Const(T),....), and maybe use mixins, but how much uglier does the code become as result? I choose D to have cleaner code, I am not interested in loosing all that just to be D1 and D2, then I prefer to wait, and convert everything at once. Well that is about it... thanks FawziIn maintaining Derelict, which is nothing more than a simple collection of bindings to C libraries, I have had headaches keeping compatibility between D1/D2. It's nothing that has been difficult to solve, just ugly. If something as simple as a C-binding is uglified, I cringe at the thought of maintaining something more complex. It's going to get very ugly, very quickly. My attitude is that any future D projects I make available will be D2 only. I just don't think it's worth being compatible with both versions from a code maintenance perspective.
Nov 18 2010
On 18-nov-10, at 12:07, Mike Parker wrote:On 11/18/2010 7:51 PM, Fawzi Mohamed wrote:that was my feeling too, but I wanted some confirmation from people having actually done it. This just reinforces my choice of being D1 only for the moment...Is there any "porting" guide around in a wiki? If not a page where to share the best tricks would be nice "D1->D2 conversion tricks"? In the short term I don't think that going D2 only is really an option for me, so how feasible it is to keep the code base compatible to both D1 and D2? I know that one can define some templates (for example Const(T),....), and maybe use mixins, but how much uglier does the code become as result? I choose D to have cleaner code, I am not interested in loosing all that just to be D1 and D2, then I prefer to wait, and convert everything at once. Well that is about it... thanks FawziIn maintaining Derelict, which is nothing more than a simple collection of bindings to C libraries, I have had headaches keeping compatibility between D1/D2. It's nothing that has been difficult to solve, just ugly. If something as simple as a C-binding is uglified, I cringe at the thought of maintaining something more complex. It's going to get very ugly, very quickly. My attitude is that any future D projects I make available will be D2 only. I just don't think it's worth being compatible with both versions from a code maintenance perspective.
Nov 18 2010
Mike Parker wrote:On 11/18/2010 7:51 PM, Fawzi Mohamed wrote:In the short term I don't think that going D2 only is really an option for me, so how feasible it is to keep the code base compatible to both D1 and D2? I know that one can define some templates (for example Const(T),....), and maybe use mixins, but how much uglier does the code become as result? I choose D to have cleaner code, I am not interested in loosing all that just to be D1 and D2, then I prefer to wait, and convert everything at once.In maintaining Derelict, which is nothing more than a simple collection of bindings to C libraries, I have had headaches keeping compatibility between D1/D2. It's nothing that has been difficult to solve, just ugly. If something as simple as a C-binding is uglified, I cringe at the thought of maintaining something more complex. It's going to get very ugly, very quickly. My attitude is that any future D projects I make available will be D2 only. I just don't think it's worth being compatible with both versions from a code maintenance perspective.wxD had the same experience, with supporting Phobos/Tango and D1/D2. It's "possible", but leads to ugly/duplicated code and best avoided... For a C/C++-binding it's doable with liberal amounts of version(Tango) and version(D_Version2) but that quickly gets hideous and in the way. --anders PS. Adding three compilers and three build systems, it just exploded. It made even the two OS and the three UI look simple in comparison. OS: Windows, Unix (linux, darwin) UI: __WXMSW__, __WXGTK__, __WXMAC__
Nov 18 2010
On Thu, 18 Nov 2010 20:07:38 +0900 Mike Parker <aldacron gmail.com> wrote:On 11/18/2010 7:51 PM, Fawzi Mohamed wrote:t?Is there any "porting" guide around in a wiki? If not a page where to share the best tricks would be nice "D1->D2 conversion tricks"? In the short term I don't think that going D2 only is really an option for me, so how feasible it is to keep the code base compatible to both D1 and D2? I know that one can define some templates (for example Const(T),....), and maybe use mixins, but how much uglier does the code become as resul=I have read your blog post on the topic. This let me think that all true ne= eds for string mixins, like yours, are cases revealing plain language issue= s. denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.comI choose D to have cleaner code, I am not interested in loosing all that just to be D1 and D2, then I prefer to wait, and convert everything at once. Well that is about it... thanks Fawzi=20 In maintaining Derelict, which is nothing more than a simple collection=20 of bindings to C libraries, I have had headaches keeping compatibility=20 between D1/D2. It's nothing that has been difficult to solve, just ugly. =20 If something as simple as a C-binding is uglified, I cringe at the=20 thought of maintaining something more complex. It's going to get very=20 ugly, very quickly. My attitude is that any future D projects I make=20 available will be D2 only. I just don't think it's worth being=20 compatible with both versions from a code maintenance perspective.
Nov 18 2010
On Thu, 18 Nov 2010 05:51:32 -0500, Fawzi Mohamed <fawzi gmx.ch> wrote:Is there any "porting" guide around in a wiki? If not a page where to share the best tricks would be nice "D1->D2 conversion tricks"? In the short term I don't think that going D2 only is really an option for me, so how feasible it is to keep the code base compatible to both D1 and D2? I know that one can define some templates (for example Const(T),....), and maybe use mixins, but how much uglier does the code become as result? I choose D to have cleaner code, I am not interested in loosing all that just to be D1 and D2, then I prefer to wait, and convert everything at once.I have had two experiences moving code from D1 to D2. First, I tried to port Tango to D2, and found that backwards compatibility is not possible. So I was simply reorganizing the files. The hugest problem for going from D1 to D2 is string literals. Every place you use char[] has to be switched to const(char)[] or string. I came up with a set of rules for this: 1. if the 99% common case for string arguments will be string literals, then use string -- you never have to worry about duping. 2. if you intend to keep a copy of the string, but you aren't sure whether the string to be passed in will have mutable aliases, use const(char)[] and dup. 3. if you don't intend to keep a copy of the string, use const(char)[] to have the most flexibility. 4. if you intend on returning a portion of the string, well, currently you're SOL, but eventually inout will come into play here. For example, Tango's log package fell under rules 1 and 3 (most of the time you are creating log objects with string literals, and logging a message does not save the message anywhere). Many many cases fell under rule 4, which is one of the main reasons I gave up (inout still isn't ready). The second experience I had was porting dcollections to D2. Since dcollections has not many places for string literals, it was much easier to port. I don't think I made it backwards compatible, but it's very very similar to the original in terms of constancy. I've added a few features that only really make sense in D2 (ranges for one). Porting dcollections was quite easy, but with my experience trying to port Tango, I never tried to make it backwards compatible. My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible. -Steve
Nov 18 2010
Steven Schveighoffer wrote:My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings. Might I suggest: 1. Replace all occurrences of char[] with string. 2. Compile to find every place that mutable strings are used. 3. Refactor the code to clearly encapsulate where mutable strings are created and manipulated, and as the last step, cast them to string.
Nov 18 2010
On Thu, 18 Nov 2010 22:19:12 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Steven Schveighoffer wrote:No, it's not. From my experience, a bigger issue is that version (D2) { /* some D2 only code involving const etc */ } simply won't compile in D1, because even if it's D2-only the code needs to be correct D1 code as well.My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings.
Nov 18 2010
Denis Koroskin wrote:From my experience, a bigger issue is that version (D2) { /* some D2 only code involving const etc */ } simply won't compile in D1, because even if it's D2-only the code needs to be correct D1 code as well.I agree that's an issue. Currently, the only way to deal with this is one of: 1. Minimize the differences, and maintain two copies of the source code. Using the (rather fabulous) meld tool (available on Linux), the merging is pretty easy. I use meld all the time to, for example, merge differences in the code base between dmd1 and dmd2. 2. Isolate the code that is different into different files, which minimizes the work involved in (1). 3. Use string mixins and the token string literal form. I've also found that using the string alias rather than char[] does account for an awful lot of the differences.
Nov 18 2010
On 11/18/2010 12:39, Walter Bright wrote:I agree that's an issue. Currently, the only way to deal with this is one of: 1. Minimize the differences, and maintain two copies of the source code. Using the (rather fabulous) meld tool (available on Linux), the merging is pretty easy. I use meld all the time to, for example, merge differences in the code base between dmd1 and dmd2. 2. Isolate the code that is different into different files, which minimizes the work involved in (1). 3. Use string mixins and the token string literal form.4. Use an external preprocessor to generate the D1 and D2 code from the same source file. -- Rainer Deyke - rainerd eldwood.com
Nov 18 2010
Walter Bright wrote:I agree that's an issue. Currently, the only way to deal with this is one of:There's a 4th method I almost hesitate to mention, but you can use a common source code file, and a macro text preprocessor (such as m4) to generate the D1 and D2 versions from it.
Nov 18 2010
On Thu, 18 Nov 2010 22:39:31 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Denis Koroskin wrote:All these are ugly.From my experience, a bigger issue is that version (D2) { /* some D2 only code involving const etc */ } simply won't compile in D1, because even if it's D2-only the code needs to be correct D1 code as well.I agree that's an issue. Currently, the only way to deal with this is one of: 1. Minimize the differences, and maintain two copies of the source code. Using the (rather fabulous) meld tool (available on Linux), the merging is pretty easy. I use meld all the time to, for example, merge differences in the code base between dmd1 and dmd2. 2. Isolate the code that is different into different files, which minimizes the work involved in (1). 3. Use string mixins and the token string literal form. I've also found that using the string alias rather than char[] does account for an awful lot of the differences.
Nov 18 2010
Denis Koroskin wrote:All these are ugly.I agree.
Nov 18 2010
== Quote from Denis Koroskin (2korden gmail.com)'s articleOn Thu, 18 Nov 2010 22:19:12 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Wow, that sucks!! Can't this simply be mended with special D_DMD1 and D_DMD2 (D_DMD3 :) version identifiers? If the compiler version isn't the same then it won't look at the code.Steven Schveighoffer wrote:No, it's not. From my experience, a bigger issue is that version (D2) { /* some D2 only code involving const etc */ } simply won't compile in D1, because even if it's D2-only the code needs to be correct D1 code as well.My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings.
Nov 18 2010
On Thu, 18 Nov 2010 19:58:42 +0000, %u wrote:== Quote from Denis Koroskin (2korden gmail.com)'s articleNo. The contents of a version block is still parsed. If the D2 code doesn't _parse_ as valid D1, then the D1 compiler will reject it, even if the version block will not be processed further than that.On Thu, 18 Nov 2010 22:19:12 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Wow, that sucks!! Can't this simply be mended with special D_DMD1 and D_DMD2 (D_DMD3 :) version identifiers? If the compiler version isn't the same then it won't look at the code.Steven Schveighoffer wrote:No, it's not. From my experience, a bigger issue is that version (D2) { /* some D2 only code involving const etc */ } simply won't compile in D1, because even if it's D2-only the code needs to be correct D1 code as well.My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings.
Nov 19 2010
Bernard Helyer wrote:No. The contents of a version block is still parsed. If the D2 code doesn't _parse_ as valid D1, then the D1 compiler will reject it, even if the version block will not be processed further than that.Yes. Now, if the D1 compiler is supposed to successfully lex & parse (and then ignore) false version blocks, it becomes impossible for a D1 compiler to be stable. It will have to track every future D language variant, missteps and all.
Nov 19 2010
On Fri, Nov 19, 2010 at 6:18 PM, Walter Bright <newshound2 digitalmars.com>wrote:Bernard Helyer wrote:I'm a fan of parsing false version blocks, but it seems like there should be some sort of exception for version blocks for a different version of the language. Could they just be skipped with a warning that an unknown version of the language is in use? It's somewhat hackish, but to me, that seems better than parsing it and giving an error because of syntax changes.No. The contents of a version block is still parsed. If the D2 code doesn't _parse_ as valid D1, then the D1 compiler will reject it, even if the version block will not be processed further than that.Yes. Now, if the D1 compiler is supposed to successfully lex & parse (and then ignore) false version blocks, it becomes impossible for a D1 compiler to be stable. It will have to track every future D language variant, missteps and all.
Nov 19 2010
Andrew Wiley wrote:I'm a fan of parsing false version blocks, but it seems like there should be some sort of exception for version blocks for a different version of the language. Could they just be skipped with a warning that an unknown version of the language is in use? It's somewhat hackish, but to me, that seems better than parsing it and giving an error because of syntax changes.How can it skip it without knowing the grammar?
Nov 19 2010
On Sat, 20 Nov 2010 04:14:05 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Andrew Wiley wrote:#ifdef D_Version2 ... #else ... #endif D has a precedence of having preprocessor macros (e.g #line) even if it's not a full-fledged preprocessor, and I don't see why it couldn't adapt the same solution.I'm a fan of parsing false version blocks, but it seems like there should be some sort of exception for version blocks for a different version of the language. Could they just be skipped with a warning that an unknown version of the language is in use? It's somewhat hackish, but to me, that seems better than parsing it and giving an error because of syntax changes.How can it skip it without knowing the grammar?
Nov 19 2010
Denis Koroskin wrote:On Sat, 20 Nov 2010 04:14:05 +0300, Walter BrightYes, I know it can be done with a text preprocessor. [[ Queue run away screaming ]]How can it skip it without knowing the grammar?#ifdef D_Version2 ... #else ... #endif D has a precedence of having preprocessor macros (e.g #line) even if it's not a full-fledged preprocessor, and I don't see why it couldn't adapt the same solution.
Nov 19 2010
On Sat, 20 Nov 2010 04:33:36 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Denis Koroskin wrote:Once again, it's NOT a text preprocessor. It's NOT a separate parsing phase (unlike C/C++). It's done at parsing level, similarly to comments.On Sat, 20 Nov 2010 04:14:05 +0300, Walter BrightYes, I know it can be done with a text preprocessor. [[ Queue run away screaming ]]How can it skip it without knowing the grammar?#ifdef D_Version2 ... #else ... #endif D has a precedence of having preprocessor macros (e.g #line) even if it's not a full-fledged preprocessor, and I don't see why it couldn't adapt the same solution.
Nov 19 2010
On Fri, Nov 19, 2010 at 7:14 PM, Walter Bright <newshound2 digitalmars.com>wrote:Andrew Wiley wrote:It's probably too much work for too little gain, but would it be possible to basically just scan for matching braces until the version block ends? If they always come in matching pairs (or at least they *should*), you could scan for them and ignore everything else until you find the end of the block.I'm a fan of parsing false version blocks, but it seems like there should be some sort of exception for version blocks for a different version of the language. Could they just be skipped with a warning that an unknown version of the language is in use? It's somewhat hackish, but to me, that seems better than parsing it and giving an error because of syntax changes.How can it skip it without knowing the grammar?
Nov 19 2010
Andrew Wiley wrote:On Fri, Nov 19, 2010 at 7:14 PM, Walter Bright <newshound2 digitalmars.com <mailto:newshound2 digitalmars.com>> wrote: Andrew Wiley wrote: I'm a fan of parsing false version blocks, but it seems like there should be some sort of exception for version blocks for a different version of the language. Could they just be skipped with a warning that an unknown version of the language is in use? It's somewhat hackish, but to me, that seems better than parsing it and giving an error because of syntax changes. How can it skip it without knowing the grammar? It's probably too much work for too little gain, but would it be possible to basically just scan for matching braces until the version block ends? If they always come in matching pairs (or at least they *should*), you could scan for them and ignore everything else until you find the end of the block.It would still have to know about all the new tokens.
Nov 19 2010
On 20.11.2010 01:18, Walter Bright wrote:Bernard Helyer wrote:But current D 1 compilers are useless for most people anyway, until Tango catches up. And in any case, if you upgrade, you often get some nice new bugs in addition to the fixes, so upgrading is often not worth the hassle. But making it easier to go from D 1 to D 2 would still be a big gain, so why not do that? No one cares about "stability" of D 1 compilers. If you want "stability", it's very simple. You just don't upgrade. Tango is dying, and D 1 with it. We don't want "stability", we want a viable way to move to D 2.No. The contents of a version block is still parsed. If the D2 code doesn't _parse_ as valid D1, then the D1 compiler will reject it, even if the version block will not be processed further than that.Yes. Now, if the D1 compiler is supposed to successfully lex& parse (and then ignore) false version blocks, it becomes impossible for a D1 compiler to be stable. It will have to track every future D language variant, missteps and all.
Nov 19 2010
torhu wrote:But current D 1 compilers are useless for most people anyway, until Tango catches up. And in any case, if you upgrade, you often get some nice new bugs in addition to the fixes, so upgrading is often not worth the hassle. But making it easier to go from D 1 to D 2 would still be a big gain, so why not do that? No one cares about "stability" of D 1 compilers. If you want "stability", it's very simple. You just don't upgrade. Tango is dying, and D 1 with it. We don't want "stability", we want a viable way to move to D 2.I understand. You can use a text preprocessor right now to do it, m4. It's quick, easy, and works.
Nov 19 2010
== Quote from Bernard Helyer (b.helyer gmail.com)'s articleOn Thu, 18 Nov 2010 19:58:42 +0000, %u wrote:Well, actually I said the compiler wouldn't look at it, so it wouldn't get parsed as well. What you should have said is that is very difficult to not parse a piece of code and still know its boundaries ;) So how can the compiler be sure a version block ends without actually parsing the text as code. A single line version would do :)== Quote from Denis Koroskin (2korden gmail.com)'s articleNo. The contents of a version block is still parsed. If the D2 code doesn't _parse_ as valid D1, then the D1 compiler will reject it, even if the version block will not be processed further than that.On Thu, 18 Nov 2010 22:19:12 +0300, Walter Bright <newshound2 digitalmars.com> wrote:Wow, that sucks!! Can't this simply be mended with special D_DMD1 and D_DMD2 (D_DMD3 :) version identifiers? If the compiler version isn't the same then it won't look at the code.Steven Schveighoffer wrote:No, it's not. From my experience, a bigger issue is that version (D2) { /* some D2 only code involving const etc */ } simply won't compile in D1, because even if it's D2-only the code needs to be correct D1 code as well.My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings.
Nov 19 2010
On Thu, 18 Nov 2010 14:19:12 -0500, Walter Bright <newshound2 digitalmars.com> wrote:Steven Schveighoffer wrote:But string is not always what is desired. It depends on the library. Tango uses mutable arrays everywhere because it prefers not to use the heap as much as possible. For example, something might take a char[] buffer, and a function may pass in a stack-allocated array for the buffer. Changing this to string is useless. -SteveMy recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings. Might I suggest: 1. Replace all occurrences of char[] with string. 2. Compile to find every place that mutable strings are used. 3. Refactor the code to clearly encapsulate where mutable strings are created and manipulated, and as the last step, cast them to string.
Nov 18 2010
Steven Schveighoffer wrote:On Thu, 18 Nov 2010 14:19:12 -0500, Walter Bright <newshound2 digitalmars.com> wrote:Right.Steven Schveighoffer wrote:But string is not always what is desired. It depends on the library.My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings. Might I suggest: 1. Replace all occurrences of char[] with string. 2. Compile to find every place that mutable strings are used. 3. Refactor the code to clearly encapsulate where mutable strings are created and manipulated, and as the last step, cast them to string.Tango uses mutable arrays everywhere because it prefers not to use the heap as much as possible. For example, something might take a char[] buffer, and a function may pass in a stack-allocated array for the buffer. Changing this to string is useless.That's where refactoring comes in. I haven't looked at the Tango code, but there's a big difference between "using mutable arrays everywhere" and actually needing to mutate them. Only the latter needs to be char[], and the boundaries between the two are bridged with a .idup or a cast(string). If Tango is relying on string literals to be char[], i.e. be mutable, this is a serious bug and will cause seg faults. So, this issue must have already been dealt with in Tango, and codifying string literal types as "string" rather than "char[]" should not negatively impact it. And lastly, if there are APIs in tango that accept char[] and leave the caller perplexed about whether the API will mutate the string or not, that is something that using string for the non-mutating ones nicely resolves.
Nov 18 2010
On Thu, 18 Nov 2010 14:50:06 -0500, Walter Bright <newshound2 digitalmars.com> wrote:Steven Schveighoffer wrote:Yes, Tango obviously works on Linux, so it does not modify string literals (as much as it can, you can't really prevent a user from passing in a string literal to sort for example). However, the char[] type accepts both string literals and mutable buffers. In some cases, those strings are stored for later use, in some they are modified (obviously one has to avoid passing in literals for that), and in some they are just used temporarily. The differences require you to change the argument type to string or const(char)[], and possibly to overload the function. The rules I outlined were what I was going by in order to port D1 code. peppering idup everywhere, you have created a conflict with the main goal. Refactoring in these cases isn't quite as simple as you say, and it certainly won't be backwards compatible. In general, I tried to make Tango for D2 as const aware as possible (it's a standard lib, and shouldn't require people to ignore const), and found all sorts of issues with it (const). The main one that made me quit was the problem that inout is supposed to solve. For someone just trying to "get something working" on D2 from a D1 base, the biggest issue is to deal with string literals since you are forced to deal with const at that point. Other than that, you can usually safely leave everything non-const-ified. That's all I was saying. And I don't think it's worth the effort to try and achieve backwards compatibility with D1. -SteveOn Thu, 18 Nov 2010 14:19:12 -0500, Walter Bright <newshound2 digitalmars.com> wrote:Right.Steven Schveighoffer wrote:But string is not always what is desired. It depends on the library.My recommendation -- when you are ready, switch wholly to D2. Don't bother with compatibility, it's just not possible.From what you wrote, it appears that most of the difficulties were in dealing with strings. Might I suggest: 1. Replace all occurrences of char[] with string. 2. Compile to find every place that mutable strings are used. 3. Refactor the code to clearly encapsulate where mutable strings are created and manipulated, and as the last step, cast them to string.Tango uses mutable arrays everywhere because it prefers not to use the heap as much as possible. For example, something might take a char[] buffer, and a function may pass in a stack-allocated array for the buffer. Changing this to string is useless.That's where refactoring comes in. I haven't looked at the Tango code, but there's a big difference between "using mutable arrays everywhere" and actually needing to mutate them. Only the latter needs to be char[], and the boundaries between the two are bridged with a .idup or a cast(string). If Tango is relying on string literals to be char[], i.e. be mutable, this is a serious bug and will cause seg faults. So, this issue must have already been dealt with in Tango, and codifying string literal types as "string" rather than "char[]" should not negatively impact it. And lastly, if there are APIs in tango that accept char[] and leave the caller perplexed about whether the API will mutate the string or not, that is something that using string for the non-mutating ones nicely resolves.
Nov 18 2010
Steven Schveighoffer wrote:By peppering idup everywhere, you have created a conflict with the main goal. Refactoring in these cases isn't quite as simple as you say, and it certainly won't be backwards compatible.If you know the string is unique, you can use cast(string) rather than .idup, and no allocations will be done.In general, I tried to make Tango for D2 as const aware as possible (it's a standard lib, and shouldn't require people to ignore const), and found all sorts of issues with it (const). The main one that made me quit was the problem that inout is supposed to solve.Right; inout is intended to fix that.
Nov 18 2010
On Thu, 18 Nov 2010 15:49:10 -0500, Walter Bright <newshound2 digitalmars.com> wrote:Steven Schveighoffer wrote:class obj { char[] name; this(char[] name) { this.name = name;} } What to do there? If name is typically a string literal, then string is appropriate. But if there are cases where a name is passed in via a char[] allocated on the heap, then const(char) makes sense. However, you really don't want the name to change at some other point, so if it's not immutable it should idup the name. It's not easy to make this code both safe and efficient. In the end, I think in cases like this I did something like: string name; this(const(char)[] name) { this.name = name.idup;} this(string name) {this.name = name;} These are the kinds of cases I was talking about. They require more than simple "search and replace" refactoring. In reality, the original code is unsafe and flawed, but you want to try and support any existing code that uses it as well as possible. There was also some anti-D2 sentiments to overcome when trying to get Tango to accept a D2 port, so I wanted to try and make the port as painless as possible. -SteveBy peppering idup everywhere, you have created a conflict with the main goal. Refactoring in these cases isn't quite as simple as you say, and it certainly won't be backwards compatible.If you know the string is unique, you can use cast(string) rather than .idup, and no allocations will be done.
Nov 18 2010
On Thursday 18 November 2010 13:02:40 Steven Schveighoffer wrote:On Thu, 18 Nov 2010 15:49:10 -0500, Walter Bright <newshound2 digitalmars.com> wrote:And, of course, the other issue is the ideally, most code which uses strings would work with all of the various string types, so if you want to go all the way, you have to templatize up the wazoo on string functions. - Jonathan M DavisSteven Schveighoffer wrote:class obj { char[] name; this(char[] name) { this.name = name;} } What to do there? If name is typically a string literal, then string is appropriate. But if there are cases where a name is passed in via a char[] allocated on the heap, then const(char) makes sense. However, you really don't want the name to change at some other point, so if it's not immutable it should idup the name. It's not easy to make this code both safe and efficient. In the end, I think in cases like this I did something like: string name; this(const(char)[] name) { this.name = name.idup;} this(string name) {this.name = name;} These are the kinds of cases I was talking about. They require more than simple "search and replace" refactoring. In reality, the original code is unsafe and flawed, but you want to try and support any existing code that uses it as well as possible. There was also some anti-D2 sentiments to overcome when trying to get Tango to accept a D2 port, so I wanted to try and make the port as painless as possible.By peppering idup everywhere, you have created a conflict with the main goal. Refactoring in these cases isn't quite as simple as you say, and it certainly won't be backwards compatible.If you know the string is unique, you can use cast(string) rather than .idup, and no allocations will be done.
Nov 18 2010
On 2010-11-18 16:02:40 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:class obj { char[] name; this(char[] name) { this.name = name;} } What to do there? If name is typically a string literal, then string is appropriate. But if there are cases where a name is passed in via a char[] allocated on the heap, then const(char) makes sense. However, you really don't want the name to change at some other point, so if it's not immutable it should idup the name. It's not easy to make this code both safe and efficient. In the end, I think in cases like this I did something like: string name; this(const(char)[] name) { this.name = name.idup;} this(string name) {this.name = name;}If you're only going to store the string as immutable, I think it's better to accept it only as immutable in the constructor. This way you pass the responsibility of iduping the string to the caller and this might reveal optimization opportunities. For instance: obj[100] versions; foreach (i; 1..100) versions[i] = new obj(name.idup); See how it the inefficiency obvious when the caller is the one calling idup? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 18 2010
On 11/18/10 1:50 PM, Michel Fortin wrote:On 2010-11-18 16:02:40 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:Yes, I think that's the best idiom (I use it in Phobos in a number of places). It's safe and as efficient as it could. Andreiclass obj { char[] name; this(char[] name) { this.name = name;} } What to do there? If name is typically a string literal, then string is appropriate. But if there are cases where a name is passed in via a char[] allocated on the heap, then const(char) makes sense. However, you really don't want the name to change at some other point, so if it's not immutable it should idup the name. It's not easy to make this code both safe and efficient. In the end, I think in cases like this I did something like: string name; this(const(char)[] name) { this.name = name.idup;} this(string name) {this.name = name;}If you're only going to store the string as immutable, I think it's better to accept it only as immutable in the constructor. This way you pass the responsibility of iduping the string to the caller and this might reveal optimization opportunities. For instance: obj[100] versions; foreach (i; 1..100) versions[i] = new obj(name.idup); See how it the inefficiency obvious when the caller is the one calling idup?
Nov 18 2010
Andrei Alexandrescu wrote:On 11/18/10 1:50 PM, Michel Fortin wrote:I agree that it just seems like good class design to have obj.name be immutable, and pass the responsibility for making it so up the call chain to the constructor.On 2010-11-18 16:02:40 -0500, "Steven Schveighoffer" <schveiguy yahoo.com> said:class obj { char[] name; this(char[] name) { this.name = name;} } What to do there?
Nov 18 2010
From the discussion it seems that defining something like: version(D_Version2){ template Const(T){ alias const(T) Const; } template Immutable(T){ alias immutable(T) Immutable; } immutable(T) Idup(T)(T val){ return val.idup; } alias const(char)[] cstring; } else { template Const(T){ alias T Const; } template Immutable(T){ alias T Immutable; } T Idup(T)(T val){ return val.dup; } alias char[] string; alias char[] cstring; } could help a lot later one can simply replace Const! with const and Immutable! with immutable, Idup replacement is more complicated, but doable What is not so clear is how to cope with scope, because I have lot of delegates around that will need it. For it a preprocessing step might really be the best thing, or someone knows a smart way to cope with that?
Nov 18 2010
Fawzi Mohamed wrote:From the discussion it seems that defining something like: version(D_Version2){ template Const(T){ alias const(T) Const; } template Immutable(T){ alias immutable(T) Immutable; } immutable(T) Idup(T)(T val){ return val.idup; } alias const(char)[] cstring; } else { template Const(T){ alias T Const; } template Immutable(T){ alias T Immutable; } T Idup(T)(T val){ return val.dup; } alias char[] string; alias char[] cstring; } could help a lot later one can simply replace Const! with const and Immutable! with immutable, Idup replacement is more complicated, but doableThe problem with this approach is it requires the D1 compiler to be able to parse D2 syntax, including recognizing all of D2's keywords.What is not so clear is how to cope with scope, because I have lot of delegates around that will need it. For it a preprocessing step might really be the best thing, or someone knows a smart way to cope with that?Not sure what the issue is.
Nov 18 2010
On 19-nov-10, at 08:08, Walter Bright wrote:Fawzi Mohamed wrote:mixin(`From the discussion it seems that defining something like: version(D_Version2){`);template Const(T){ alias const(T) Const; } template Immutable(T){ alias immutable(T) Immutable; } immutable(T) Idup(T)(T val){ return val.idup; } alias const(char)[] cstring;yes you are right, I meant to put a mixin ther but then I forgot it.} else { template Const(T){ alias T Const; } template Immutable(T){ alias T Immutable; } T Idup(T)(T val){ return val.dup; } alias char[] string; alias char[] cstring; } could help a lot later one can simply replace Const! with const and Immutable! with immutable, Idup replacement is more complicated, but doableThe problem with this approach is it requires the D1 compiler to be able to parse D2 syntax, including recognizing all of D2's keywords.I don't find a valid D1 expression to put in place of scope, or to somehow hide it, i.e. how do you write something like module t; void f(scope void delegate() action){ action(); } void main(){ f(scope delegate(){ printf("bla\n"); }); } so that it is valid D1 and D2?What is not so clear is how to cope with scope, because I have lot of delegates around that will need it. For it a preprocessing step might really be the best thing, or someone knows a smart way to cope with that?Not sure what the issue is.
Nov 18 2010
Fawzi Mohamed wrote:I don't find a valid D1 expression to put in place of scope, or to somehow hide it, i.e. how do you write something like module t; void f(scope void delegate() action){ action(); } void main(){ f(scope delegate(){ printf("bla\n"); }); } so that it is valid D1 and D2?Just remove the 'scope'.
Nov 18 2010
On 19-nov-10, at 08:44, Walter Bright wrote:Fawzi Mohamed wrote:removing scope causes heap allocation in D2 that I want to avoid. Still maybe you are right, I will use /+scope+/, so that one has something working, and easily go to the efficient D2 versionI don't find a valid D1 expression to put in place of scope, or to somehow hide it, i.e. how do you write something like module t; void f(scope void delegate() action){ action(); } void main(){ f(scope delegate(){ printf("bla\n"); }); } so that it is valid D1 and D2?Just remove the 'scope'.
Nov 19 2010
Walter Bright <newshound2 digitalmars.com> wrote:Fawzi Mohamed wrote:Make them string mixins. I had druntime working on both D1 and D2 for a while and did it with a mix of aliases, string mixins, and "in" for const args. It worked, but ended up being worse than simply maintaining two codebases.From the discussion it seems that defining something like:The problem with this approach is it requires the D1 compiler to be able to parse D2 syntax, including recognizing all of D2's keywords.version(D_Version2){template Const(T){ alias const(T) Const; } template Immutable(T){ alias immutable(T) Immutable; } immutable(T) Idup(T)(T val){ return val.idup; } alias const(char)[] cstring; } else { template Const(T){ alias T Const; } template Immutable(T){ alias T Immutable; } T Idup(T)(T val){ return val.dup; } alias char[] string; alias char[] cstring; }could help a lotlater one can simply replace Const! with const and Immutable! with > immutable, Idup replacement is more complicated, but doable
Nov 21 2010
On Fri, 19 Nov 2010 01:47:27 -0500, Fawzi Mohamed <fawzi gmx.ch> wrote:From the discussion it seems that defining something like: version(D_Version2){ template Const(T){ alias const(T) Const; } template Immutable(T){ alias immutable(T) Immutable; } immutable(T) Idup(T)(T val){ return val.idup; } alias const(char)[] cstring; } else { template Const(T){ alias T Const; } template Immutable(T){ alias T Immutable; } T Idup(T)(T val){ return val.dup; } alias char[] string; alias char[] cstring; } could help a lot later one can simply replace Const! with const and Immutable! with immutable, Idup replacement is more complicated, but doable What is not so clear is how to cope with scope, because I have lot of delegates around that will need it. For it a preprocessing step might really be the best thing, or someone knows a smart way to cope with that?BTW, this doesn't work as well as you would think. The problem is with IFTI. For example, IFTI can handle this: void foo(T)(const(T) arg) but cannot handle this: void foo(T)(Const!(T) arg) It was one of the things we were trying to do in Tango for D2, we had aliases for const/immutable/mutable strings templated on character type, but all of Tango's IFTI usage failed to compile. It's a really old bug I filed: http://d.puremagic.com/issues/show_bug.cgi?id=1653 which was superseded by: http://d.puremagic.com/issues/show_bug.cgi?id=1807 -Steve
Nov 19 2010
On Thu, 18 Nov 2010 07:34:01 -0500 "Steven Schveighoffer" <schveiguy yahoo.com> wrote:4. if you intend on returning a portion of the string, well, currently =20 you're SOL, but eventually inout will come into play here. =20 For example, Tango's log package fell under rules 1 and 3 (most of the =20 time you are creating log objects with string literals, and logging a =20 message does not save the message anywhere). Many many cases fell under rule 4, which is one of the main reasons I gav=e =20up (inout still isn't ready).I don't understand the issue here. Why don't slices do the job? Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Nov 18 2010
On Thu, 18 Nov 2010 14:43:17 -0500, spir <denis.spir gmail.com> wrote:On Thu, 18 Nov 2010 07:34:01 -0500 "Steven Schveighoffer" <schveiguy yahoo.com> wrote:It's the source of the problem for which inout was invented to solve. How do you specify a function that says "I won't molest the data" but returns a portion of that data? Simplest example is strstr: const(char)[] strstr(const(char)[] haystack, const(char)[] needle) Notice that the return value *must* be const(char)[]. But what if you have a char[]? All of a sudden, you can't modify that data which was returned to you. In essence, strstr modified your const contract with your data. The useful idiom: str = strstr(str, "hello"); doesn't work, because you can't reassign the data. A solution is to have 3 overloads, one for each constancy type, but the mutable version doesn't guarantee it won't modify the data, and you have to repeat the exact same code. Another solution is to use a template, but that also has the same problem that it doesn't prevent the mutable version from modifying the data, plus does not indicate that you can use const data. In addition, templates cannot be virtual functions. But inout solves this in a different way that does exactly what we want. If you have read TDPL, then it should explain how inout is *supposed* to work, or you can read DIP2 (http://prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP2). inout was chosen instead of vconst to allow reusing essentially a dead keyword. -Steve4. if you intend on returning a portion of the string, well, currently you're SOL, but eventually inout will come into play here. For example, Tango's log package fell under rules 1 and 3 (most of the time you are creating log objects with string literals, and logging a message does not save the message anywhere). Many many cases fell under rule 4, which is one of the main reasons I gave up (inout still isn't ready).I don't understand the issue here. Why don't slices do the job?
Nov 18 2010
On 2010-11-18 11:51, Fawzi Mohamed wrote:Is there any "porting" guide around in a wiki? If not a page where to share the best tricks would be nice "D1->D2 conversion tricks"? In the short term I don't think that going D2 only is really an option for me, so how feasible it is to keep the code base compatible to both D1 and D2? I know that one can define some templates (for example Const(T),....), and maybe use mixins, but how much uglier does the code become as result? I choose D to have cleaner code, I am not interested in loosing all that just to be D1 and D2, then I prefer to wait, and convert everything at once. Well that is about it... thanks FawziThe experience I have with this is the Orange library which supports both D1 (Tango) and D2. I say this: it can sometimes be surprisingly easy and sometimes a PITA. Depending on what you do you can get a long way with aliases, I have for quite a long time now been using a string alias in D1 (I just like it better than char[]). For some things you will have to use string mixins and after some testing with Derelict2 we found out that it's better to use few string mixins containing a lot of code than many string mixins containing just some code. Otherwise the compile time will increase, sometimes quite a lot. -- /Jacob Carlborg
Nov 18 2010