При работе над C ++ 11 я столкнулся с почти логической проблемой.

У меня есть класс, который я должен построить (он же нарисовать тренд), и я хочу исключить все точки, которые не удовлетворяют заданному условию. Точки относятся к классу Foo, и все условные функции определены с сигнатурой bool Foo::Bar(Args...) const, где Args... представляет собой ряд параметров (например, верхний и нижний пределы возвращаемого значения).

Все шло хорошо до того момента, когда я захотел применить одно условие к значениям для построения графика. Допустим, у меня есть класс FooPlotter, который имеет что-то вроде:

template<class ...Args> GraphClass FooPlotter::Plot([...],bool (Foo::*Bar)(Args...), Args... args)

Которая будет перебирать мой контейнер данных и применять условие Foo::*Bar ко всем элементам, отображая значения, которые удовлетворяют данному условию.

Все идет нормально.

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

Сначала я создал класс, содержащий все, что мне понадобится позже:

    template<class ...Args> class FooCondition{
        public:
            FooCondition(bool (Foo::*Bar)(Args...) const, Args... args)
            {
                fCondition = Bar;
                fArgs = std::make_tuple(args);
            }
            bool operator()(Foo data){ return (data.*fCondition)(args); }
        private:
            bool (Foo::*fCondition)(Args...) const;
            std::tuple<Args...> fArgs;
    };

Затем я застрял на том, как определить (итеративный) контейнер, который может содержать объекты FooCondition, несмотря на то, что у них есть несколько типов для пакета аргументов Args.... Проблема в том, что некоторые методы имеют Args... = uint64_t,uint_64_t, в то время как другие не требуют вызова аргументов.

Я немного покопался, как справиться с такой ситуацией. Я пробовал несколько подходов, но ни один из них не сработал.

На данный момент я добавил игнорируемые аргументы ко всем методам Bar, унифицировал их и решил проблему, но я не очень доволен!

Есть ли у некоторых из вас идеи, как элегантно хранить объекты FooCondition с разными типами?


РЕДАКТИРОВАТЬ: Дополнительная информация о результате, который я хочу получить.

Сначала я хочу иметь возможность создать std::vector из FooCondition элементов:

    std::vector<FooCondition> conditions;
    conditions.emplace_back(FooCondition(&Foo::IsBefore, uint64_t timestamp1));
    conditions.emplace_back(FooCondition(&Foo::IsAttributeBetween, double a, double b));
    conditions.emplace_back(FooCondition(&Foo::IsOk));

На данный момент я хотел бы сделать что-то вроде следующего в моем методе FooPlotter::Plot:

    GraphClass FooPlotter::Plot(vector<Foo> data, vector<FooCondition> conditions){
        GraphClass graph;
        for(const auto &itData : data){
            bool shouldPlot = true;
            for(const auto &itCondition : conditions){
                shouldPlot &= itCondition(itData);
            }
            if(shouldPlot) graph.AddPoint(itData);
        }
        return graph;
    }

Как вы можете утверждать, FooCondition struct должен автоматически передавать правильные аргументы методу с помощью перегруженного оператора.

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

2
jetstream 13 Мар 2018 в 19:31

2 ответа

Лучший ответ

Мне кажется, что с помощью FooCondition вы пытаетесь создать замену для std::function<bool(Foo *)> (или, может быть, std::function<bool(Foo const *)>), инициализированного с помощью std::bind, которая исправляет некоторые аргументы для { {X4}} методы.

То есть ... я думаю, что вместо

std::vector<FooCondition> conditions;
conditions.emplace_back(FooCondition(&Foo::IsBefore, uint64_t timestamp1));
conditions.emplace_back(FooCondition(&Foo::IsAttributeBetween, double a, double b));
conditions.emplace_back(FooCondition(&Foo::IsOk));

Ты должен написать что-нибудь как

std::vector<std::function<bool(Foo const *)>> vfc;

using namespace std::placeholders;

vfc.emplace_back(std::bind(&Foo::IsBefore, _1, 64U));
vfc.emplace_back(std::bind(&Foo::IsAttributeBetween, _1, 10.0, 100.0));
vfc.emplace_back(std::bind(&Foo::IsOk, _1));

Ниже приведен упрощенный полностью рабочий пример C ++ 11 с main(), имитирующим Plot()

#include <vector>
#include <iostream>
#include <functional>

struct Foo
 {
   double  value;

   bool IsBefore (std::uint64_t ts) const
    { std::cout << "- IsBefore(" << ts << ')' << std::endl; 
      return value < ts; }

   bool IsAttributeBetween (double a, double b) const
    { std::cout << "- IsAttrributeBetwen(" << a << ", " << b << ')'
         << std::endl; return (a < value) && (value < b); }

   bool IsOk () const
    { std::cout << "- IsOk" << std::endl; return value != 0.0; }
 };

int main ()
 {
   std::vector<std::function<bool(Foo const *)>> vfc;

   using namespace std::placeholders;

   vfc.emplace_back(std::bind(&Foo::IsBefore, _1, 64U));
   vfc.emplace_back(std::bind(&Foo::IsAttributeBetween, _1, 10.0, 100.0));
   vfc.emplace_back(std::bind(&Foo::IsOk, _1));

   std::vector<Foo> vf { Foo{0.0}, Foo{10.0}, Foo{20.0}, Foo{80.0} };

   for ( auto const & f : vf )
    {
      bool  bval { true };

      for ( auto const & c : vfc )
         bval &= c(&f);

      std::cout << "---- for " << f.value << ": " << bval << std::endl;
    }
 }

Другой способ - избегать использования std::bind и использовать вместо него лямбда-функцию.

Примером

std::vector<std::function<bool(Foo const *)>> vfc;

vfc.emplace_back([](Foo const * fp)
                 { return fp->IsBefore(64U); });
vfc.emplace_back([](Foo const * fp)
                 { return fp->IsAttributeBetween(10.0, 100.0); });
vfc.emplace_back([](Foo const * fp)
                 { return fp->IsOk(); });
1
max66 13 Мар 2018 в 18:19

За исключением панели foo, вам просто нужен класс с методом, который можно реализовать для удовлетворения сюжета.

Просто добавьте метод Plot к классу, который принимает узел, и выполните преобразование и построение графика на том же этапе.

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

Таким образом, простого args * будет достаточно, и если null нет аргументов, каждый arg показывает свой тип и значение или может быть принят из вызова функции.

0
Jay 13 Мар 2018 в 16:36