February 07, 2007
BCS wrote:
> Walter Bright wrote:
>> The right way to do versions that cut across multiple files is to abstract the versioning into an API, and implement the different versions in different modules.
> What about cases where 90% of the code is identical but small bits and peaces are different? If I understand correctly, to do what you suggest would requirer that those bits be put in functions and have several versions of the function somewhere else. This could be a problem in several ways
> 
> ===Tiny bits of code would requirer tiny functions that would hide what is going on.

Yes, it would require tiny functions, though I don't agree they hide what is going on. Presumably a descriptive name would be used for it. One of the nice things about it is that porting the code requires generating a new module with the right implementations in it, which is a lot easier than going through checking all the #ifdef's (yes, I know one shouldn't have to do that, but in practice you ALWAYS have to because the macros always get misapplied, and often get forgotten to even apply). With an API, it is difficult to forget to use it, and difficult to use the wrong macro.

For example, when writing portable C code, one is often faced with the macros:
	__GNUC__	for the Gnu compiler
	linux		for the host operating system
	TARGET_LINUX	for the target operating system we're cross compiling for

I can't even count the number of times __GNUC__ was being used to decide whether we're compiling for windows or linux, or the host operating system was confused with the target:

#if __GNUC__
#include <pthreads.h>
#else
#include <windows.h>
#endif

AAAARRRRRGGGGGHHHHH!!! That, my friends, is evil.

Now, if one abstracted away what one was *doing* with threads, then one just does:

import mythreadapi;

and provide a different implementation of mythreadapi for windows or linux. It's a LOT harder to screw that up.

> version(RowMajor)
>     x = table[i][j];
> else // RowMinor
>     x = table[j][i];

int GetRow(i,j) { return table[i][j]; }

The function gets inlined.

> ====Empty else cases would result in do nothing functions:
> 
> version(StrongChecks)
> {
>     if(foo) ...
>     if(bar) ...
>     ...
> }
> //empty else
> 
> ====You can't break across function calls
> 
> switch(i)
> {
>     case 1:
>         version(Baz)
>             if(baz) break;
>         else
>             break;
>     case 2:
> 
>     ...// lots of un versioned code
> }

	if (globalversion.baz && baz)
		break;

> ====lots of version combinations
> 
> version(Foo) i = foo(i);
> version(Boo) i = boo(i);
> version(Fig) i = fig(i);
> version(Baz) i = baz(i);
> version(Bar) i = bar(i);    //32 options???

i = abc(i);	// a different abc is implemented for each version.

> 
> Are these valid concerns? Am I misunderstanding what you said?

They are valid concerns, you're just used to thinking in terms of the C preprocessor.
February 07, 2007
BCS wrote:
> Hasan Aljudy wrote:
> 
>>
>> Why is that evil? I think it's actually a great idea. "versions" are a sort of configuration that determines which code should be compiled and which code shouldn't. Storing this configuration in a separate file makes sense to me.
>>
> 
> rename versions.txt to versions.d
> 
> and use
> 
> import versions;
> 
> same effect, less confusion

No, versions defined in an import do NOT affect the importer.
February 07, 2007
Yauheni Akhotnikau wrote:
> Do you think this task can be done with D templates at complile time?

Yes, that's exactly the intent. If this can't be made to work, we'll fix D so it can.
February 07, 2007
renoX wrote:
> Pragma a écrit :
>> BLS wrote:
>>> Pragma schrieb:
>>>> BLS wrote:
>>> I can imagine the following scenario : D Compiler is calling a
>>> Translator, a modified Enki f.i. to translate a Domain Specific Language
>>> into D ... strange
>>
>> I've thought about that too- much like BCS's work.  The only thing 
> 
> Enki?
> BCS?
> 
> Could you avoid mysterious references?

Enki: http://www.dsource.org/projects/ddl/wiki/Enki

BCS is a poster in these newsgroups. He's been mentioning a project of his called dparser lately: http://www.dsource.org/projects/scrapple/browser/trunk/dparser/dparse.d
February 07, 2007
renoX wrote:
> Pragma a écrit :
> 
>> BLS wrote:
>>
>>> Pragma schrieb:
>>>
>>>> BLS wrote:
>>>
>>> I can imagine the following scenario : D Compiler is calling a
>>> Translator, a modified Enki f.i. to translate a Domain Specific Language
>>> into D ... strange
>>
>>
>> I've thought about that too- much like BCS's work.  The only thing 
> 
> 
> Enki?

http://www.dsource.org/projects/ddl/browser/trunk/enki
written by Pragma

> BCS?

http://www.dsource.org/projects/scrapple/browser/trunk/dparser/dparse.d
written by me (BCS)

> 
> Could you avoid mysterious references?
> 
> Regards,
> renoX
February 07, 2007
BCS wrote:
> Hasan Aljudy wrote:
> 
>>
>> Why is that evil? I think it's actually a great idea. "versions" are a sort of configuration that determines which code should be compiled and which code shouldn't. Storing this configuration in a separate file makes sense to me.
>>
> 
> rename versions.txt to versions.d
> 
> and use
> 
> import versions;
> 
> same effect, less confusion

It doesn't work like that:
-----
urxae@urxae:~/tmp$ cat test.d
import std.stdio;

import test2;

void main() {
    version(Foo)
        writefln("version=Foo");
    else
        writefln("Not version=Foo");
}
urxae@urxae:~/tmp$ cat test2.d
version=Foo;
urxae@urxae:~/tmp$ dmd test.d test2.d -oftest && ./test
gcc test.o test2.o -o test -m32 -lphobos -lpthread -lm -Xlinker -L/home/urxae/opt/dmd/lib
Not version=Foo
-----

Version specifications ("version=X" lines) don't get imported.
February 07, 2007
janderson wrote:
> I'm not familiar with forth.  Can you provide some examples?  Does it allow partial macro definitions.  Can you apply string operations on them at compile time?

Forth is not a traditional compiler that generates executables from source files. A Forth system includes a built-in interpreter and compiler (and most systems have an assembler too).

Source definitions are compiled and linked to the internal dictionary i.e. you extend the system itself. You can not only create application programms, but you can also easily add new features to the compiler e.g. new compiler commands.

Macro functions at compile-time are only a small exercise for a Forth programmer. You mark the last defined function IMMEDIATE, and the next time the function is used it is executed _at_compile-time_ !

Please note, Forth and D are playing in different leagues. But you don't always have large object-oriented applications. For the more "bare metal" stuff Forth is flexibler than D. If you never had contact with Forth you will probably find it rather strange: it is a stack-based language and uses post-fix annotation like HP calculators.

Andreas
February 07, 2007
Walter Bright wrote:
> 
> #if __GNUC__
> #include <pthreads.h>
> #else
> #include <windows.h>
> #endif
> 
> AAAARRRRRGGGGGHHHHH!!! That, my friends, is evil.

agreed.

> BCS wrote:
>>
>> version(RowMajor)
>>     x = table[i][j];
>> else // RowMinor
>>     x = table[j][i];
> 
> 
> int GetRow(i,j) { return table[i][j]; }
> 
> The function gets inlined.

The equivalent of this doesn't

version(RowMajor)
{
    x = 0
    for(i=0;i<max;i++)
	x+=table[i][j];
}
else // RowMinor
{
    x = 0
    for(i=0;i<max;i++)
	x+=table[j][i];
}

and IIRC this doesn't ether

version(X86) // avoid overflow in midpoint calculations
{
	asm
	{
		MOV low EAX;
		ADD EAX hi;
		RCR EAX 1;
		MOV EAX mid;
	}
}
else
{
	mid = (hi-low)/2 + low;
}

>>
>> ====You can't break across function calls
>>
>> switch(i)
>> {
>>     case 1:
>>         version(Baz)
>>             if(baz) break;
>>         else
>>             break;
>>     case 2:
>>
>>     ...// lots of un versioned code
>> }
> 
>     if (globalversion.baz && baz)
>         break;

The point is to have all of the versioning done by the time you link, that leaves a runtime check for version info.

> 
>> ====lots of version combinations
>>
>> version(Foo) i = foo(i);
>> version(Boo) i = boo(i);
>> version(Fig) i = fig(i);
>> version(Baz) i = baz(i);
>> version(Bar) i = bar(i);    //32 options???
> 
> 
> i = abc(i);    // a different abc is implemented for each version.

All 32 possibilities??? What if there are 16 independent versions? that's 64K functions! And no that is not an unlikely case, say "i" is a parse tree and we want to add different types of annotation depending on what features are enabled.

> 
>>
>> Are these valid concerns? Am I misunderstanding what you said?
> 
> 
> They are valid concerns, you're just used to thinking in terms of the C preprocessor.

I have barely ever used CPP for that type of thing so I wasn't ever used to thinking that way in the first place.
<g>
February 07, 2007
Walter Bright wrote:
> BCS wrote:
> 
>> Hasan Aljudy wrote:
>>
>>>
>>> Why is that evil? I think it's actually a great idea. "versions" are a sort of configuration that determines which code should be compiled and which code shouldn't. Storing this configuration in a separate file makes sense to me.
>>>
>>
>> rename versions.txt to versions.d
>>
>> and use
>>
>> import versions;
>>
>> same effect, less confusion
> 
> 
> No, versions defined in an import do NOT affect the importer.

What?? then how do you implement non trivial vertion logic?

version(Foo)
{
	// Foo alwys needs Bar
	version = Bar

	...
}

... more of the like
February 07, 2007
On Wed, 07 Feb 2007 22:18:28 +0300, Walter Bright <newshound@digitalmars.com> wrote:

> Yauheni Akhotnikau wrote:
>> Do you think this task can be done with D templates at complile time?
>
> Yes, that's exactly the intent. If this can't be made to work, we'll fix D so it can.

May be I'm wrong, but I think that 'static if' and recursive templates (and other techniques available for metaprogramming at compile time) are not as powerful as ordinary D itself. So it is much more preferable to me to program such DSL as 'normal' D program. May be it is a good idea to make 'staged' compilation? For example DSL transformation code is written as ordinal D programm. Then that code compiled at first compilation stage, then it is invoked by compiler and the result is placed into input to the next stage.

Something like that:

// active_record.d
// DActiveRecord implementation.
module active_record;

// DSL transformator.
char[] DActiveRecord( char[] input ) { ... }

===

// demo.d
// DActiveRecord usage.
module demo;

import active_record;

// Function DActiveRecord will be called at compile time.
mixin( DActiveRecord( "class Account ... end" ) );

===

Two points must be highlighted here:
* code of DActiveRecord must be used only at complite time and threw out from the resulting application code;
* multiple stages must be allowed: for example, DActiveRecord may depend on another DSL and so on.

-- 
Regards,
Yauheni Akhotnikau