簡體   English   中英

C++ 中非成員函數的廣義鏈接

[英]Generalized chaining of non-member functions in C++

我不知道這是否可以實現,但考慮到這些函數\類集:

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;

但是,我想將其概括\擴展為能夠在類型之間進行轉換並具有 arguments 的功能。不幸的是,我不夠聰明,無法弄清楚如何或是否可以做到這一點。 更具體地說,我希望能夠做這樣的事情(其中operator<<只是一個任意選擇,最好我什至不必在開頭寫“chain”); 此外,這些只是虛擬示例,我不打算將其用於算術。

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>;

有任何想法嗎?

我想我明白你為什么要這樣做。 它類似於iostream操縱器。

您將始終需要從chain(...) (即,您將永遠無法神奇地執行int x = 1 << plus(2) << times(2) ),但是您可以重載operator intoperator float ,...以允許隱式轉換。

您還需要返回並定義每種類型(例如mul ),然后實現operator<< ,它需要一個mul或一個const mul,但從總體上講是可行的(但使用PITA)

這是我針對 C++17 的解決方案。

#include <type_traits>
#include <utility>

template <class F>
struct waterfall
{
    waterfall(F&& f)
    : fn(std::forward<F>(f))
    {}

    template <class... Args>
    decltype(auto) operator()(Args&&... args) const {
        return fn(std::forward<Args>(args)...);
    }

    template <class T>
    auto then(T&& t) const & {
        return then_impl(fn, std::forward<T>(t));
    }

    template <class T>
    auto then(T&& t) const && {
        return then_impl(std::move(fn), std::forward<T>(t));
    }

private:
    F fn;

    template <class In, class Out>
    static auto then_impl(In&& in, Out&& out)
    {
        auto fn = [in = std::forward<In>(in), out = std::forward<Out>(out)](auto&&... args)
        {
            using InRet = std::invoke_result_t<In, decltype(args)...>;

            if constexpr (std::is_invocable_v<Out, InRet>) {
                return out(in(std::forward<decltype(args)>(args)...));
            }
            else {
                in(std::forward<decltype(args)>(args)...);
                return out();
            }
        };

        return waterfall<decltype(fn)>(std::move(fn));
    }
};

並像這樣使用它

int main()
{
    // Create a chain
    waterfall chain([](const char* s) {
        return 42;
    })
    .then([](auto x) {
        // x = 42 here
        return x + 1;
    })
    .then([] {
        // Ignoring value from previous function.
        // Send double to next one.
        return 3.14;
    })
    .then([](double value) {
        // etc...
        return true;
    });

    // chain signature is now bool(const char*)

    // Now call our functions in chain
    bool ret = chain("test");
}

為了在類型之間進行轉換,您希望所有內容都返回一個代理對象,該對象可以轉換為任何類型。 也許是基於boost :: variant的東西。

您也可以將運算符<<重寫為模板函數,使其更加通用:

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

那將允許您使用任何類型的函數對象作為參數。

要使用帶有多個參數的函數,可以使用bind函數。 此功能在C ++ 11之前得到了增強,但現在已成為標准功能,並且可以在任何與C ++ 11兼容的編譯器上使用。

使用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
}

基本思想是將功能對象包裝為原型終端,構建一個小的| 基於語法,讓原型系統處理合成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM