アダム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;
}
:これは、より汎用的なソリューションです。私は 'b.opDispatch!(" p2 "、int、10)(5);' b.p2!(int、10)(5); 'ではなく 'working'を取得できました。あなたは彼らが同じものだと思うでしょうが、明らかにそうではありません。 –
私はopDispatchによって生成されたエラーは、 "プロパティなし"メッセージのために削除されると考えています。実際のエラーメッセージは表示されません。しかし、私はミックスインが悪いと思う、それは完全な声明ではない。私は '(args)'パラメータのパスがmixinに属していて、 'opDispatch'の代わりにセミコロン – weltensturm
を忘れないで、' alias b this; 'を試すことができます。 –