| Thread overview | |||||
|---|---|---|---|---|---|
|
April 16, 2012 Cheaper compile-time tests | ||||
|---|---|---|---|---|
| ||||
This is from the office of not so useful things. If you try to fake dependent types in D you end instantiating many times the same template, inflating the binary with little purpose (because you are not very interested in the optimizations performed on the compile-time-known values). So to help that kind of coding I am thinking about something like this:
int foo1(@generic int c)(int x) if (c > 5) {
return c * x * x; // foo body
}
void main() {
auto r1 = foo1!(10)(15);
}
Its semantics is similar to this, but foo2() doesn't exists in the binary:
int foo2__(int c, int x) {
return c * x * x; // foo body
}
int foo2(int c)(int x) if (c > 5) {
return foo2__(c, x);
}
void main() {
auto r1 = foo2!(10)(15);
}
So it's closer to this:
int foo3__(int c, int x) {
return c * x * x; // foo body
}
void main() {
enum int c = 10;
static assert(c > 5, "required by foo");
auto r1 = foo3__(c, 15);
}
So it looks like a function templated on some value, but you are instead calling a normal run-time function that requires the first arguments must be known at compile-time, and they get verified by template constraints.
So all this is just a way to perform compile-time tests on values, avoiding the instantiation of many useless templates. Those tests are one of the two halves of the tests of dependency of the types :-)
Bye,
bearophile
| ||||
April 16, 2012 Re: Cheaper compile-time tests | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | > you end instantiating many times the same template, inflating the binary with little purpose (because you are not very interested in the optimizations performed on the compile-time-known values). I know you want a simple example: import std.stdio, std.string, std.conv, std.numeric, std.array, std.algorithm, std.traits; template TMMul(M1, M2) { // helper alias Unqual!(typeof(M1[0][0]))[M2[0].length][M1.length] TMMul; } void matrixMul(T, T2, size_t k, size_t m, size_t n) (const ref T[m][k] A, const ref T[n][m] B, /*out*/ ref T2[n][k] result) pure nothrow if (is(T2 == Unqual!T)) { T2[m] aux; foreach (j; 0 .. n) { foreach (k, row; B) aux[k] = row[j]; foreach (i, ai; A) result[i][j] = dotProduct(ai, aux); } } void main() { immutable int[2][3] a = [[1, 2], [3, 4], [3, 6]]; immutable int[3][2] b = [[-3, -8, 3,], [-2, 1, 4]]; enum form = "[%([%(%d, %)],\n %)]]"; writefln("A = \n" ~ form ~ "\n", a); writefln("B = \n" ~ form ~ "\n", b); TMMul!(typeof(a), typeof(b)) result = void; matrixMul(a, b, result); writefln("A * B = \n" ~ form, result); } Here a new matrixMul is instantiated for each size of the input matrices, but this avoids that: void matrixMul(T, T2, @generic size_t k, @generic size_t m, @generic size_t n) (const ref T[m][k] A, const ref T[n][m] B, /*out*/ ref T2[n][k] result) pure nothrow Bye, bearophile | |||
April 16, 2012 Re: Cheaper compile-time tests | ||||
|---|---|---|---|---|
| ||||
Posted in reply to bearophile | > Here a new matrixMul is instantiated for each size of the input matrices, but this avoids that:
> ...
So something like this:
import std.stdio;
void foo(T, @generic size_t n, @generic size_t m)(const ref
T[n][m] matrix) {
foreach (ref row; matrix)
for (int i = 0; i < n; i++)
writeln(row[i]);
}
void main() {
int[2][3] m = [[10, 20],
[30, 40],
[50, 60]];
foo(m);
}
means something like this, that is templated only on T:
import std.stdio;
void foo(T)(in size_t n, in size_t m, const T* matrix) {
for (int j = 0; j < m; j++) {
auto row = &matrix[j];
for (int i = 0; i < n; i++)
writeln(row[i]);
}
}
void main() {
int[2][3] m = [[10, 20],
[30, 40],
[50, 60]];
foo(m.length, m[0].length, cast(int*)m.ptr);
}
Bye,
bearophile
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply