首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >opDispatch和编译时间参数

opDispatch和编译时间参数
EN

Stack Overflow用户
提问于 2015-10-07 17:31:02
回答 3查看 358关注 0票数 6

如何使用opDispatch转发到具有编译时参数的方法。见下面的代码:

代码语言:javascript
复制
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);
 }

编辑

用struct替换类:避免使用CTFE进行new的字段初始化。这与我的问题无关。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-10-19 19:37:28

D模板系统功能非常强大。这是更通用的解决方案:

代码语言:javascript
复制
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

票数 7
EN

Stack Overflow用户

发布于 2015-10-19 13:53:10

如果正如AdamD.Ruppe的答案所暗示的那样,在opDispatch中这确实是不可能的,那么您唯一的方法可能是使用string mixins,它有点难看,但是功能非常强大。除非使用opDispatch是很难的要求,否则字符串混合可能是唯一的方法。

幸运的是,大多数的工作是已经做了 (而且它比您想象的还要复杂)。代码本身是多毛的,但是您所需要做的就是如下所示:

代码语言:javascript
复制
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目前不支持常规函数的重载;它只是选择它找到的指定函数的第一个实例。不过,我认为它们应该适用于模板函数。

代码语言:javascript
复制
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;
}
票数 1
EN

Stack Overflow用户

发布于 2015-10-18 05:58:04

您需要使用同名模板模式,并在接受常规的opDispatch字符串参数的外部opDispatch模板中有一个包含编译时参数的opDispatch函数。您还可以有多个遵循常规重载规则的内部opDispatch函数(和字段)。

代码语言:javascript
复制
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") );
}

产生的输出:

代码语言:javascript
复制
bar
0
foo Foo foo

DPaste链接:http://dpaste.dzfl.pl/6e5cfca8b702

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32998781

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档