Работа со сложными вложенными типами в API - PullRequest
1 голос
/ 24 октября 2019

Итак, давайте предположим, что есть проект, связанный с обработкой изображений. У нас есть алгоритм, который вычисляет «баллы» (независимо от того, что это) между двумя изображениями так:

double score(Image i1, Image i2)

Если у нас есть несколько кадров (изображений), мы хотим рассчитать его для всех них(соответствует всем всем):

std::vector<std::pair<std::pair<int, int>, double>> score(std::vector<Image> images); // [1]

( пары целых чисел представляют индексы изображения в векторе изображений, потому что мы сопоставляем все со всеми )

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

std::vector<std::vector<std::vector<std::pair<std::pair<int, int>, double>>>>
     ^devices    ^streams    ^frames     ^score   ^image indices

Как бы вы работали с функцией, которая имеетвернуть такой сложный тип?

Речь идет только о том, чтобы обернуть его в typedefs и правильно назвать? Или, может быть, вы бы позволили пользователю использовать более простую версию API, то есть с одним вектором [1], и дополнительно обернуть его самостоятельно? А может, есть какой-нибудь причудливый узор для такого случая?

Ответы [ 2 ]

1 голос
/ 24 октября 2019

В общем, вы имеете дело со сложностью, присваивая собственные имена вещам.

Когда я вижу ваш API в [1], я вижу график, где изображения являются узлами графика, и вы возвращаетекрая как список смежности. Информация о смежности также может храниться в форме симметричной матрицы смежности. Наличие правильной терминологии для вашей проблемы может дать вам доступ к обширному набору инструментов, например, к таким библиотекам, как Boost.Graph. Это может быть отправной точкой для вас, чтобы написать свои собственные повторно используемые компоненты. Вы можете даже узнать, что люди имели дело с проблемами, которые вы пытаетесь решить, но в более абстрактных терминах.

Это всего лишь один из вариантов, и когда вы думаете о правильном названии, могут произойти удивительные вещи.

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

using AdjacencyList = std::vector<std::pair<std::pair<int, int>, double>;
// Info that might also be represented as a matrix or symmetric matrix.

class SimilarityData
{
    public:
        const AdjacencyList &
        getAdjacencyList(
            int device,
            int stream,
            int frame ) const;
};
0 голосов
/ 24 октября 2019

Не зная слишком много о данных, одним из вариантов является создание структур или классов для переноса отдельных частей:

namespace your_company {
struct index {
    std::pair<int, int> indexes;
};
struct frame {
    std::pair<index, double> scores;
};
struct stream {
    std::vector<frame> frames;
};
struct device {
    std::vector<stream> streams;
};
}

При необходимости вы даже можете добавить дополнительную информацию в структуры. Например:

struct device {
    std::vector<stream> streams;
    std::string name;
};

Итак, ваша функция вернет device. Код пользователя может выглядеть так:

your_company::device d = your_function();
d.streams[0].frames[0].scores[0].pair[0].....
...