在C ++ / CLI中解压缩模板函数签名

问题描述 投票:0回答:1

是否有一种方法可以以与C ++ / CLI受管类型一起使用的方式应用功能签名作为模板参数的包装习惯?

作为示例,请考虑以下代码:

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

(单独的ManagedDelegate有点烦人,但遗憾的是,无法在本机类中声明委托类型。]

如果您注释掉底部的所有Method2代码,则此代码将按预期运行并运行-它将调用f1(4, 5)并进行相应打印。

然而,尝试使用托管类型参数做同样的事情,会导致模板无法匹配专业化并导致:

error C2027: use of undefined type 'Method<void (System::String ^)>'

这是编译器错误,还是有某种方法可以使其正常工作?为了使它在我的真实代码中起作用,我确实需要遵守一些约束:

  • [Method必须是包含委托类型的gcroot的非托管类型。
  • 打算使用模板而不是泛型。我认为使用泛型还是不可能做到这一点的。
  • 也打算不使用std::forward,因为这也会使托管类型不佳。 (而且我也不打算传递本机引用参数,所以这是不必要的。)
  • 虽然我更喜欢从签名自动创建委托类型,如此处所示,也可以在外部创建委托并将其传递来代替签名,例如:

    delegate void Method1Delegate(int, int);
    ...
    Method<Method1Delegate> m1(gcnew Method1Delegate(&f1));
    
  • 但是无论哪种方式,我的确需要一个Args...参数列表(用于operator()和其他原因)。而且我认为不可能从托管委托类型中提取它。

  • 我也希望operator()继续使用Args...类型的Method,以便它不会接受“错误”的参数。 (我确实有一个较旧的代码版本,直接在Args上模板化operator(),但这给IntelliSense带来了错误的印象,即它可以接受任何参数。)
  • 如果有实现上述目的的方法,那么我可能想要一个既可以使用模板化返回类型又可以使用void的版本。我知道如何使用上面的代码来做到这一点-只要可能,任何重写都不应阻止该工作。

编辑:作为可论证的可变参数管理型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;
};

[此方法有效,只要将呼叫更改为m2(gcnew String("hello world"));以强制使用正确的类型,或者将operator()更改为接受单个String^参数而不是开放的可变参数即可。因此,问题肯定出在匹配可变参数模板专业化上,而不是其他方面。

c++ visual-studio-2017 c++-cli c++17
1个回答
0
投票

我通常可以通过放弃功能-签名-专业化而仅指定签名组件来做我想做的事情:

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");

这不是理想的方法,但是确实可以编译和工作。但是,我仍然对确实支持解压缩函数签名类型的任何替代答案感兴趣。 (我将原始问题归档为compiler bug。)

© www.soinside.com 2019 - 2024. All rights reserved.