Programming Language for Games, part 3

bearophile via Digitalmars-d digitalmars-d at puremagic.com
Sat Nov 1 15:26:16 PDT 2014


Walter Bright:

> I know you've suggested extensive data flow analysis,

The "static enum" (and related ideas) I've suggested require no 
flow analysis.


> Compile time checking can only be done on compile time 
> arguments (obviously) and template functions can arbitrarily 
> check compile time arguments.

In D it's easy to define a function that you call at compile-time 
to test that some compile-time data is well formed, I do this 
often. This is a simple example:


import std.range, std.algorithm;

alias Nibble = ubyte; // 4 bits used.
alias SBox = immutable Nibble[16][8];

private bool _validateSBox(in SBox data) @safe pure nothrow @nogc 
{
     return data[].all!((ref row) => row[].all!(ub => ub < 16));
}

struct GOST(s...) if (s.length == 1 && s[0]._validateSBox) {
     private static generate(ubyte k)() @safe pure nothrow {
         return k87.length.iota
                .map!(i=> (s[0][k][i >> 4] << 4) | s[0][k - 1][i & 
0xF])
                .array;
     }

     // ...
}

void main() {
     enum SBox cbrf = [
       [ 4, 10,  9,  2, 13,  8,  0, 14,  6, 11,  1, 12,  7, 15,  
5,  3],
       [14, 11,  4, 12,  6, 13, 15, 10,  2,  3,  8,  1,  0,  7,  
5,  9],
       [ 5,  8,  1, 13, 10,  3,  4,  2, 14, 15, 12,  7,  6,  0,  
9, 11],
       [ 7, 13, 10,  1,  0,  8,  9, 15, 14,  4,  6, 12, 11,  2,  
5,  3],
       [ 6, 12,  7,  1,  5, 15, 13,  8,  4, 10,  9, 14,  0,  3, 
11,  2],
       [ 4, 11, 10,  0,  7,  2,  1, 13,  3,  6,  8,  5,  9, 12, 
15, 14],
       [13, 11,  4,  1,  3, 15,  5,  9,  0, 10, 14,  7,  6,  8,  
2, 12],
       [ 1, 15, 13,  0,  5,  7, 10,  4,  9,  2,  3, 14,  6, 11,  
8, 12]];

     GOST!cbrf g;
     // ...
}


But you can run such compile-time tests only on template 
arguments, or on regular arguments of functions/constructors that 
are forced to run at compile-time. But for me this is _not_ 
enough. You can't implement the printf test example he shows 
(unless you turn the formatting string into a template argument 
of printf, this introduces template bloat and forbids you to have 
run-time format strings, or forces you to use two different 
syntaxes or to create two different print functions).

I'd like a way to run compile-time tests for the arguments of a 
regular function/constructor if they are known at compile-time.

So here I'd like a way to perform a compile-time test of the 
arguments of the call of #1 (and to not perform them for the call 
#2 because its argument is not a compile-time constant) (note 
that here both foo calls are not run at compile-time, and this is 
good):


void main() {
     auto x = foo(1); // #1
     int n = bar();
     auto y = foo(n); // #2
}


Currently if you want to do the same thing in D you have to use 
something like:

void main() {
     auto x = foo(ctEval!test(1)); // #1b
}


(Where "test" is a function that tests the argument and "ctEval" 
is a little template that forces to run "test" at compile time 
(here "foo" itself is not run). This becomes not much practical 
if you have arrays of values, or lot of data, etc, it's not 
*transparent* at all for the user, and the user can forget to use 
ctEval).



So this is useful in a large number of cases. If instead of foo() 
there's a call to a constructor, we become able to verify "game 
data" at compile time where possible while avoiding templates, 
and running the actual functions only at run-time.


Probably there are various ways to solve this problem. A lot of 
time ago I have suggested a "enum precondition":

int foo(in int x)
enum in(x) {
     // Optional enum pre-condition.
} in {
     // Optional normal pre-condition.
} body {
     // Function body.
}


The idea is that if foo is called with literals or compile-time 
(enum) arguments (here just the x argument is required to be 
known at compile-time) then it performs the tests inside the enum 
precondition at compile-time. If the arguments are run-time 
values then the enum precondition is ignored (and eventually the 
normal pre condition runs at run-time. Sometimes the two 
pre-conditions contain the same code or call the same testing 
function).

If you want to implement this idea very well, you can keep the 
enum precondition as source code (like with templates) so you can 
run it at compile-time when the arguments are known at 
compile-time.

Bye,
bearophile


More information about the Digitalmars-d mailing list