Разреженный массив в compile-time
От: avovana Россия  
Дата: 05.07.18 11:44
Оценка:
Дорогие форумчане, здравствуйте!

В соседней ветке по плюсам развернулась дискуссия о том, зачем нужны шаблоны.
Да вот хотя бы для этого — нужно решить задачу на современных плюсах(С++14, С++17) с использованием шаблонов.

В решение данной задачи помогла в т.ч. книга
Автор: LaptevVV
Дата: 21.05.18
, описанная Валерием Лаптевым.
Три года назад я начал изучать программирование по его книги "C++. Экспресс курс".
Тогда для меня задача по вычислению дня рождения в зависимости от високосности года было верхом программисткой мысли.
Сейчас пошли уже такие "игрушки"

Выложу:

    1. Саму задачу.
    2. Что получилось.
    3. В чем прошу помощи.

1. Задача.

Как будет видно из кода, необходимо реализовать разреженный массив:
#include <cstdint>
#include <cassert>
#include <type_traits>

template<typename T, uint64_t Mask>
class SparseArray {
public:
    using ElementType = T;

    //Написать constexpr конструкторы:
    //1. Дефолтный конструктор SparseArray()
    //2. Конструктор, принимающий значения именно всех элементов прореженного массива

    template<uint8_t Index>
    constexpr T get() {  //Возвращает элементы именно по значению, не по ссылке.
        //Вернуть дефолтное значение T, если элемента нет в массиве, иначе вернуть существующий элемент из массива.
    }

    //Исключить оператор при помощи SFINAE если нет оператора + для пары T и TOther
    template<typename TOther, uint64_t MaskOther>
    constexpr auto operator +(const SparseArray<TOther, MaskOther>& other) {
        //Вернёт новый SparseArray с поэлементной суммой двух исходных массивов
    }

private:
    T values[/*Посчитать размер прореженного массива*/];
};

int main() {

    SparseArray<float,  3 > array0(1.0f, 2.0f);
    SparseArray<double, 10> array1(      4.0,    7.0);

    auto sum = array0 + array1;

    static_assert(sizeof(sum) == sizeof(double) * 3, "Invalid sum array size");
    static_assert(sizeof(array0) == sizeof(float) * 2, "Invalid array size");
    static_assert(sizeof(array1) == sizeof(double) * 2, "Invalid array size");

    assert((std::is_same_v<typename decltype(sum)::ElementType, double> == true));

    assert(sum.get<0>() == 1.0);
    assert(sum.get<1>() == 6.0);
    assert(sum.get<2>() == 0.0);
    assert(sum.get<3>() == 7.0);

    SparseArray<float, 3> array2;
    assert(array2.get<0>() == 0.0f);
    assert(array2.get<1>() == 0.0f);

    return 0;
}


2. Что получилось.

Получилось всё, кроме реализации оператора сложения:
// My SparseArray implementation

#include <cstdint>
#include <iostream>
#include <cassert>
#include <utility>
#include <bitset>

template<typename T, uint64_t Mask>
class SparseArray 
{
    public:
    using ElementType = T;

    constexpr SparseArray() 
        : values() { 
            std::cout << "Default ctor" << '\n';
        }

    template<typename... Args>
    constexpr SparseArray(Args&&... args) 
    : values{args...} {
        std::cout << "Args ctor. Mask = " << Mask << '\n';
    }

    template<uint8_t Index>
    constexpr ElementType get() const {
        if(isSet(Index))
            return values[countEntityNumber(Index)];
        else
            return T();
    }

    constexpr static std::size_t countEntityNumber (size_t index) {
        uint64_t subMask{0};

        for(uint64_t i = 0; i < index; ++i) {
            subMask = subMask | 0b1;

            if(i + 1 < index)
                subMask <<= 1;
        }
        return popcount(Mask & subMask);
    }

    constexpr static std::size_t popcount (size_t value) {
        return value != 0 ? (value & 0b1) + popcount(value >> 1) : 0;
    }

    constexpr static std::size_t isSet (size_t pos) {
        return (Mask >> pos) & 0b1;
    }

    static const int mask = Mask;
    static const int size = popcount(Mask);

    ElementType values[size];  
};

int main ()
{
    SparseArray<float,  3> array0(1.0f, 2.0f);
    SparseArray<double, 10> array1(      4.0,    7.0);

    static_assert(sizeof(array0) == sizeof(float) * 2, "Invalid array size");
    static_assert(sizeof(array1) == sizeof(double) * 2, "Invalid array size");

    SparseArray<float, 3> array2;
    assert(array2.get<0>() == 0.0f);
    assert(array2.get<1>() == 0.0f);

    return 0 ;
}


3. В чем прошу помощи.

Прошу помочь с реализацией operator+.
То что я сделал, к сожалению, не компилируется.
Ошибка в попытке использования объекта в выражение std::index_sequence.
Не получается всё в compile-time сделать.
Описал конкретно эту проблему здесь.

с++14 с++17 templates constexpr
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.