Обобщенная цепочка функций, не являющихся членами в C ++ - PullRequest
2 голосов
/ 24 сентября 2011

Я не знаю, может ли это быть даже достижимым, но учитывая этот набор функций \ class:

float plus1(float x) { return x+1; }
float div2(float x) { return x/2.0f; }
template <typename T>
class chain {
public:
    chain(const T& val = T()) : val_(val) {}
    chain& operator<<( std::function<float (float)> func ) {
    val_ = func(val_);
    return *this;
  }
  operator T() const {
    return val_;
  }
  T val_;
};

Я могу связать функции, работающие с плавающими числами, следующим образом:

float x = chain<float>(3.0f) << div2 << plus1 << div2 << plus1;

Однако я хотел бы обобщить \ расширить это, чтобы иметь возможность конвертировать между типами и иметь функции с аргументами. К сожалению, я не достаточно умен, чтобы понять, как или если это можно сделать. Точнее говоря, я бы хотел иметь возможность что-то подобным образом (где operator<< - просто произвольный выбор, и, предпочтительно, мне даже не нужно писать «цепочку» в начале); Кроме того, это всего лишь фиктивные примеры, я не собираюсь использовать их для арифметики.

std::string str = chain<float>(3.0) << mul(2.0f) << sqrt << to_string << to_upper;

или

vec3d v = chain<vec3i>(vec3i(1,1,1)) << normalize << to_vec3<double>;

Есть идеи?

Ответы [ 3 ]

2 голосов
/ 24 сентября 2011

Я думаю, я понимаю, почему вы хотите это сделать. Это похоже на манипуляторы iostream.

Вам всегда нужно начинать с chain(...) (то есть вы никогда не сможете волшебным образом сделать что-то вроде int x = 1 << plus(2) << times(2)), но вы можете перегрузить operator int, operator float, ..., чтобы учесть неявные преобразования.

Вам также нужно будет вернуться назад и определить каждый тип (например, mul), а затем реализовать operator<<, который принимает mul или const mul, но в целом это выполнимо (кроме PITA)

1 голос
/ 25 сентября 2011

Общее и расширяемое решение с использованием boost :: proto:

#include <iostream>
#include <boost/proto/proto.hpp>

namespace bp = boost::proto;

// -----------------------------------------------------------------------------
// perform is a callable transform that take a function_ terminal and execute it
// -----------------------------------------------------------------------------
struct perform : bp::callable
{
  template<class Sig> struct result;
  template<class This, class Func, class In>
  struct result<This(Func,In)> 
       : boost::result_of<typename boost::remove_reference<Func>::type(In)> {};

  template<class Func, class In>
  typename result<perform(Func &,In)>::type
  operator()( Func& f, In& in ) const
  {
    return f(in);
  }
};

// -----------------------------------------------------------------------------
// Grammar for chaining pipe of functions
// -----------------------------------------------------------------------------
struct pipeline_grammar
: bp::or_<
    bp::when<
        bp::bitwise_or<pipeline_grammar,pipeline_grammar>
          , pipeline_grammar(
                bp::_right
              , pipeline_grammar(bp::_left,bp::_state)
                )
        >
      , bp::when<
            bp::terminal<bp::_>
          , perform(bp::_value, bp::_state) 
    >
> {};

// -----------------------------------------------------------------------------
// Forward declaration of the pipeline domain
// -----------------------------------------------------------------------------
struct pipeline_domain;

// -----------------------------------------------------------------------------
// A pipeline is the top level DS entity
// -----------------------------------------------------------------------------
template<class Expr>
struct  pipeline : bp::extends<Expr,pipeline<Expr>, pipeline_domain>
{
  typedef bp::extends<Expr, pipeline<Expr>, pipeline_domain> base_type;
  pipeline(Expr const &expr = Expr()) : base_type(expr) {}

  // ---------------------------------------------------------------------------
  // A pipeline is an unary callable object
  // ---------------------------------------------------------------------------
  template<class Input>
  typename boost::result_of<pipeline_grammar(pipeline,Input)>::type
  operator()(Input const& in) const
  {
    pipeline_grammar evaluator;
    return evaluator(*this,in);
  }
};

// -----------------------------------------------------------------------------
// the pipeline_domain make pipeline expression macthes pipeline_grammar
// -----------------------------------------------------------------------------
struct pipeline_domain 
     : bp::domain<bp::generator<pipeline>,pipeline_grammar>
{};

// -----------------------------------------------------------------------------
// Takes a PFO instance and make it a pipeline terminal
// -----------------------------------------------------------------------------
template<class Func>
typename bp::result_of::
make_expr<bp::tag::terminal, pipeline_domain,Func>::type
task( Func const& f )
{
  return bp::make_expr<bp::tag::terminal,pipeline_domain>( f );
}

//--------------------------- Examples --------------------

struct return_value
{  
  template<class Sig> struct result;
  template<class This, class T>
  struct result<This(T)> : bp::detail::uncvref<T>
  {};

  return_value(int i = 1) : factor(i) {}

  template<class T> 
  T operator()(T const& in) const
  {
    return in*factor;
  }

  int factor;
};

struct say_hi
{
  typedef void result_type;

  template<class T> 
  void operator()(T const& in) const
  {
    std::cout << "Hi from value = " << in << "\n";
  }
};

int main()
{
  return_value r1,r2(5);
  (task(r1) | task(r2) | task(say_hi())) (7); // SHould print 35

  float k = 10,r;
  r = (task(r2) | task(r2) | task(r2) | task(r2))(k);
  std::cout << r << "\n"; // Should print 6250
}

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

1 голос
/ 24 сентября 2011

Чтобы получить преобразования между типами, вам нужно, чтобы все возвращало прокси-объект, который мог бы преобразовать в любой тип.Возможно, что-то на основе boost :: variable.

Вы также можете переписать ваш оператор << как функцию шаблона, чтобы сделать его более универсальным: </p>

template <class UnaryFunction>
chain& operator<<(UnaryFunction func) { _val = func(_val); return *this;}

Это позволитиспользуйте любой тип объекта функции в качестве аргумента.

Чтобы использовать функции с несколькими аргументами, вы можете использовать функцию связывания.Это было в разгар до C ++ 11, однако теперь оно в стандарте и должно быть доступно на любом C ++ 11-совместимом компиляторе.

...