Re: Best practices of using const

2019-06-22 Thread Bart via Digitalmars-d-learn

On Wednesday, 13 February 2019 at 16:40:18 UTC, H. S. Teoh wrote:
So ironically, the iron-clad semantics of D's const system 
turns out to be also its own downfall.





Such things are not ironic. There is always a trade off. You get 
nothing for free in this universe. Physics tells us this. 
Conservation laws apply to energy and everything is energy. Hence 
your computer cannot violate these laws nor can the D 
specification(whatever it ends up meaning) nor can the D const 
system, so to speak...


That is, any time there something is inversely related to 
something else then there will be a conservation relationship. If 
you restrict something too much then something else in direct 
opposition is becoming too unrestricted.


It's not that D's const system is bad, it is that it creates too 
much restriction without any other option. The usual way too 
solve these problems is granularity. this way you can choose the 
right tool for the job.


Maybe D needs different levels of const. constN where constN can 
always be cast to constn for n <= N. One would need to properly 
define the levels to maximize utility and minimize the 
granularity. D probably only needs 3-5 levels to be effective.














Trick for passing void* arrays around with typing

2019-06-21 Thread Bart via Digitalmars-d-learn




foo(cast(void*)[Object1, Object2]);

foo(cast(bool delegate(void*))(Tuple!(X,Y) objects)) { });

One can pass arbitrary data as an array(the cast is ugly though)

Then the tuple is cast back to void* but one can access objects 
correctly with typing.


This works well when interacting with C/C++ void* data passing 
but can also work with passing heterogeneous objects in general.





import std.stdio, std.typecons;

class X { int x = 1; }
class Y { int y = 2; }

// a ** is required for the cast.
void foo(void** data)
{
auto dd = cast(Tuple!(X,Y)*)(data);
auto d = *dd;
   writeln(d[0].x);
   writeln(d[1].y);
}


// This would be a C callback that is hidden from us but the 
callback isn't.

alias Q = void delegate(void*);
void bar(Q d, void* x)
{
d(x);
}



