diff mde/Options.d @ 50:f68ae1d667f9

Options: impl template & new OptionsFont class. Options: impl template to ease creating Options sub-classes. New OptionsFont class (currently only controls render mode and hinting). committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Sun, 01 Jun 2008 18:22:54 +0100
parents 1530d9c04d4d
children f000d6cd0f74
line wrap: on
line diff
--- a/mde/Options.d	Sat May 31 13:10:06 2008 +0100
+++ b/mde/Options.d	Sun Jun 01 18:22:54 2008 +0100
@@ -44,7 +44,8 @@
 * Each sub-class provides named variables for maximal-speed reading. Local sub-class references
 * should be used for reading variables, and via the addOptionsClass() hook will be loaded from
 * files during pre-init (init0 stage). Do not write changes directly to the subclasses or they will
-* not be saved; use, for example, Options.setBool(...).
+* not be saved; use, for example, Options.setBool(...). Use an example like OptionsMisc as a
+* template for creating a new Options sub-class.
 *
 * Details: Options sub-classes hold associative arrays of pointers to all option variables, with a
 * char[] id. This list is used for saving, loading and to provide generic GUI options screens. The
@@ -224,41 +225,174 @@
     //END Static
     
     //BEGIN Templates
-    template store(A...) {
-        alias A a;
-    }
-    template decRecurse(char[] A, B...) {
-        static if (B.length) const char[] decRecurse = A ~ ", " ~ decRecurse!(B);
-        else                 const char[] decRecurse = A;
-    }
-    template decBool(A...) {
-        const char[] decBool = "bool " ~ decRecurse!(A) ~ ";\n";
-    }
-    template decInt(A...) {
-        const char[] decInt = "int " ~ decRecurse!(A) ~ ";\n";
-    }
-    template decDouble(A...) {
-        const char[] decDouble = "double " ~ decRecurse!(A) ~ ";\n";
+    private {
+        // Return index of first comma, or halts if not found.
+        template cIndex(char[] A) {
+            static if (A.length == 0)
+                static assert (false, "Error in implementation");
+            else static if (A[0] == ',')
+                const size_t cIndex = 0;
+            else
+                const size_t cIndex = 1 + cIndex!(A[1..$]);
+        }
+        // Return index of first semi-colon, or halts if not found.
+        template scIndex(char[] A) {
+            static if (A.length == 0)
+                static assert (false, "Error: no trailing semi-colon");
+            else static if (A[0] == ';')
+                const size_t scIndex = 0;
+            else
+                const size_t scIndex = 1 + scIndex!(A[1..$]);
+        }
+        // Look for "type symbols;" in A and return symbols as a comma separated list of names
+        // (even if type is encountered more than once). Output may contain spaces and, if
+        // non-empty, will contain a trailing comma. Assumes scIndex always returns less than A.$.
+        template parseT(char[] type, char[] A) {
+            static if (A.length < type.length + 1)	// end of input, no match
+                const char[] parseT = "";
+            else static if (A[0] == ' ')		// leading whitespace: skip
+                const char[] parseT = parseT!(type, A[1..$]);
+            else static if (A[0..type.length] == type && A[type.length] == ' ')	// match
+                const char[] parseT = A[type.length+1 .. scIndex!(A)] ~ "," ~
+                        parseT!(type, A[scIndex!(A)+1 .. $]);
+            else					// no match
+                const char[] parseT = parseT!(type, A[scIndex!(A)+1 .. $]);
+        }
+        // May have a trailing comma. Assumes cIndex always returns less than A.$.
+        template aaVars(char[] A) {
+            static if (A.length == 0)
+                const char[] aaVars = "";
+            else static if (A[0] == ' ')
+                const char[] aaVars = aaVars!(A[1..$]);
+            else
+                const char[] aaVars = "\""~A[0..cIndex!(A)]~"\"[]:&"~A[0..cIndex!(A)] ~ "," ~
+                        aaVars!(A[cIndex!(A)+1..$]);
+        }
+        // strip Trailing Comma
+        template sTC(char[] A) {
+            static if (A.length && A[$-1] == ',')
+                const char[] sTC = A[0..$-1];
+            else
+                const char[] sTC = A;
+        }
+        // if string is empty (other than space) return null, otherwise enclose: [A]
+        template listOrNull(char[] A) {
+            static if (A.length == 0)
+                const char[] listOrNull = "null";
+            else static if (A[0] == ' ')
+                const char[] listOrNull = listOrNull!(A[1..$]);
+            else
+                const char[] listOrNull = "["~A~"]";
+        }
+    } protected {
+        /** Produces the implementation code to go in the constuctor. */
+        template aaDefs(char[] A) {
+            const char[] aaDefs =
+                    "optsBool = "  ~ listOrNull!(sTC!(aaVars!(parseT!("bool"  , A)))) ~ ";\n" ~
+                    "optsInt = "   ~ listOrNull!(sTC!(aaVars!(parseT!("int"   , A)))) ~ ";\n" ~
+                    "optsDouble = "~ listOrNull!(sTC!(aaVars!(parseT!("double", A)))) ~ ";\n" ~
+                    "optsCharA = " ~ listOrNull!(sTC!(aaVars!(parseT!("char[]", A)))) ~ ";\n";
+        }
+        /+/** Produces the implementation code to go in the static constuctor. */
+        template optClassAdd(char[] symb) {
+            const char[] optClassAdd = symb ~ "=new "~classinfo(this).name~";\n";//Options.addOptionsClass("~symb~", );\n";
+        }+/
+        /** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]")
+         *
+         * E.g.
+         * ---
+         * mixin (impl ("bool a, b; int i;"));
+         * ---
+         *
+         * In case this() needs to be customized, mixin(impl!(A)) is equivalent to:
+         * ---
+         * mixin (A~"\nthis(){\n"~aaDefs!(A)~"}");
+         * ---
+         *
+         * Notes: Only use space as whitespace (no new-lines or tabs). Make sure to add a trailing
+         * semi-colon (;) or you'll get told off! :D
+         *
+         * In general errors aren't reported well. Trial with pragma (msg, impl!(...)); if
+         * necessary.
+         *
+         * Extending: mixins could also be used for the static this() {...} or even the whole
+         * class, but doing would rather decrease readability of any implementation. */
+        template impl(char[] A /+, char[] symb+/) {
+            const char[] impl = A~"\nthis(){\n"~aaDefs!(A)~"}";
+            // ~"\nstatic this(){\n"~optClassAdd!(symb)~"}"
+        }
     }
-    template decCharA(A...) {
-        const char[] decCharA = "char[] " ~ decRecurse!(A) ~ ";\n";
-    }
-    template aaRecurse(char[] A, B...) {
-        static if (B.length) const char[] aaRecurse = "\""~A~"\"[]:&"~A ~ ", " ~ aaRecurse!(B);
-        else                 const char[] aaRecurse = "\""~A~"\"[]:&"~A;
-    }
-    template aaBool(A...) {
-        const char[] aaBool = "optsBool = [" ~ aaRecurse!(A) ~ "];\n";
-    }
-    template aaInt(A...) {
-        const char[] aaInt = "optsInt = [" ~ aaRecurse!(A) ~ "];\n";
-    }
-    template aaDouble(A...) {
-        const char[] aaDouble = "optsDouble = [" ~ aaRecurse!(A) ~ "];\n";
-    }
-    template aaCharA(A...) {
-        const char[] aaCharA = "optsCharA = [" ~ aaRecurse!(A) ~ "];\n";
-    }
+    /+/** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]")
+     *
+     * E.g.
+     * ---
+     * mixin (impl ("bool a, b; int i;"));
+     * ---
+     * The parser isn't highly accurate. */
+    // Try using templates instead? See std.metastrings
+    static char[] impl (char[] A) {
+        char[] bools;
+        char[] ints;
+        
+        while (A.length) {
+            // Trim whitespace
+            {
+                size_t i = 0;
+                while (i < A.length && (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD)))
+                    ++i;
+                A = A[i..$];
+            }
+            if (A.length == 0) break;
+            
+            char[] type;
+            for (size_t i = 0; i < A.length; ++i) {
+                if (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD)) {
+                    type = A[0..i];
+                    A = A[i+1..$];
+                    break;
+                }
+            }
+            
+            char[] symbols;
+            for (size_t i = 0; i < A.length; ++i) {
+                if (A[i] == ';') {
+                    symbols = A[0..i];
+                    A = A[i+1..$];
+                    break;
+                }
+            }
+            
+            if (type == "bool") {
+                if (bools.length)
+                    bools = bools ~ "," ~ symbols;
+                else
+                    bools = symbols;
+            }
+            else if (type == "int") {
+                if (ints.length)
+                    ints = ints ~ "," ~ symbols;
+                else
+                    ints = symbols;
+            }
+            else {
+                // Unfortunately, we cannot output non-const strings (even though func is compile-time)
+                // We also cannot use pragma(msg) because the message gets printed even if the code isn't run.
+                //pragma(msg, "Warning: impl failed to parse whole input string");
+                // Cannot use Cout / logger either.
+                break;
+            }
+        }
+        
+        char[] ret;
+        if (bools.length)
+            ret = "bool "~bools~";\n";
+        if (ints.length)
+            ret = ret ~ "int "~ints~";\n";
+        
+        
+        
+        return ret;
+    }+/
     //END Templates
 }
 
@@ -341,22 +475,7 @@
 /** A home for all miscellaneous options, at least for now. */
 OptionsMisc miscOpts;
 class OptionsMisc : Options {
-    alias store!("useThreads", "exitImmediately") BOOL;
-    alias store!("logLevel") INT;
-    alias store!("pollInterval") DOUBLE;
-    alias store!("L10n") CHARA;
-    
-    mixin (decBool!(BOOL.a));
-    mixin (decInt!(INT.a));
-    mixin (decDouble!(DOUBLE.a));
-    mixin (decCharA!(CHARA.a));
-    
-    this () {
-        mixin (aaBool!(BOOL.a));
-        mixin (aaInt!(INT.a));
-        mixin (aaDouble!(DOUBLE.a));
-        mixin (aaCharA!(CHARA.a));
-    }
+    mixin (impl!("bool useThreads, exitImmediately; int logLevel; double pollInterval; char[] L10n;"));
     
     static this() {
         miscOpts = new OptionsMisc;