Шаблон для вывода аргументов шаблона класса из функции-члена stati c - PullRequest
1 голос
/ 30 марта 2020

Моя ситуация

У меня есть два класса, предоставляемых внешним библиотекой, не находящейся под моим контролем, например, их интерфейс для меня фиксирован.

Первый - это шаблонный 2-D контейнер тип, который позволяет мне манипулировать содержимым, хранящимся как в функциях-членах, так и через необработанный указатель. Второй - это класс, содержащий набор функций-членов stati c, которые абстрагируют некоторые сильно оптимизированные для SIMD векторные операции. Большинство из них перегружены, чтобы принимать разные типы данных, однако все они используют один и тот же интерфейс, например

VecOperations::op (Type* dest, const Type* src, /* a variable number of operation specific arguments */, int len)

Чего я хочу достичь:

Я хочу перебрать первое измерение моего 2D контейнер и применить векторную операцию к каждому вектору во второй итерации на месте. Поэтому я хочу заменить, например,

auto** ptrs = conatiner.getArrayOfRawPointers();
for (int i = 0; i < container.getXDim(); ++i)
    VecOperations::foo (ptrs[i], ptrs[i], arg1, arg2, arg3, container.getYDim());

в идеале на что-то вроде этого (псевдокод)

forAllElements<VecOperations::foo> (container, arg1, arg2, arg3);

Это должно работать для всех типов типов, хранящихся в моем контейнере, которые поддерживаются вектором классы операций, а также для всех чисел векторных операций указываются c аргументы. Насколько мне известно, написать что-то вроде forAllElements выше невозможно.

Мое текущее решение:

Вместо этого я придумал:

template <typename ElemType, typename ...Args>
struct ForAllElemements
{
    template <void(*op)(ElemType*, const ElemType*, Args..., int)>
    static void call (Container<ElemType>& buffer, Args... args)
    {
        auto xDim = container.getXDim();
        auto yDim = container.getYDim();
        auto** ptrs = conatiner.getArrayOfRawPointers();

        for (int i = 0; i < xDim; ++i)
            op (ptrs[i], const_cast<const ElemType*>(ptrs[i]), args..., yDim);
    }
};

Это можно использовать как

// using a Container<float> and VecOperations::foo (float*, const float*, float, int, float, int)

ForAllElemements<float, float, int, float>::call<VecOperations::foo> (container, arg1, arg2, arg3);

Хотя в C ++ 17 вывод аргументов шаблона класса из конструктора работает, вывод его из вызова функции stati c не работает, насколько мне известно. Насколько я понимаю, это просто не определено, технически я не вижу причин, по которым

ForAllElemements::call<VecOperations::foo> (container, arg1, arg2, arg3);

должно быть невозможно, поскольку все типы шаблонов могут быть выведены из параметров, передаваемых в функцию stati c. .

Итак, я спрашиваю вас, есть ли какой-нибудь очень умный обходной путь или шаблон, о котором я не знаю, который сделал бы что-то подобное с C ++ 17 или с более поздними стандартами?

1 Ответ

1 голос
/ 30 марта 2020

Вывод аргумента шаблона класса происходит только на основе инициализатора для объекта класса. Здесь вам даже не нужен объект типа класса, просто чтобы использовать функции-члены stati c.

Но при резервном копировании может работать простой шаблон функции:

// C++20 will define std::type_identity_t; or just define your own:
template <typename T>
struct type_identity { using type = T; };
template <typename T>
using type_identity_t = typename type_identity<T>::type;

template <typename ...Args, typename ElemType>
void forAllElements(
    Container<ElemType> &c,
    void (*op)(ElemType*, const ElemType*, type_identity_t<Args>..., int), 
    Args...);

Указатель на функцию должен быть аргументом функции вместо аргумента шаблона. Это работает с простым Args... в сигнатуре указателя функции вместо type_identity_t<Args>..., если функция перегружена, но когда функция НЕ перегружена, компиляторам может потребоваться type_identity_t, предположительно, чтобы убедиться, что Args не вывести контекст там. (Я думаю, что в Стандарте есть неясное требование, вызывающее некоторые другие результаты ...)

Примечание Args может быть выведено только из аргументов forAllElements, но не из типа функции, а типа функции должно быть точное совпадение. Поэтому, если вы разрешите вывод этих типов, вам нужно быть осторожным с точными типами выражений, которые вы передаете. Приведите их при необходимости. Если в качестве константных значений используются литералы, вы можете использовать такие формы, как 1.0f, чтобы получить тип float и т. Д. c. Или вы можете указать типы аргументов, такие как forAllElements<float, int, float>, поэтому я поместил ...Args перед ElemType в шаблоне (хотя теперь ElemType никогда не может быть явно задан и должен выводиться из аргумента контейнера).

...