Распаковка сигнатуры функции шаблона в C ++ / CLI - PullRequest
0 голосов
/ 10 января 2020

Есть ли способ применить идиому для распаковки функции-сигнатуры-шаблона-шаблона таким образом, чтобы он работал с управляемыми типами 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. Я знаю, как это сделать с помощью приведенного выше кода - только то, что любое переписывание не должно препятствовать тому, чтобы это работало, если это возможно.

РЕДАКТИРОВАТЬ: как демонстрация того, что управляемые аргументы работают в вариациях , это может быть добавлено:

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. Так что проблема определенно в том, чтобы соответствовать специализации шаблона variadi c, а не где-либо еще.

1 Ответ

0 голосов
/ 13 января 2020

В основном я могу делать то, что хочу, отказавшись от специализации function-signature-special и просто указав компоненты сигнатуры отдельно:

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

Это не идеально, но оно компилируется и работает. Однако я по-прежнему заинтересован в любом альтернативном ответе, который поддерживает распаковку типа сигнатуры функции. (И я подал исходную проблему как ошибка компилятора .)

...