2015-10-07 14 views
6

opDispatchを使用して、コンパイル時のパラメータを持つメソッドに転送する方法を教えてください。構造体とクラスを置き換えopDispatchおよびコンパイル時のパラメータ

import std.stdio; 

struct B{ 
    auto p1(T)(T arg) { 
     writeln("p1: ", arg); 
    } 
    auto p2(T, int C)(T s) { 
     writeln("p2: ", s, "/", C); 
    } 
} 

struct C(T) { 
    T b; 

    auto opDispatch(string s, Args...)(Args args) { 
     mixin("b."~s)(args); 
    } 
} 

void main() { 
    C!B b; 
    //fine: compiler is smart enough 
    b.p1("abc"); 
    //oops: "no property 'p2' for type ..." 
    b.p2!(int, 10)(5); 
    B origB; 
    //fine: 
    origB.p2!(int, 10)(5); 
} 

EDIT

newとフィールドの初期化のためのCTFEの使用を避けるため、以下のコードを参照してください。それは私の質問とは関係ありません。

+0

:これは、より汎用的なソリューションです。私は 'b.opDispatch!(" p2 "、int、10)(5);' b.p2!(int、10)(5); 'ではなく 'working'を取得できました。あなたは彼らが同じものだと思うでしょうが、明らかにそうではありません。 –

+0

私はopDispatchによって生成されたエラーは、 "プロパティなし"メッセージのために削除されると考えています。実際のエラーメッセージは表示されません。しかし、私はミックスインが悪いと思う、それは完全な声明ではない。私は '(args)'パラメータのパスがmixinに属していて、 'opDispatch'の代わりにセミコロン – weltensturm

+0

を忘れないで、' alias b this; 'を試すことができます。 –

答えて

7

Dテンプレートシステムは非常に強力です。私は実際にこれを行うことができるかわからない

import std.stdio; 
class B { 
    auto p1(T)(T arg) { writeln("p1: ", arg); } 
    auto p2(T, int C)(T s) { writeln("p2: ", s, "/", C); } 
} 
class C(T) { 
    T b = new T; 
    template opDispatch(string s) { 
     template opDispatch(TARGS...) { 
      auto opDispatch(ARGS...)(ARGS args) { 
       static if(TARGS.length) return mixin("b." ~ s ~ "!TARGS(args)"); 
       else return mixin("b." ~ s ~ "(args)"); 
      } 
     } 
    } 
} 

void main() { 
    auto b = new C!(B)(); 
    b.p1("abc"); 
    b.p2!(int, 10)(5); 
} 

http://dpaste.dzfl.pl/791c65d0e4ee

0

名義テンプレートパターンを使用し、通常のopDispatch文字列パラメータを使用する外部opDispatchテンプレート内にコンパイル時パラメータを含むopDispatch関数を用意する必要があります。通常の過負荷ルールに従う複数の内部opDispatch関数(およびフィールド)を持つこともできます。

import std.stdio; 

struct Foo { 

    public template opDispatch(string name) { 

     public string opDispatch() { 
      return name; 
     } 

     public T opDispatch(T)() { 
      return T.init; 
     } 

     public string opDispatch(T)(string s) { 
      return name ~ ' ' ~ T.stringof ~ ' ' ~ s; 
     } 
    } 
} 

void main() 
{ 
    Foo foo; 
    writeln(foo.bar); 
    writeln(foo.baz!int); 
    writeln(foo.foo!Foo("foo")); 
} 

は出力を生成します。

bar 
0 
foo Foo foo 

DPasteリンク:http://dpaste.dzfl.pl/6e5cfca8b702

+0

私は一般的なラッパーを作りたいと思いますし、可能なすべてのopDispatchの変種を予測することはできません。 @Metaの回答をチェックします(dlangフォーラムから)。彼は特別なforwardToMember mixinを使うことを提案しています – sibnick

+0

'forwardToMember'の元の実装では' opDispatch'を使っていましたが、私はmixin宣言を使用しました。また、後で 'p2'メンバを追加するためにstruct/classが変更された場合(または既にstructがある場合)、' opDispatch'実装は静かにshadowinされますが、mixin宣言から得られるエラーではありません。 – Meta

