2015-10-07 12 views
6

Come posso utilizzare opDispatch per l'inoltro al metodo con i parametri di compilazione del tempo. Vedere codice qui sotto:opDispatch e parametri di compilazione del tempo

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

Sostituire classe con struct: evitare l'utilizzo di CTFE per l'inizializzazione campo con new. Non è legato alla mia domanda.

+0

io non sono realmente sicuro questo può essere fatto. Sono riuscito a ottenere 'b.opDispatch! (" P2 ", int, 10) (5);' funzionante, ma non 'b.p2! (Int, 10) (5);'. Penseresti che siano la stessa cosa ma apparentemente no. –

+0

Penso che gli errori generati da opDispatch vengano abbandonati a favore di un messaggio "nessuna proprietà", quindi non si otterrà il messaggio di errore effettivo. Ma penso che il mixin sia mal formato, non è una dichiarazione completa. Penso che il passaggio del parametro '(args)' appartenga al mixin, e non dimentichi il punto e virgola – weltensturm

+0

Invece di 'opDispatch', puoi provare 'alias b this;'. –

risposta

7

D modello di sistema è molto potente. Questo è più soluzione generica:

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

È necessario utilizzare il modello di modello omonimo e avere una funzione opDispatch con i parametri di compilazione in un modello opDispatch esterno che utilizza il normale parametro di stringa opDispatch. Puoi anche avere più funzioni opDispatch interne (e campi) che seguono regole di sovraccarico regolari.

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")); 
} 

produce l'output:

bar 
0 
foo Foo foo 

collegamento DPaste: http://dpaste.dzfl.pl/6e5cfca8b702

+0

Voglio creare un wrapper generale e non posso prevedere tutte le possibili varianti di opDispatch. Controllerò la risposta @Meta (dal forum dlang). Propone di utilizzare un mix speciale forwardToMember – sibnick

+0

L'implementazione originale di 'forwardToMember' ha usato' opDispatch', ma ho scelto di utilizzare invece le dichiarazioni mixin perché l'utente potrebbe già definire un 'opDispatch' concorrente. Inoltre, se la struct/class viene modificata in seguito per aggiungere, ad esempio, un membro 'p2' (o ne ha già uno), l'implementazione di' opDispatch' verrà ombreggiata silenziosamente, invece dell'errore che si otterrà da una dichiarazione di mixin . – Meta

1

Se in effetti questo non è possibile con opDispatch come suggerisce la risposta di Adam D. Ruppe, quindi l'unica soluzione potrebbe essere quella di trasformare per impastare mixin, che sono un po 'brutti ma incredibilmente potenti. A meno che l'utilizzo di opDispatch sia un requisito difficile, i mixin di stringhe potrebbero essere l'unico modo.

Fortunatamente, la maggior parte del lavoro svolto è already done (ed è più complesso di quanto si possa pensare). Il codice stesso è peloso, ma tutto quello che dovete fare è la seguente:

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); 
} 

Ho incluso il codice qui sotto con tutti i Unittests spogliato. È importante notare che attualmente lo forwardToMember non supporta gli overload per le normali funzioni; sceglierà solo la prima istanza della funzione specificata che trova. I penso loro dovrebbero funzionare per le funzioni di modello, tuttavia.

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; 
} 
Problemi correlati