Download source code.
import std.stdio;
import std.string;
// Type from string
// Usage:
// ToType!("int") x;
template ToType (string s)
{
alias ToTypeHelper!(s).type ToType;
}
template ToTypeHelper (string s)
{
mixin ("alias " ~ s ~ " type;");
}
//-----------------
// Array operations
//-----------------
// Given ["a", "b"] produces [["a"], ["b"]]
string [][] toArrays (string [] strings)
{
string [][] result = [];
foreach (s; strings)
result ~= [s];
return result;
}
// Concatenate strings
string ctConcat (string [] arr)
{
string result = "";
foreach (s; arr)
result ~= s;
return result;
}
// Concatenate strings with a separator
string ctConcat (string [] arr, char sep)
{
assert (arr.length != 0);
string result = "";
foreach (s; arr)
result ~= s ~ sep;
return result [0..$-1]; // remove the trailing sep
}
// Compile-time insertion sort
string [] ctSort (string [] arr)
{
if (arr.length == 0)
return arr;
else
return ctInsert (ctSort (arr [0..$-1]), arr[$-1]);
}
// Insert a string into a sorted array
string [] ctInsert (string [] arr, string str)
{
int i = 0;
while (i < arr.length && arr[i] < str)
++i;
return arr [0..i] ~ str ~ arr [i..$];
}
// Return set difference of sorted arrays
string [] ctDiff (string [] a1, string [] a2)
{
if (a1.length == 0)
return [];
else if (a2.length == 0)
return a1;
else if (a1[$-1] == a2 [$-1])
return ctDiff (a1 [0..$-1], a2 [0..$-1]);
else if (a1[$-1] > a2 [$-1])
return ctDiff (a1 [0..$-1], a2) ~ a1 [$-1];
else
return ctDiff (a1, a2[0..$-1]);
}
// Given ["a", "b", "c"] produces
// [["a"], ["a", "b"], ["a", "b", "c"]
string [][] prefixes (string [] a)
{
string [][] result = [];
for (int i = 1; i <= a.length; ++i)
result ~= a [0..i];
return result;
}
// Given ["a", "b", "c"] and len = 2
// produces [["a", "b"], ["a", "c"], ["b", "c"]]
string [][] combinations (int len, string [] arr)
{
if (len == 0)
return [[]];
string [][] result = [];
// generate all prefixes
foreach (string [] t; prefixes (arr))
{
// split (non-empty) prefix at the penultimate position
string [] beg = t [0..$-1];
string fin = t [$-1];
// recurse with the beginning of the prefix
foreach (string [] shortCmb; combinations (len - 1, beg))
result ~= [shortCmb ~ fin];
}
return result;
}
// Given ["a", "b", "c"] produces
// [["a"], ["b"], ["c"],
// ["a", "b"], ["a", "c"], ["b", "c"],
// ["a", "b", "c"]]
string [][] allCombinations (string [] arr)
{
string [][] result = [];
for (int i = 1; i <= arr.length; ++i)
result ~= combinations (i, arr);
return result;
}
// Function features
// The goal is to create the following hierarchy from ["A", "B", "C"]
//
// interface A_B_C {}
//
// interface A_B: A_B_C {}
// interface A_C: A_B_C {}
// interface B_C: A_B_C {}
//
// interface A: A_B, A_C {}
// interface B: A_B, B_C {}
// interface C: A_C, B_C {}
//
// interface NoFeatures: A, B, C {}
// Prepares a list of base classes (in the array of arrays form)
// by inserting elements of "missing" into the array of "present"
// where "present" are the atoms in the derived interface
// Called with ["b"] ["a", "c"]
// returns [["a", "b"], ["a", "c"]]
string [][] listBases (string [] present, string [] omitted)
{
string [][] result = [];
foreach (o; omitted)
result ~= ctInsert (present, o);
return result;
}
// Given [["a", "b"], ["a", "c"], ["b", "c"]] produces
// ": a_b, a_c, b_c
string concatBases (string [][] bases)
{
if (bases.length == 0)
return "";
string result = ": ";
foreach (a; bases)
result ~= ctConcat (a, '_') ~ ",";
return result [0..$-1]; // remove the trailing comma
}
// Creates a declaration of the hierarchy of interfaces
string declareAllFeatures (string [] allFeatures)
{
string [] all = ctSort (allFeatures);
string result;
foreach (c; allCombinations (all))
{
result ~= "interface " ~ ctConcat (c, '_');
string [][] bases = listBases (c, ctDiff (all, c));
result ~= concatBases (bases);
result ~= " {}\n";
}
result ~= "interface NoFeatures" ~ concatBases (toArrays (all)) ~ " {}\n";
return result;
}
// Creates an interface name corresponing to a list of atoms
// Given ["B", "A"] producese "A_B", or "NoFeatures" for an empty list
string MakeFeatures (string[] a)
{
if (a.length == 0)
return "NoFeatures";
else
return ctConcat (ctSort (a), '_');
}
//------
// Usage
//------
// Declare the whole hierarchy of function features
mixin (declareAllFeatures (["NoEx", "Tested", "Portable"]));
// foo guarantees Portable and NoEx
void foo (ToType!(MakeFeatures (["Portable", "NoEx"])) x)
{
writeln ("Overload: Portable, NoEx");
}
// foo guarantees Tested, Portable, and NoEx
void foo (ToType!(MakeFeatures (["Tested", "Portable", "NoEx"])) x)
{
writeln ("Overload: Tested, Portable, NoEx");
}
// bar guarantees Tested
void bar (ToType!(MakeFeatures (["Tested"])) x)
{
writeln ("Tested called");
foo (x);
}
/* This will soon work
class Base
{
public:
void M (ToType!(MakeFeatures (["Tested"])))
{
writeln ("Base class method");
}
}
class Derived
{
public:
override void M (ToType!(MakeFeatures (["Tested", "Portable"])))
{
writeln ("Derived class method");
}
}
*/
void main ()
{
// Runtime tests:
// writeln (prefixes (["a", "b", "c"]));
// writeln (combinations (2, ["a", "b", "c"]));
// writeln (allCombinations (["a", "b", "c", "d"]));
writeln ("---This is the declaration produced by declareAllFeatures---");
writeln (declareAllFeatures (["Tested", "Portable"]));
writeln ("---Testing function features---");
// Create various requirements
ToType!(MakeFeatures ([])) n; // Don't require anything
ToType!(MakeFeatures (["Portable"])) p;
ToType!(MakeFeatures (["Tested"])) t;
ToType!(MakeFeatures (["Portable", "Tested"])) pt;
foo (p); // calls foo that guarantees Portable and NoEx
foo (n); // calls foo that guarantees Portable and NoEx
foo (t); // calls foo that guarantees Tested, Portable, and NoEx
// bar (pt);
// function features.bar (Tested) does not match parameter types (Portable_Tested)
// bar (p);
// function features.bar (Tested) does not match parameter types (Portable)
bar (t);
/*
// Virtual function override
Base b = new Base;
Derived d = new Derived;
b.M (t);
d.M (pt);
*/
}