简体   繁体   English

C++/CLI 中的模板函数签名解包

[英]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的非托管类型。
  • The use of templates rather than generics is intended.打算使用模板而不是泛型。 I don't think any of this is possible with generics anyway.无论如何,我认为泛型不可能实现这些。
  • The non-use of 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.而且我认为不可能从托管委托类型中提取它。

  • I also want the 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 一个错误的印象,即它会接受任何参数。)
  • If there is a way to do the above, then I'd probably want a version that works with a templated return type as well as just 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM