Насколько хорош этот способ абстрагирования типа данных класса Matrix? - PullRequest
0 голосов
/ 12 июня 2018

Мне недавно пришлось написать собственную библиотеку умножения матриц.Первоначально я написал его как шаблонный класс, затем я понял, что большинство классов используют класс матрицы, не заботясь о типе данных, используемом классом Matrix, поскольку они просто выполняют определенное преобразование и для матрицы без проверки результата.Таким образом, они действительно не должны знать о типах данных.Я думал о создании матричного класса с пустым указателем на данные.

    class Mat
    {
    private:
        void *data;
        int dtype; // data type used by matrix
        int cols, rows;

        template<class type>
        Mat add(const Mat& a, type unused); // notice unused parameters

    public:
        Mat(int dtype);
        ~Mat();
        Mat operator+(const Mat& a);
        template<class type>
        type* getdata(); // this only function  that exposes the 
        //datatype to the user since they want to read the elements
    };

Мне нужна функция сложения, чтобы быть шаблоном, поскольку он ускоряет вычисления с использованием встроенных функций SSE, и я абстрагировал встроенные функции с помощью шаблона.классы.Поэтому я подумал о добавлении неиспользуемого параметра в шаблон добавления, чтобы компилятор мог различать разные шаблоны.

Mat Mat::operator+(const Mat& a)
{
    Mat result;

    switch(dtype)
    {
        case 0: // int
            result = this->add<int>(a, 0);
            break;
        case 1: // float
            result = this->add<float>(a, 0);
            break;
    };

    return result;
}

Это плохая идея?если нет способа избавиться от неиспользуемого параметра в методе add?

Другая идея, которая у меня была, заключалась в том, чтобы сделать классы IntMatrix, Float Matrix унаследованными от класса Mat просто для того, чтобы он вызывал функцию add с типом шаблона, чтобы избежать переключения регистра в перегрузке оператора сложения.Это тоже плохой дизайн?

уточнение

Я хочу иметь 2 вектора:

vector<Transform*> transformVector; // list of classes doing operation on matrix
vector<Mat*> results; // intermediate results vector

results.push_back(input_mat)
for(int i = 0; i < transformVector.size(); ++i){
    results.push_back(transformVector[i]->transform(results[i]));
// transform here might have to return a result of type float
// even though the input was of type int
}

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Одна трудная точка здесь - type * getData().Здесь вы либо возвращаете простой void * и требуете, чтобы вызывающий объект выполнял явное приведение к нему, либо вам приходится использовать шаблонную функцию.

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

Вы говорите , большинство классов используют класс матрицы, не заботясь отип данных .Это именно то, для чего созданы шаблоны: набор средств хранения и обработки, который не зависит от базового типа (хорошо шаблоны могут делать немного больше, но изначально были созданы для этого)

void * всегдабезопасный указатель и отличный выбор для C-совместимых API.Но если у вас нет проблем с производительностью (шаблоны могут использовать слишком много памяти на крошечных системах, поскольку они объявляют разные классы для каждой реализации (*)) и могут доказать, что void * лучше для конкретного случая использования, вам следует придерживатьсяобщие правила.Пишите простой и легкий для чтения код и оптимизируйте его только тогда, когда вы обнаружите узкое место.

После вашего редактирования я вижу, что вы хотите хранить матрицы разных базовых типов в одном контейнере.Я могу представить себе полиморфизм, если все матрицы могут быть производными от общего не шаблонного типа, но я не удивлюсь, если вы вдруг попадаете в проблему type * getData() позже: вы статически приводите указатель void, поэтому компилятор имеетнет способа помешать вам сделать плохой актерский состав.Другой возможностью будет std::variant для матриц (если C ++ 17) или boost::variant или любой другой вариант или любой вариант.Некоторые из них реализуют приемы для предотвращения неудачных приведений во время выполнения.

Трудно понять, какой путь лучше, не экспериментируя с реальной проблемой ...


Некоторые другие языки, такие как Java, имеютне шаблонов (различный класс для каждой реализации), а обобщенных (общий класс, который действует на объектов ).Плюсы - это всего лишь один класс, поэтому вопрос о том, что правильный шаблон недоступен во время ссылки, исчез, минусы заключаются в том, что требуются некоторые приемы, чтобы сделать фактический тип доступным во время выполнения.

0 голосов
/ 12 июня 2018

Было бы более эффективно сделать шаблон Mat шаблонным и позволить компилятору создавать необходимые функции добавления.

В текущей реализации вам придется добавлять новый регистр переключателя для каждого новоговведите и будьте осторожны, чтобы правильно привести void* к нужному типу.Когда вы используете шаблоны, компилятор поможет вам, проверив ваши типы.

Вы даже можете создать шаблон, который позволит вам добавить Mat<int> к Mat<float> (или двум другим матрицам разных типов)..

template <typename T, size_t Col, size_t Row>
Mat {
    std::array<T, Col * Row> data; // or other data structure
    // ...
    template <typename OtherT>
    add(const Mat<OtherT, Col, Row>& other);
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...