1

アダムD. Ruppeの答えが示唆するように確かに、これはopDispatchでは不可能であれば、あなたの唯一の頼みの綱は、ターンである可能性があります文字列mixinsに、少し醜いしかし信じられないほど強力です。 opDispatchを使用しない限り、文字列ミックスインが唯一の方法かもしれません。

幸いなことに、これを行う作業のほとんどはalready doneです(考えているよりも複雑です)。コード自体は毛深いですが、次のようにすべてを行う必要がある:

import std.stdio; 

class B{ 
    auto p1(T)(T arg) { 
     writeln("p1: ", arg); 
    } 
    auto p2(T, int C)(T s) { 
     writeln("p2: ", s, "/", C); 
    } 
} 

class C(T) { 
    T b = new T; 

    mixin(forwardToMember!(b, "p1", "p2")); 
} 

void main() { 
    auto b = new C!(B)(); 
    b.p1("abc"); 

    //This now compiles and correctly forwards to b.b.p2 
    b.p2!(int, 10)(5); 
} 

私は剥奪すべてのユニットテストで以下のコードを含めました。 forwardToMemberは現在、通常の関数のオーバーロードをサポートしていないことに注意することが重要です。見つかった指定された関数の最初のインスタンスを選択するだけです。私はだと思います。しかし、彼らはでなければなりません。はテンプレート関数で動作します。

import std.traits; 
import std.meta; 

private alias isSomeStringType(alias str) = isSomeString!(typeof(str)); 

template forwardToMember(alias member, symbols...) 
if (symbols.length > 0 && allSatisfy!(isSomeStringType, symbols)) 
{ 
    static if (symbols.length == 1) 
    { 
     static assert(hasMember!(typeof(member), symbols[0]), 
      "Cannot dispatch: member '" ~ member.stringof ~ 
       "' does not support method '" ~ symbols[0] ~ "'"); 

     enum forwardToMember = genWrapperMixin!(member, symbols[0]); 
    } 
    else 
    { 
     enum forwardToMember = forwardToMember!(member, symbols[0]) ~ forwardToMember!(member, symbols[1..$]); 
    } 
} 

private enum SymbolKind 
{ 
    function_, 
    property, 
    templateFunction, 
    fieldFunction, 
    field, 
    aliasableSym, 
} 

//Ugly hack but there's no other way to do this 
private template isTemplateFunction(f...) 
if (f.length == 1) 
{ 
    import std.algorithm: among, balancedParens, canFind, count; 

    static if (!__traits(isTemplate, f[0])) 
    { 
     enum isTemplateFunction = false; 
    } 
    else 
    { 
     enum fstr = f[0].stringof; 

     //A template function's .stringof is of the format <function name>(<template args>)(<function args>) 
     //so match on the number of brackets to determine whether it's a template function or not 
     enum isTemplateFunction = __traits(isTemplate, f) 
            && fstr.balancedParens('(', ')') 
            && (fstr.canFind("if") 
              || fstr.count!(c => cast(bool)c.among!('(', ')')) == 4); 
    } 
} 

private template getSymbolKind(Aggregate, string symbol) 
{ 
    import std.traits; 
    import std.typetuple; 

    enum getMemberMixin = "Aggregate." ~ symbol; 

    //Appears in Aggregate.tupleof so it must be a field 
    static if (staticIndexOf!(symbol, FieldNameTuple!Aggregate) > -1) 
    { 
     //Check if it's a regular field or a function pointer 
     static if (isSomeFunction!(mixin(getMemberMixin))) 
      enum getSymbolKind = SymbolKind.fieldFunction; 
     else 
      enum getSymbolKind = SymbolKind.field; 
    } 
    else 
    { 
     static if (isSomeFunction!(mixin(getMemberMixin)) 
         && !__traits(isStaticFunction, mixin(getMemberMixin)) 
         || isTemplateFunction!(mixin(getMemberMixin))) 
     { 
      static if (isTemplateFunction!(mixin(getMemberMixin))) 
       enum getSymbolKind = SymbolKind.templateFunction; 
      else static if (functionAttributes!(mixin(getMemberMixin)) & FunctionAttribute.property) 
       enum getSymbolKind = SymbolKind.property; 
      else 
       enum getSymbolKind = SymbolKind.function_; 
     } 
     //If it's not a member function/property then it should be an aliasable static symbol 
     else static if (__traits(compiles, { alias _ = Alias!(mixin(getMemberMixin)); })) 
      enum getSymbolKind = SymbolKind.aliasableSym; 
     else 
      static assert(0, "Error: " ~ Aggregate.stringof ~ "." ~ symbol ~ " is not a member function, field, or aliasable symbol"); 
    } 
} 

