Mercurial > projects > qtd
changeset 351:59d847a814e3
added meta subdir
author | Max Samukha <maxter@spambox.com> |
---|---|
date | Thu, 20 May 2010 15:54:06 +0300 |
parents | 31520b2c0b3c |
children | 1a5569684e30 |
files | d2/qtd/meta/Compiletime.d d2/qtd/meta/Runtime.d |
diffstat | 2 files changed, 849 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/d2/qtd/meta/Compiletime.d Thu May 20 15:54:06 2010 +0300 @@ -0,0 +1,557 @@ +/************************************************************************** + Copyright: QtD Team, 2010 + Authors: Max Samukha, Eldar Insafutdinov + License: Boost Software License 1.0 +**************************************************************************/ +module qtd.meta.Compiletime; + +import + std.traits, + std.conv, + std.variant, + std.typetuple; + +import std.string : startsWith; + +/* +uint startsWith(string s, string pattern) +{ + if (pattern.length <= s.length && s[0..pattern.length] == pattern) + return pattern.length; + + return 0; +} +*/ + + +enum standardNamespace = "qtd"; + +template Alias(A...) if (A.length == 1) +{ + static if (__traits(compiles, { alias A[0] x; })) + alias A[0] Alias; + else + enum Alias = A[0]; +} + +/** + Encapsulates a static tuple. Useful for preventing tuples from + flattening when they are passed to a variadic template. +*/ +template TypeTupleWrapper(A...) +{ + alias A tuple; +} + +// returns the type of a template parameter if there is one +template templateParam(U : V!(U), alias V) +{ + alias U templateParam; +} + + +/** + Returns a tuple with T repeated count times. + */ +// TODO: generalize to accept any symbol +template Repeat(T, size_t count) +{ + static if (count) + alias TypeTuple!(count, Repeat!(T, count - 1)) Repeat; + else + alias TypeTuple!() Repeat; +} + +/** + Returns the required number of function arguments, optional arguments excluded + */ +int requiredArgCount(alias fn)() { + alias ParameterTypeTuple!(typeof(&fn)) P; + P p; + static if (P.length == 0) + return 0; + + foreach(i, _; P) + { + static if (!__traits(compiles, fn(p[0..$-i-1]))) + { + return p.length - i; + } + } + return 0; +} + +/** + Returns true if is a delegate type. + */ +template isDelegate(T) +{ + enum isDelegate = is(T == delegate); +} + +/** + Returns true if T is a function pointer type. + */ +template isFunctionPointer(T) +{ + enum isFuntionPointer = is(typeof(*T.init) == function); +} + +/** + Returns true if entity is a function or function type. +*/ +template isFunction(alias entity) +{ + static if (is(entity)) + alias isFunction!(entity, void) isFunction; + else + enum isFunction = is(typeof(entity) == function); +} + +template isFunction(T, Dummy = void) +{ + enum isFunction = is(T == function); +} + + +//TODO: hack +uint isModule(string str) +{ + return startsWith(str, "module "); +} + + +template qualifiedCppName(T) +{ + static if(!isModule(__traits(parent, T).stringof)) + enum qualifiedCppName = qualifiedCppName!(__traits(parent, T)) ~ "::" ~ T.stringof; + else + enum qualifiedCppName = T.stringof; +} + +template qualifiedName(T) +{ + static if (!isModule(__traits(parent, T).stringof)) + enum qualifiedName = qualifiedName!(__traits(parent, T)) ~ "." ~ T.stringof; + else + enum qualifiedName = T.stringof; +} + +template fullName(T) +{ + static if (is(T == enum)) + enum fullName = qualifiedName!T; + else + enum fullName = T.stringof; +} + +/** + */ +enum AttributeOptions +{ + /** + */ + none, + + /** + Allows multiple equally named attributes to be associated with the symbol. + */ + allowMultiple = 0x0000_0001, + + /* internal */ inner = 0x0000_0002, +} + +/** + When mixed in an aggregate, converts a compile-time tuple to + members of that aggregate. + */ +mixin template tupleToMembers(string nameSpace, size_t index, A...) +{ + static if (index < A.length) + { + enum indexStr = to!string(index); + + static if (is(typeof( { typeof(A[index]) x; }() ))) + mixin("typeof(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ " = A[" ~ indexStr ~"];\n"); + else + mixin("alias Alias!(A[" ~ indexStr ~ "]) " ~ nameSpace ~ indexStr ~ ";\n"); + + mixin tupleToMembers!(nameSpace, index + 1, A); + } +} + +/** + When mixed in an aggregate, converts a compile-time tuple of name-value pairs to + members of that aggregate. + */ +mixin template NameValueTupleToFields(A...) +{ + +} + +version (QtdUnittest) +{ + unittest + { + static int foo() + { + return 42; + } + + static struct S + { + mixin tupleToMembers!("member", 0, + int, + "a", + 22, + foo); + } + + static assert(is(S.member0 == int)); + S s; + assert(s.member1 == "a"); + assert(s.member2 == 22); + assert(S.member3() == 42); + } +} + +private template attributeId(alias symbol, uint index = 0) +{ + enum attributeId = standardNamespace ~ "_attr_" ~ uniqueId!symbol ~ "_" ~ to!string(index); +} + +/** + Attributes allow to associate arbitrary compile-time data + with a declaration and optionaly make that data available at run-time. + +---- +class A +{ +} +mixin Attribute!(C, "someAttribute", "B"); +---- + + The example above associates the string "B" with class A under the attribute name "someAttribute". + Multiple attributes with the same name can be associated with a single declaration. + +---- +class A +{ +} +mixin Attribute!(C, "someAttribute", "B"); +mixin Attribute!(C, "someAttribute", AttributeOptions.allowMultiple, "C"); +---- + + Attributes can be accessed at compile-time as follows: + +---- +alias GetAttributes!(C, "someAttribute") attrs; +---- + + GetAttribute returns a static tuple, which contains "someAttribute" attributes in the form + of TypeTupleWrapper instances. The wrapped tuples have the following layout: + + [0] - attribute name + [1] - attribute options + [2..$] - attribute data + +---- +alias attrs[0] attr0; +alias attrs[1] attr1; + +static assert(attrs0.tuple[0] == "someAttribute" && attrs0.tuple[2] == "B"); +static assert(attrs1.tuple[0] == "someAttribute" && attrs1.tuple[2] == "C"); +---- + + Attributes can be inserted inside the body of a declaration. +---- + class A + { + mixin Attribute!("someAttribute", "B"); + } +---- + + Attributes can be made available at run time by means of the declaration's meta-object +---- + // prints names of all attributes associated with class A + auto a = new A; + foreach (attr; a.metaObject.attributes) + writeln(attr.name); +---- + + Attributes can be specialized + +---- +mixin template DbFieldAttribute(alias prop, string columnName) +{ + mixin Attribute!(prop, "DbFieldAttribute", columnName); +} + +class A +{ + int value; + mixin DbFieldAttribute!(value, "Value"); + + int anotherValue; + mixin DbFieldAttribute!(anotherValue, "Value2"); +} + +assert(GetAttributes!(A.value, "DbFieldAttribute")[0].tuple[1] == "Value"); +assert(GetAttributes!(A.anotherValue, "DbFieldAttribute")[0].tuple[1] == "Value2"); +---- + +*/ +mixin template Attribute(alias symbol, string attrClass, A...) +{ + mixin Attribute!(symbol, attrClass, AttributeOptions.none, A); +} + +/// ditto +mixin template Attribute(alias symbol, string attrClass, AttributeOptions opts, A...) +{ + mixin AttributeImpl!(symbol, attrClass, opts, 0, A); +} + + +/// ditto +// TODO: probably make this an overload of Attribute +mixin template InnerAttribute(string attrClass, AttributeOptions opts, A...) +{ + // BUG: needs to be generalized to accept any parent + mixin Attribute!(typeof(this), attrClass, opts | AttributeOptions.inner, A); +} + +/// ditto +mixin template InnerAttribute(string attrClass, A...) +{ + mixin InnerAttribute!(attrClass, AttributeOptions.none, A); +} + +private mixin template AttributeImpl(alias symbol, string attrClass, AttributeOptions opts, size_t index, A...) +{ + private enum attrId = attributeId!(symbol, index) ~ (opts & AttributeOptions.inner ? "_inner" : ""); + + static if (is(typeof(mixin(attrId)))) + { + mixin ("alias " ~ attrId ~ " attr;"); + static if (!(opts & AttributeOptions.allowMultiple)) + { + static assert (attr[0] != attrClass, "Multiple " ~ attrClass ~ " attributes are not allowed for " + ~ __traits(parent, symbol).stringof ~ "." ~ stringOf!symbol); + } + + mixin AttributeImpl!(symbol, attrClass, opts, index + 1, A); + } + else + { + mixin ("alias TypeTuple!(attrClass, opts, A) " ~ attrId ~ ";"); + } +} + +private string stringOfFunction(alias symbol)() +{ + auto ptrType = typeof(&symbol).stringof; + auto paramList = ParameterTypeTuple!(symbol).stringof; + + string result = ReturnType!(symbol).stringof ~ " " ~ __traits(identifier, symbol) ~ paramList; + + if (ptrType[$ - 1] != ')') + { + for (size_t i = ptrType.length - 2;; --i) + { + if (ptrType[i] == ')') + { + result ~= ptrType[i + 1..$]; + break; + } + } + } + + return result; +} + +/** + String of any symbol, including functions + */ +template stringOf(alias symbol) +{ + static if (isFunction!symbol) + enum stringOf = stringOfFunction!symbol; + else + enum stringOf = symbol.stringof; +} + +/** + Returns the string uniquely identifying the + symbol in its container. + */ +template uniqueName(alias symbol) +{ + enum uniqueName = stringOf!symbol; +} + +// TODO: works only for simple types. implement +string uniqueIdImpl(string symbol) +{ + char[] r = symbol.dup; + foreach (i, c; symbol) + { + if (c == '(' || c == ')' || c == ' ') + r[i] = '_'; + } + + return cast(immutable)r; +} + +/** + Returns a valid D identifier uniquely + identifying symbol in its container. + */ +template uniqueId(alias symbol) +{ + enum uniqueId = uniqueIdImpl(stringOf!symbol); +} + +version (QtdUnittest) +{ + unittest + { + static class C + { + void foo() const {}; + bool bar(int) { return true; }; + int x; + static assert (stringOf!foo == "void foo() const"); + static assert (uniqueName!foo == "void foo() const"); + static assert (stringOf!bar == "bool bar(int)"); + static assert (uniqueName!bar == "bool bar(int)"); + static assert (stringOf!x == "x"); + static assert (uniqueName!x == "x"); + } + + static assert (stringOf!(C.foo) == "void foo() const"); + static assert (uniqueName!(C.foo) == "void foo() const"); + static assert (stringOf!(C.bar) == "bool bar(int)"); + static assert (uniqueName!(C.bar) == "bool bar(int)"); + + static assert (stringOf!(C.x) == "x"); + static assert (uniqueName!(C.x) == "x"); + } +} + +/** + Predicate that always evaluates to true regardless of arguments. + */ +template truePred(A...) +{ + enum truePred = true; +} + +private template attrNamePred(string name) +{ + template attrNamePred(A...) + { + enum attrNamePred = A[0] == name; + } +} + +/** + Returns a compile-time tuple of attributes that + match pred. + */ +template GetAttributes(alias symbol, alias pred = truePred) +{ + alias GetAttributesImpl!(symbol, 0, pred).result GetAttributes; +} + +/** + Returns a compile-time tuple of attributes + matching the specified attribute name. + */ +template GetAttributes(alias symbol, string attrName) +{ + alias GetAttributes!(symbol, attrNamePred!attrName) GetAttributes; +} + + +template GetAttributesImpl(alias symbol, size_t index, alias pred) +{ + //pragma(msg, mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner").stringof); + static if (is(typeof(mixin("__traits(parent, symbol)." ~ attributeId!(symbol, index))))) + mixin ("alias Alias!(__traits(parent, symbol))." ~ attributeId!(symbol, index) ~ " attr;"); + else static if (is(typeof(mixin("symbol." ~ attributeId!(symbol, index) ~ "_inner")))) + mixin ("alias symbol." ~ attributeId!(symbol, index) ~ "_inner attr;"); + + static if (is(typeof(attr))) + { + alias GetAttributesImpl!(symbol, index + 1, pred).result next; + + static if (pred!attr) + alias TypeTuple!(TypeTupleWrapper!attr, next) result; + else + alias next result; + } + else + alias TypeTuple!() result; +} + +version (QtdUnittest) +{ + mixin template MyAttribute(alias symbol, A...) + { + mixin Attribute!(symbol, "MyAttribute", AttributeOptions.allowMultiple, A); + } + + mixin template ClassInfo(string name, alias value) + { + mixin InnerAttribute!("ClassInfo", AttributeOptions.allowMultiple, name, value); + } + + unittest + { + static class C + { + // inner C attributes + mixin InnerAttribute!("Inner", 33); // generic + mixin ClassInfo!("version", 123); + mixin ClassInfo!("author", "James Bond"); + + + void foo() {}; + // foo attributes + mixin Attribute!(foo, "SomeAttribute", 42); + mixin MyAttribute!(foo, 1, 2); + mixin MyAttribute!(foo, 3, 4); + + alias GetAttributes!(typeof(this), "Inner") innerAttrs; + static assert(innerAttrs[0].tuple[0] == "Inner"); + } + // outer C attribute + mixin MyAttribute!(C, 24); + + alias GetAttributes!(C, "Inner") innerAttrs; + static assert(innerAttrs[0].tuple[0] == "Inner" && innerAttrs[0].tuple[2] == 33); + + alias GetAttributes!(C, "ClassInfo") ciAttrs; + static assert(ciAttrs[0].tuple[2] == "version" && ciAttrs[0].tuple[3] == 123); + + alias GetAttributes!(C.foo, "SomeAttribute") someAttr; + static assert(someAttr.length == 1); + static assert(someAttr[0].tuple[0] == "SomeAttribute"); + + alias GetAttributes!(C.foo, "MyAttribute") myAttrs; + + //COMPILER BUG: cannot 'alias myAttrs[0].tuple myAttrs_0'; + static assert(myAttrs[0].tuple[0] == "MyAttribute"); + static assert(myAttrs[0].tuple[2] == 1 && myAttrs[0].tuple[3] == 2); + + static assert(myAttrs[1].tuple[0] == "MyAttribute"); + static assert(myAttrs[1].tuple[2] == 3 && myAttrs[1].tuple[3] == 4); + + /+ BUG: Fails: local declarations cannot be accessed as parent.localDecl + alias GetAttributes!(C, "MyAttribute") myAttrs2; + static assert(myAttrs2[0].tuple[0] == "MyAttribute"); + static assert(myAttrs2[0].tuple[1] == 24); + +/ + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/d2/qtd/meta/Runtime.d Thu May 20 15:54:06 2010 +0300 @@ -0,0 +1,292 @@ +/************************************************************************** + Copyright: Copyright Max Samukha, 2010 + Authors: Max Samukha + License: Boost Software License 1.0 +**************************************************************************/ +module qtd.meta.Runtime; +//TODO: Probably replace switch dispatch with pointer dispatch +//and leave switch dispatch only in C interface + +import + qtd.meta.Compiletime, + + std.typetuple, + std.conv, + std.variant, + core.sync.rwmutex; + +private __gshared ReadWriteMutex lock; +shared static this() +{ + lock = new ReadWriteMutex; +} + +/** + IDs of the built-in basic types. +*/ +enum BasicTypeId +{ + /// + void_, + /// + bool_, + /// + byte_, + /// + ubyte_, + /// + short_, + /// + ushort_, + /// + int_, + /// + uint_, + /// + long_, + /// + ulong_, + /// + cent_, + /// + ucent_, + /// + float_, + /// + double_, + /// + real_, + /// + ifloat_, + /// + idouble_, + /// + ireal_, + /// + cfloat_, + /// + cdouble_, + /// + creal_, + /// + char_, + /// + wchar_, + /// + dchar_ +} + +/** + Thrown on meta-system errors. +*/ +class MetaException : Exception +{ + this(string msg) + { + super(msg); + } +} + +abstract class Meta +{ + alias typeof(this) This; + + string name; + MetaAttribute[] attributes; + Meta[] members; + + template createImpl(M : This) + { + static M createImpl(alias symbol)() + { + auto m = new M; + m.construct!symbol; + return m; + } + } + + private void createAttrs(alias symbol)() + { + alias GetAttributes!symbol attrs; + enum len = attrs.length; // COMPILER BUG + foreach (i, _; Repeat!(void, len)) + { + alias TypeTuple!(attrs[i].tuple) attr; + // if the third element of the attribute data is a MetaAttribute subclass, + // use that to create the attribute instance. + static if (attr.length > 2 && (is(attr[2] : MetaAttribute))) + { + alias attr[2] MA; + alias TypeTuple!(attr[0..2], attr[3..$]) args; + attributes ~= MA /*COMPILER BUG: tuple element as tuple*/[0].create!args(); + } + } + } + + protected void construct(alias symbol)() + { + createAttrs!symbol; + } +} + +/** + Base class for run time attributes. + */ +abstract class MetaAttribute +{ + alias typeof(this) This; + + string name; + AttributeOptions options; + + This create(string name, AttributeOptions opts, A...)() + { + auto ma = new This; + ma.construct!(name, opts, A)(); + return ma; + } + + void construct(string name, AttributeOptions opts)() + { + this.name = name; + options = opts; + } +} + +abstract class MetaType : Meta +{ +} + +abstract class MetaAggregate : MetaType +{ +} + +class MetaClass : MetaAggregate +{ + alias typeof(this) This; + alias createImpl!This create; +} + +class MetaStruct : MetaAggregate +{ + alias typeof(this) This; + alias createImpl!This create; +} + +@property +auto meta(alias symbol, M : Meta)() +{ + __gshared static M m; + + { + lock.reader.lock; + scope(exit) + lock.reader.unlock; + if (m) + return m; + } + + lock.writer.lock; + scope(exit) + lock.writer.unlock; + + if (!m) + m = M.create!symbol; + return m; +} + +// only classes and structs for now +@property +auto meta(T)() +{ + static if (is(typeof(T.staticMetaObject))) + return T.staticMetaObject; + else static if (is(T == class)) + return meta!(T, MetaClass); + else static if (is(T == struct)) + return meta!(T, MetaStruct); + else + static assert(false, "No meta object for symbol " ~ T.stringof); +} + +/** + A run time attribute implementation that stores the attribute data in an + array of variants. + */ +class MetaVariantAttribute : MetaAttribute +{ + Variant[] values; + + private this() + { + } + + static MetaVariantAttribute create(string category, AttributeOptions opts, A...)() + { + auto ret = new MetaVariantAttribute; + ret.construct!(category, opts)(); + foreach(i, _; A) + { + static if (__traits(compiles, { ret.values ~= Variant(A[i]); } )) + ret.values ~= Variant(A[i]); + } + return ret; + } +} + +/** + A run time attribute implementation that stores the attribute data in an + assiciative array of variants. + */ +class MetaVariantDictAttribute : MetaAttribute +{ + Variant[string] values; + alias typeof(this) This; + + private this() + { + } + + static This create(string category, AttributeOptions opts, A...)() + { + auto ret = new This; + ret.construct!(category, opts)(); + foreach(i, _; A) + { + static if (i % 2 == 0 && __traits(compiles, { ret.values[A[i]] = Variant(A[i + 1]); } )) + ret.values[A[i]] = Variant(A[i + 1]); // PHOBOS BUG: phobos asserts on this + } + return ret; + } +} + +version (QtdUnittest) +{ + unittest + { + static void foo() {} + + static class C + { + mixin InnerAttribute!("variantAttribute", MetaVariantAttribute, "22", foo, 33); + mixin InnerAttribute!("variantDictAttribute", MetaVariantDictAttribute, + //"a", "33", // PHOBOS BUG: variant is unusable with AAs + "b", foo + //"c", 44 + ); + } + + auto attrs = meta!(C).attributes; + assert(attrs.length == 2); + auto attr = cast(MetaVariantAttribute)attrs[0]; + + assert(attr.name == "variantAttribute"); + assert(attr.values[0] == "22"); + assert(attr.values[1] == 33); + + auto attr2 = cast(MetaVariantDictAttribute) attrs[1]; + assert(attr2.name == "variantDictAttribute"); + //assert(attr2.values["a"] == "33"); + //assert(attr2.values["c"] == 44); + } +} \ No newline at end of file