[英]Template function signature unpacking in C++/CLI
Is there a way to apply the function-signature-as-template-parameter unpacking idiom in a way that works with C++/CLI managed types?有没有办法以适用于 C++/CLI 托管类型的方式应用函数签名作为模板参数解包习语?
As an example, consider the following code:例如,请考虑以下代码:
#include <msclr/gcroot.h>
using namespace System;
template<typename... Args>
ref struct ManagedDelegate abstract
{
delegate void Fn(Args...);
};
template<typename Signature>
struct Method;
template<typename... Args>
struct Method<void(Args...)>
{
using Fn = typename ManagedDelegate<Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
void f1(int a, int b)
{
Console::WriteLine("a = {0}, b = {1}", a, b);
}
void f2(String^ s)
{
Console::WriteLine("s = {0}", s);
}
int main(array<String ^> ^args)
{
using Method1 = Method<void(int, int)>;
Method1 m1(gcnew Method1::Fn(&f1));
m1(4, 5);
using Method2 = Method<void(String^)>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
return 0;
}
(The separate ManagedDelegate
is a little annoying, but it's not possible to declare a delegate type inside a native class, sadly.) (单独的
ManagedDelegate
有点烦人,但ManagedDelegate
是,不可能在本机类中声明委托类型。)
If you comment out all the Method2
code at the bottom, then this compiles and runs as you'd expect -- it calls f1(4, 5)
and prints accordingly.如果您注释掉底部的所有
Method2
代码,那么它会按照您的预期编译和运行——它调用f1(4, 5)
并相应地打印。
Trying to do the same thing with a managed type argument, however, causes the template to fail to match the specialisation and results in:但是,尝试使用托管类型参数执行相同的操作会导致模板无法匹配特化并导致:
error C2027: use of undefined type 'Method<void (System::String ^)>'
Is this a compiler bug, or is there some way to get this to work?这是一个编译器错误,还是有什么方法可以让它工作? There are some constraints that I do need to keep to in order for this to work in my real code:
为了让它在我的真实代码中工作,我确实需要遵守一些限制:
Method
needs to be an unmanaged type that contains a gcroot
of the delegate type. Method
必须是包含委托类型的gcroot
的非托管类型。std::forward
is also intended, since this also upsets managed types.std::forward
也是有意的,因为这也会扰乱托管类型。 (And I'm not intending to pass native reference arguments anyway, so it's unnecessary.) While I prefer automatically creating the delegate type from the signature as shown here, it would also be acceptable to create the delegate outside and pass it in instead of a signature, eg:虽然我更喜欢从这里显示的签名自动创建委托类型,但也可以在外部创建委托并将其传入而不是签名,例如:
delegate void Method1Delegate(int, int); ... Method<Method1Delegate> m1(gcnew Method1Delegate(&f1));
But either way, I do need an Args...
parameter list (both for the operator()
and for other reasons).但无论哪种方式,我都需要一个
Args...
参数列表(出于operator()
和其他原因)。 And I don't think it's possible to extract this from a managed delegate type.而且我认为不可能从托管委托类型中提取它。
operator()
to keep using Args...
from the Method
type so that it won't accept the "wrong" parameters.operator()
继续使用Method
类型中的Args...
以便它不会接受“错误”参数。 (I did have an older version of the code that templated Args
directly on operator()
, but this gives IntelliSense the false impression that it would accept any parameters.) operator()
上对Args
进行模板化,但这给 IntelliSense 一个错误的印象,即它会接受任何参数。)void
.void
。 I know how to do that with the above code -- just that any rewrite shouldn't prevent that working if possible. EDIT: as demonstration that the managed args sort of work in variadics, this can be added:编辑:为了证明托管 args 在可变参数中工作,可以添加:
template<>
struct Method<void(String^)>
{
using Fn = typename ManagedDelegate<String^>::Fn;
Method(Fn^ m) : m_Method(m) {}
template<typename... Args>
void operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
This works, provided that the call is changed to m2(gcnew String("hello world"));
这有效,前提是调用更改为
m2(gcnew String("hello world"));
to force the correct type, or operator()
is changed to accept a single String^
parameter instead of an open variadic.强制正确的类型,或
operator()
更改为接受单个String^
参数而不是开放的可变参数。 So the problem is definitely in matching a variadic template specialisation, not elsewhere.所以问题肯定在于匹配可变参数模板专业化,而不是其他地方。
I can mostly do what I want by abandoning the function-signature-specialisation and just specifying the signature components separately:我可以通过放弃函数签名专业化并单独指定签名组件来做我想做的事情:
template<typename R, typename... Args>
ref struct ManagedDelegate abstract
{
delegate R Fn(Args...);
};
template<typename R, typename... Args>
struct Method
{
using Fn = typename ManagedDelegate<R, Args...>::Fn;
Method(Fn^ m) : m_Method(m) {}
R operator()(Args... args)
{
auto method = safe_cast<Fn^>(m_Method);
return method(args...);
}
private:
msclr::gcroot<Fn^> m_Method;
};
//...
using Method2 = Method<void, String^>;
Method2 m2(gcnew Method2::Fn(&f2));
m2("hello world");
This is not ideal, but it does compile and work.这并不理想,但它确实可以编译和工作。 I'm still interested in any alternative answer that does support unpacking a function signature type, however.
但是,我仍然对支持解包函数签名类型的任何替代答案感兴趣。 (And I filed the original issue as a compiler bug .)
(我将原始问题作为编译器错误提交。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.