2015-10-07 1 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

Заменить класс с структурой: избегать использования CTFE для инициализации поля с new. Это не связано с моим вопросом.

+0

Я на самом деле не уверен, что это может быть сделано. Мне удалось получить 'b.opDispatch! (« P2 », int, 10) (5);' работает, но не 'b.p2! (Int, 10) (5);'. Вы думаете, что они одно и то же, но, видимо, нет. –

+0

Я думаю, что ошибки, сгенерированные opDispatch, отбрасываются в пользу сообщения «no property», поэтому вы не получите фактическое сообщение об ошибке. Но я думаю, что микшинг плохо сформирован, это не полное утверждение. Я думаю, что параметр '(args)' pass pass принадлежит в mixin и не забывает точку с запятой – weltensturm

+0

Вместо 'opDispatch' вы можете попробовать« 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 вместо этого, потому что пользователь уже может определить конкурирующую 'opDispatch'. Кроме того, если впоследствии структура/класс будет изменена, чтобы добавить, например, элемент 'p2' (или он уже имеет один), реализация' opDispatch' будет незаметно затенена вместо ошибки, которую вы получите из объявления mixin , – Meta

1

Если на самом деле это не представляется возможным с opDispatch как подсказывает ответ Адам Д. Ruppe, тогда ваш единственный выход может быть, чтобы включить для струнных миксинов, которые немного уродливы, но невероятно мощны. Если использование 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); 
} 

Я включил следующий код со всем UnitTests раздели. Важно отметить, что forwardToMember в настоящее время не поддерживает перегрузки для регулярных функций; он просто выберет первый экземпляр указанной функции, которую он найдет. I думаю они должен работа для функций шаблона, однако.

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