On 4/15/11 8:19 PM, Andrej Mitrovic wrote:
[…] My biggest issue is that
I can't modify variables at compile time. I wish there was some
special CTFE int type which is only visible at compile-time and which
we can use however we want. That way I could keep count of things, for
example I could generate a list of indexes that can then be mixed in
as a string.
[…]
I dunno, CTFE overall seems like a buggy thing where I have to guesswhether 
something will work or not. It's very stress-inducing.

Correct me if I'm wrong, but might it be that you are conflating templates and CTFE, which are – although they are both used for metaprogramming – two entirely different concepts?

Maybe it helps to think of calling a function at compile time as a gate into a parallel universe. In this other world, you can modify variables, call functions, etc. as you please, just as if you were executing code at runtime (well, inside the more or less implementation-defined boundaries of CTFE). The only restriction is that you can only return a single value through the gate, there is no other way to influence the »compiler« universe from inside the functions you call. More specifically, there is no way to manipulate types in the compiler universe – although you can instantiate templates in CTFE functions just like normal, you will never be able to »return« them back to the outside world. Also, be aware of the fact that a CTFE function can just work on _values_, like every other runtime function, not on types.

So much for CTFE. Templates, on the other hand, are basically just named scopes with a few extra features which happen to make a Turing complete language. As such, there will never be something like a runtime variable modifiable at compile time in templates, as you asked (at least I hope so). The interesting thing about templates is that they allow you to work with types themselves. Due to the absence of mutable state, you are generally limited to functional techniques, though, which can be uncomfortably similar to the proverbial Turing tarpit in some cases (although it's surprising how easy it is to write certain algorithms in a functional manner once you got the hang of it).

However, there are ways to combine templates and CTFE to your advantage. Without any concrete question, it's obviously hard to give a good suggestion, but an example would be to use template functions called at compile time to produce string mixins:

---
string generateCode(T...)() {
    string code;
    [… construct a string containing some declarations …]
    return code;
}

template Foo(T...) {
    alias DoSomethingWithATypeTuple!(T) U;
    mixin(generateCode(U));
}
---

You can also shorten this by using delegate literals:
---
template Foo(T...) {
    alias DoSomethingWithATypeTuple!(T) U;
    mixin({
        […]
        return "<some code generated while having access to T and U>";
    }());
}
---

Another small template metaprogramming example showing a way to process a list of types without needing mutable state. Specifically, it aliases a new type tuple to Result which doesn't contain any items where Exp.numerator is 0 (you can do the same with eponymous templates). TypeTuples are automatically flattened, which allows for a concise implementation here.

---
template Process(T...) {
    static if (T.length == 0) {
        alias TypeTuple!() Result;
    } else {
        alias T[0] A;
        static if (A.Exp.numerator == 0) {
            alias Process!(T[1..$]).Result Result;
        } else {
            alias TypeTuple!(A, Process!(T[1..$]).Result) Result;
        }
    }
}
---

As for constructing lists of indices and then generating code of them: If you need to work on types for generating the list, you could e.g. use some recursive template to construct a type tuple of integer literals (that name really doesn't fit well), and then process it via CTFE to generate code to be mixed in – or whatever you need in your specific case. Feel free to ask about any specific problems in d.D.learn.

David

Reply via email to