private template genWrapperMixin(alias member, string symbol) 
{ 
    import std.algorithm: among; 
    import std.string: format; 

    enum symbolKind = getSymbolKind!(typeof(member), symbol); 

    static if (symbolKind.among!(SymbolKind.function_, SymbolKind.property, SymbolKind.fieldFunction)) 
    { 
     alias MethodType = FunctionTypeOf!(mixin("member." ~ symbol)); 
     enum funAttrs = functionAttributes!MethodType; 
     enum methodIsStatic = __traits(isStaticFunction, mixin("member." ~ symbol)); 
     enum funAttrStr = getFunctionAttributeStr(funAttrs) ~ (methodIsStatic ? " static" : ""); 

     //Workaround Issue 14913 
     enum returnStr = funAttrs & FunctionAttribute.return_ ? "return" : ""; 

     enum genWrapperMixin = q{ 
      %3$s 
      auto ref %2$s(ParameterTypeTuple!(FunctionTypeOf!(%1$s.%2$s)) args) %4$s 
      { 
       import std.functional: forward; 

       return %1$s.%2$s(forward!args); 
      } 
     } 
     .format(member.stringof, symbol, funAttrStr, returnStr); 
    } 
    else static if (symbolKind == SymbolKind.templateFunction) 
    { 
     enum genWrapperMixin = q{ 
      template %2$s(TemplateArgs...) 
      { 
       auto ref %2$s(FunArgs...)(auto ref FunArgs args) 
       { 
        import std.functional: forward; 

        return %1$s.%2$s!(TemplateArgs)(forward!args); 
       } 
      } 
     } 
     .format(member.stringof, symbol); 
    } 
    else static if (symbolKind == SymbolKind.field) 
    { 
     alias FieldType = typeof(mixin("member." ~ symbol)); 
     alias FA = FunctionAttribute; 
     enum attrStr = getFunctionAttributeStr(FA.pure_ | FA.nothrow_ | FA.safe | FA.nogc); 
     enum genWrapperMixin = q{ 
      @property %3$s %4$s %1$s() 
      { 
       return %2$s.%1$s; 
      } 

      @property %3$s void %1$s(%4$s val) 
      { 
       %2$s.%1$s = val; 
      } 
     } 
     .format(symbol, member.stringof, attrStr, FieldType.stringof); 
    } 
    else static if (symbolKind == SymbolKind.aliasableSym) 
    { 
     enum genWrapperMixin = q{ 
      alias %1$s = %2$s.%1$s; 
     } 
     .format(symbol, member.stringof); 
    } 
    else 
     static assert(member.stringof ~ "." ~ symbol ~ " has unexpected kind '" ~ symbolKind.to!string); 
} 

private string getFunctionAttributeStr(FunctionAttribute funAttrs) 
{ 
    import std.algorithm: among, filter, joiner, map, strip; 
    import std.conv: to; 

    string funAttrStr; 
    with (FunctionAttribute) 
    { 
     funAttrStr = [EnumMembers!FunctionAttribute] 
         .filter!(e => (funAttrs & e) && e != none && e != ref_ && e != return_) 
         .map!(e => e.to!string.strip('_')) 
         .map!(s => s.among!("safe", "trusted", "system", "nogc", "property") ? '@' ~ s : s) 
         .joiner(" ") 
         .to!string; 
    } 

    return funAttrStr; 
} 
関連する問題