У меня есть математическая библиотека.В этой библиотеке у меня есть функции для управления гиперплоскостями в симплексном пространстве, так что я могу их сортировать различными способами.
Оказывается, что эти гиперплоскости могут представлять разные вещи в разных контекстах.Хотя математика одна и та же, в каждом контексте гиперплоскости означают разные вещи и связаны с разными структурами данных.
Мне выгодно иметь возможность написать код для управления гиперплоскостями один раз, нопозволяя им обрабатывать различные структуры данных.
Ниже приведен упрощенный пример, который пытается объяснить, что я пытаюсь сделать:
// Assume this struct represent my hyperplane, or whatever
// construct I want to be able to manipulate.
struct Math {
int x;
};
// Here is my function `foo` which expects a range of Math. It does
// some work on it, and re-arranges it as it is useful to me.
template <typename It>
void foo(It begin, It end) {
while (begin < end) {
--end;
if (begin->x < end->x)
std::iter_swap(begin, end);
++begin;
}
}
template <typename Range>
void foo(Range & r) {
foo(ranges::begin(r), ranges::end(r));
}
Это в основном моя базовая функциональность, которая является общей длякаждый дополнительный класс, который использует мои гиперплоскости (или, в данном случае, класс Math
).
Теперь в других частях моей библиотеки у меня есть классы, которые выглядят так:
struct Something {
int additional_metadata;
Math contextual_name_for_math;
};
struct SomethingElse {
double other_metadata;
std::vector<int> some_other_metadata;
Math another_different_contextual_name;
};
Теперь мне нужно иметь возможность применить foo
к диапазонам этих классов и переставить их на основе свойств Math
, которые они содержат.В то же время:
foo
не знает контекстное имя, которое Math
имеет в каждом из этих классов. foo
не заботится о дополнительных метаданныхчто присутствует.
Я хотел бы написать что-то вроде этого:
// Create data
std::vector<Something> S{{1,{2}},{3,{4}},{5,{6}}};
// Transform data in to view of Math, so that 'foo' can work on it
auto S_as_math = S | ranges::view::transform(
// I guess I can remove the consts here, although `foo` does not
// really need to alter the values, it only shuffles things around.
[](auto const& s) -> Math const& { return s.contextual_name_for_math; }
);
// Do work inline on the view, but hopefully on the underlying S too.
foo(S_as_math);
// Print results.
for (auto s : S) {
std::cout << '(' << s.additional_metadata << ", "
<< s.contextual_name_for_math.x << ")\n";
}
std::cout << "\n";
// expected, keeps Math and associated metadata together:
//
// (5, 6)
// (3, 4)
// (1, 2)
//
// NOT WANTED, only shuffles Math with no regard for metadata:
//
// (1, 6)
// (3, 4)
// (5, 2)
В настоящее время я делаю это, передавая boost::transform_iterator
s foo
, который извлекаеткомпонент Math
при разыменовании и с помощью пользовательской реализации iter_swap
внутри foo
, которая может узнать, передается ли он через прокси-итератор, и всегда заменяет исходные оригиналы.Это достигает того, чего я хочу.
Мне любопытно, возможно ли это сделать с помощью ranges-v3
.В настоящее время я могу скомпилировать этот пример, если я удаляю const
s в лямбде, который я использую, чтобы развернуть класс Something
, но тогда foo
только перетасовывает Math
s, не сохраняя их вместе с их метаданными.