Манипулировать базовым диапазоном с помощью range-v3 - PullRequest
0 голосов
/ 14 октября 2018

У меня есть математическая библиотека.В этой библиотеке у меня есть функции для управления гиперплоскостями в симплексном пространстве, так что я могу их сортировать различными способами.

Оказывается, что эти гиперплоскости могут представлять разные вещи в разных контекстах.Хотя математика одна и та же, в каждом контексте гиперплоскости означают разные вещи и связаны с разными структурами данных.

Мне выгодно иметь возможность написать код для управления гиперплоскостями один раз, нопозволяя им обрабатывать различные структуры данных.

Ниже приведен упрощенный пример, который пытается объяснить, что я пытаюсь сделать:

// 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, не сохраняя их вместе с их метаданными.

1 Ответ

0 голосов
/ 14 октября 2018

Передайте функцию преобразования на foo, а не foo преобразованный диапазон.

template <typename It, typename UnaryFunction>
void foo(It begin, It end, UnaryFunction func) {
    while (begin < end) {
        --end;
        if (func(*begin).x < func(*end).x)
            std::iter_swap(begin, end);
        ++begin;
    }
}
template <typename Range, typename UnaryFunction>
void foo(Range & r, UnaryFunction func) {
    foo(ranges::begin(r), ranges::end(r));
}

int main()
{
    std::vector<Something> S{{1,{2}},{3,{4}},{5,{6}}};
    auto S_as_math = [](auto const& s) { return s.contextual_name_for_math; };
    foo(S, S_as_math);

    for (auto s : S) {
        std::cout << '(' << s.additional_metadata << ", " 
                         << s.contextual_name_for_math.x << ")\n";
    }
    std::cout << "\n";   
}

Вы можете оставить исходный шаблон или значение по умолчанию UnaryFunction для функции идентификации , если вы используете только Math диапазоны.

...