C ++ 17 Как имитировать c функцию Джулии «promo_type», используя шаблон variadi c - PullRequest
0 голосов
/ 22 апреля 2020

По некоторым личным причинам я работал над C ++ версией Julia's SparseMatrixCSC, которая задает c в проекте, который я портирую на C ++ и где Armadillo SpMat не может быть идеальной альтернативой.

template <typename Tv, typename Ti>
struct SparseMatrixCSC
{
    size_t m, n;
    arma::Col<Ti> colptr;
    arma::Col<Ti> rowval;
    arma::Col<Tv> nzval;
};

Некоторые функции Julia, такие как blockdiag(), допускают использование вариабельного c -образного числа разреженной матрицы на входе и одной матрицы 'blockdiag' на выходе. Скриптовый код Джулии допускает некоторый обобщенный метод c, который можно легко перенести с помощью C ++ 17, например, сбор размера n-матрицы во входных данных, например:

template <typename... Args>
constexpr auto blockdiag(const Args& ...args)
{
    auto mX = details::apply([](auto x) { return size(x, 1); }, args...);
    auto nX = details::apply([](auto x) { return size(x, 2); }, args...);
    auto m = sum(mX);
    auto n = sum(nX);
    ...

Где внутренний * Функция 1010 * позволяет рекурсивно собирать строки / столбцы на входной n-матрице. Окончательный размер матрицы суммируется в m и n. Нет проблем, код работает отлично.

Но теперь моя проблема состоит в том, чтобы вычислить typename параметры матрицы вывода, собирая / продвигая типы Tv (значение) и Ti (индексирование) от чего-то похожего. По характеру матрицы разреженности типы Tv и Ti являются числовыми значениями. И, более конкретно, Ti является целочисленным типом.

Как я понимаю, поскольку я настоящий новичок ie в метапрограммировании с последними стандартами C ++, лучший способ сделать это - использовать <type_traits> std::common_type. Но я не вижу, как распаковать мои шаблонные аргументы c variadi (которые содержат SparseMatrixCS C) и развернуть параметры std::common_type<...> с результатом функтора, который получает decltype того или иного внутреннего вектора столбца arma::Col<T>. Что-то вроде:

template <typename Func, typename ... Args>
constexpr auto promote_type(Func f, Args const&... args)
{
    return typename std::common_type<(f(args), ...)>::type;
}

Вызывается в предыдущей функции blockdiag с помощью:

typename Tv = details::promote_type([](auto x) { return decltype(x.nzval); }, args...);
typename Ti = details::promote_type([](auto x) { return decltype(x.rowval); }, args...);

К сожалению, слишком плохо для компилятора VS2019 16.5.4. Более того, я почти уверен, что выражение сгиба (f(args), ...) недопустимо и не может использоваться в качестве параметра шаблона.

Итак, мне нужна ваша помощь, и я очень благодарен вам за это.

РЕДАКТИРОВАТЬ: Чтобы ответить Барри, promote_type - это функция Джулии, описанная так:

Повышение означает преобразование значений смешанных типов в один общий тип. promote_type представляет поведение продвижения по умолчанию в Julia, когда операторам (обычно математическим) задаются аргументы разных типов. promote_type обычно пытается вернуть тип, который может по крайней мере приблизиться к большинству значений любого типа ввода без чрезмерного расширения. Некоторая потеря терпима; например, promote_type(Int64, Float64) возвращает Float64, хотя строго, не все Int64 значения могут быть представлены в точности как Float64 значения.

1 Ответ

2 голосов
/ 22 апреля 2020

Поскольку каждое из args... здесь:

template <typename... Args>
constexpr auto blockdiag(const Args& ...args)

само по себе является SparseMatrixCSC, давайте сделаем это более явным:

template <typename... T, typename... U>
constexpr auto blockdiag(const SparseMatrixCSC<T, U>& ...args)

Оба эти требования предъявляют blockdiag проще для понимания (теперь мы знаем, что нужно) и облегчает поиск ответа: у нас есть T s и U s прямо здесь, поэтому просто используйте их:

template <typename... T, typename... U>
constexpr auto blockdiag(const SparseMatrixCSC<T, U>& ...args)
    -> SpareMatrixCSC<std::common_type_t<T...>, std::common_type_t<U...>>;
...