void main()
{
X x = new X;
Y y = new Y;
foo(cast(void**)[x, y]);
bar(cast(Q)(Tuple!(X,Y)* dd)
{
auto d = *dd;
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}


What would be nice is if one didn't actually have to jump through 
all the hoops:



void foo(void* as Tuple!(X,Y) d) // Redundant here but only 
because we have access to foo

{
   writeln(d[0].x);
   writeln(d[1].y);
}

// This would be a C callback that is hidden from us but the 
callback isn't.

alias Q = void delegate(void*);
void bar(Q d, void* x)
{
d(x);
}



void main()
{
X x = new X;
Y y = new Y;
foo([x, y]);
bar((as Tuple!(X,Y) d)
{
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, as [x, y]);
}


here I'm using as to signify a sort of implicit cast.

The main thing to note is that for the callback, D gets the type 
as it is defined  even though the cast changes the overall 
delegate to what it should be:



bar(cast(Q)(Tuple!(X,Y)* dd)
   {
auto d = *dd;
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}


I don't actually use * in my code I think it is necessary to get 
the right values due to how they are passed. I think it has to do 
with register vs stack passing. So it might be fragile


What I have is

bar(cast(Q)(Tuple!(X,Y) d)
   {
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}

which works, at least for x64.

The cool thing is the "casting" all happens internally and so 
there is no direct need for casting. Obviously though there is no 
real casting going on(one could swap x and y in the argument) and 
so it is dangerous... but D could make it 100% type safe with the 
appropriate syntax. (only for delegates and callbacks) although 
maybe one could use it for normal functions too:


foo([x,y] as Tuple!(X,Y))

e.g., normally we would have to do

bar((dd)
   {
auto d = cast(Tuple!(X,Y))dd; // Doesn't work though 
because tuple is not a reference, so must use the double pointer 
hacks.

writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}


When working with quite a number of C callbacks and passing data, 
this is a nice pattern to use to avoid the ugly temp variables 
and casting.







Component based programming in D

2019-06-18 Thread Bart via Digitalmars-d-learn
I'm new to component based programming. I've read it is an 
alternative to oop for speed. I don't understand how it is 
possible to have an alternative to oop and still have oop like 
behavior(polymorphism) nor how to do this. It seems all the great 
things oop offers(all the design patterns) would be impossible. 
The benefits are suppose to better reusability as components are 
more isolated in their dependencies.


Can someone help me understand this a little better and how I'd 
go about using it in D? Specifically I'm looking at the pros and 
cons, what are the real similarities and differences to oop, and 
how one implements them in D(taking in to account D's 
capabilities).


Thanks.



Regex driving me nuts

2019-06-17 Thread Bart via Digitalmars-d-learn
Error: static variable `thompsonFactory` cannot be read at 
compile time, Trying to regex an import file.


Also I have a group (...)* and it always fails or matches only 
one but if I do (...)(...)(...) it matches all 3(fails if more or 
less of course. ... is the regex).


Also when I ignore a group (?:Text) I get Text as a matched group 
;/


But all this is irrelevant if I can't get the code to work at 
compile time. I tried ctRegex






// A workaround for R-T enum re = regex(...)
template defaultFactory(Char)
{
@property MatcherFactory!Char defaultFactory(const ref 
Regex!Char re) @safe

{
import std.regex.internal.backtracking : 
BacktrackingMatcher;

import std.regex.internal.thompson : ThompsonMatcher;
import std.algorithm.searching : canFind;
static MatcherFactory!Char backtrackingFactory;
static MatcherFactory!Char thompsonFactory;
if (re.backrefed.canFind!"a != 0")
{
if (backtrackingFactory is null)
backtrackingFactory = new 
RuntimeFactory!(BacktrackingMatcher, Char);

return backtrackingFactory;
}
else
{
if (thompsonFactory is null)
thompsonFactory = new 
RuntimeFactory!(ThompsonMatcher, Char);

return thompsonFactory;
}
}
}

The workaround seems to workaround working.


CT/RT annoyance

2019-06-16 Thread Bart via Digitalmars-d-learn

Consider

void foo(string A = "")(string B = "")
{
static if (A != "")
   do(A);
else
   do(B);
}

foo!("x");

foo("x");


This is necessary because D's templating and meta programming 
system is frail.


While CTFE should take care of such things, it does not, consider 
import(file) vs readText(file).


If do loads such a file then one must use tricks as above:


auto load(string n = "")(string n2 = "")
{
static if (n != "")
enum x = import(n);
else
auto x = readText(n2);
}

auto load2(string n)
{
   enum x = import(n);
   string y = import(n);
}


load2 fails to compile SIMPLY because n is a RT parameter.

Even if ctfe were to kick in, such as 
load2("LiteralFileName.txt"), it is irrelevant because we can't 
compile the code.


So tricks as in load are used, but it is very hacky and verbose 
to do something very simple and natural.


This is precisely because readText cannot be used at compile time 
since it dispatches to the OS's reading routines rather than 
using import. This is not necessarily bad since we generally do 
not want to import files in to the application at compile time, 
but when we do it creates somewhat of a mess to unify code under 
ctfe.


For example, if I create a function that loads a file and 
processes it at RT then that code will not compile when used in 
CTFE. If I modify it to import the text at CT then it does not 
work at RT... even though the single point of breakage is the 
import/readText line.



auto importOrReadText(string n)
{
if (__ctfe)
{
auto x = import(n);
} else
{
auto x = readText(n);
}
}

Fails for the same reasons above.

There is no way to get out of this conundrum(and therefor it is a 
contradiction). It thin requires kicking the can down the street 
and not read files at all but take text.


My suggestion is for someone to fix this. I'm not sure of the 
solution.



maybe something like

auto importOrReadText(string n)
{
if (__ctfe)
{
auto x = import(ctfeFix(n));
} else
{
auto x = readText(n);
}
}

ctfeFix would be a special compiler semantic that takes a RT-like 
variable and converts it to CT. It does this because it actually 
can. It knows that n is ctfe'able and therefore n is actually a 
string literal and will do some "magic" trick the compiler in to 
seeing n for what it really is.


importOrReadText(rtFilename); // uses readText
importOrReadText("x.txt"); // uses import


Unfortunately __ctfe doesn't seem to actually work well... so I 
suggest further


auto importOrReadText(string n)
{
static if (isKACT(n))
{
auto x = import(n);
// or auto x = import(ctfeFix(n));
} else
{
auto x = readText(n);
}
}

isKACT determines if n is actually known at compile time and 
marks it n as essentially a CT constant as if it were passed as a 
template parameter(internally it would probably just convert it 
to a template parameter as if it were passed as such).



There may be other solutions.