C++迭代器流水线设计

发布于 2024-10-19 05:03:09 字数 997 浏览 5 评论 0原文

假设我们要应用一系列转换,int f1(int)int f2(int)int f3(int),对象列表。一种天真的方法是,

SourceContainer source;

TempContainer1 temp1;
transform(source.begin(), source.end(), back_inserter(temp1), f1);
TempContainer2 temp2;
transform(temp1.begin(), temp1.end(), back_inserter(temp2), f2);

TargetContainer target;
transform(temp2.begin(), temp2.end(), back_inserter(target), f3);

由于 temp1temp2 需要额外的空间,因此这个第一个解决方案并不是最佳的。因此,让我们变得更聪明:

int f123(int n) { return f3(f2(f1(n))); }
...
SourceContainer source;
TargetContainer target;
transform(source.begin(), source.end(), back_inserter(target), f123);

这个第二解决方案要好得多,因为不仅代码更简单,而且更重要的是,没有中间计算,空间需求更少。

但是,组合 f123 必须在编译时确定,因此在运行时固定。

如果要在运行时确定组合,我将如何有效地做到这一点?例如,如果此代码位于 RPC 服务中,并且实际组合可以是 f1f2f3< 的任何子集的任意排列/code>--基于 RPC 调用的参数。

Suppose we want to apply a series of transformations, int f1(int), int f2(int), int f3(int), to a list of objects. A naive way would be

SourceContainer source;

TempContainer1 temp1;
transform(source.begin(), source.end(), back_inserter(temp1), f1);
TempContainer2 temp2;
transform(temp1.begin(), temp1.end(), back_inserter(temp2), f2);

TargetContainer target;
transform(temp2.begin(), temp2.end(), back_inserter(target), f3);

This first solution is not optimal because of the extra space requirement with temp1 and temp2. So, let's get smarter with this:

int f123(int n) { return f3(f2(f1(n))); }
...
SourceContainer source;
TargetContainer target;
transform(source.begin(), source.end(), back_inserter(target), f123);

This second solution is much better because not only the code is simpler but more importantly there is less space requirement without the intermediate calculations.

However, the composition f123 must be determined at compile time and thus is fixed at run time.

How would I try to do this efficiently if the composition is to be determined at run time? For example, if this code was in a RPC service and the actual composition--which can be any permutation of any subset of f1, f2, and f3--is based on arguments from the RPC call.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

归属感 2024-10-26 05:03:09

编辑:工作版本位于 http://ideone.com/5GxnW 。下面的版本有想法但无法编译。它支持运行时类型检查和运行时函数组合。

这个想法是定义一个通用(一元)函数类,以及一种通过运行时类型检查来组合它们的方法。这是通过结合 boost::anyboost::function 和类型擦除习惯用法来完成的。

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>


template <typename T>
struct identity
{
    T operator()(const T& x) { return x; }
};

struct any_function
{
    template <typename Res, typename Arg>
    any_function(boost::function<Res, Arg> f)
    {
        impl = make_impl(f);
    }

    boost::any operator()(const boost::any& x)
    {
        return impl->invoke(x);
    }

    static any_function compose(const any_function& f,
                                const any_function& g)
    {
        any_function ans;
        ans.impl = compose_impl(f.impl, g.impl);
        return ans;
    }

    template <typename T>
    static any_function id()
    {
        using boost::function
        return any_function(function<T(T)>(identity<T>()));
    }

    template <typename Res, typename Arg>
    boost::function<Res(Arg)> to_function()
    {
        using boost::function;
        return function<Res(Arg)>(to_function_helper(impl));
    }

private:
    any_function() {}

    struct impl_type
    {
        virtual ~impl_type() {}
        virtual boost::any invoke(const boost::any&) = 0;
    };

    boost::shared_ptr<impl_type> impl;

    template <typename Res, typename Arg>
    static impl_type* make_impl(boost::function<Res(Arg)> f)
    {
        using boost::function;
        using boost::any;
        using boost::any_cast;

        class impl : public impl_type
        {
            function<Res(Arg)> f;

            any invoke(const any& x)
            {
                const Arg& a = any_cast<Arg>(x);
                return any(f(a));
            }

        public:
            impl(function<Res(Arg)> f) : f(f) {}
        };

        return new impl(f);
    }

    impl_type* compose_impl(boost::shared_ptr<impl_type> f,
                            boost::shared_ptr<impl_type> g)
    {
        using boost::any;
        using boost::shared_ptr;

        class impl : public impl_type
        {
            shared_ptr<impl> f, g;

            any invoke(const any& x)
            {
                return g->invoke(f->invoke(x));
            }

        public:
            impl(const shared_ptr<impl>& f,
                 const shared_ptr<impl>& g)
                : f(f), g(g)
            {}
        };

        return new impl(f, g);
    }

    struct to_function_helper
    {
        template <typename Res, typename Arg>
        Res operator()(const Arg& x)
        {
            using boost::any;
            using boost::any_cast;

            return any_cast<Res>(p->invoke(any(x)));
        }

        to_function_helper(const boost::shared_ptr<impl>& p) : p(p) {}

    private:
        boost::shared_ptr<impl> p;
    };
};

现在,让我们使用标准算法来执行此操作(这甚至适用于空序列):

// First function passed is evaluated first. Feel free to change.
template <typename Arg, typename Res, typename I>
boost::function<Res(Arg)> pipeline(I begin, I end)
{
    return std::accumulate(begin, end, 
        any_function::id<Arg>,
        std::ptr_fun(any_function::compose)
    ).to_function<Res, Arg>();
}

并使用以下内容来应用它

std::vector<any_function> f;
std::vector<double> v;
std::vector<int> result;

std::transform(v.begin(), v.end(), 
    result.begin(), 
    pipeline<double, int>(f.begin(), f.end())
);

您甚至可以使用 boost::transform_iterator

typedef boost::transform_iterator<
    boost::function<double, int>, 
    std::vector<double>::const_iterator
> iterator;

boost::function<double, int> f = pipeline<double, int>(f.begin(), f.end());
std::copy(iterator(v.begin(), f), iterator(v.end(), f), result.begin());

EDIT: Working version at http://ideone.com/5GxnW . The version below has the ideas but does not compile. It supports run time type checking, and run time function composition.

The idea is to define a generic (unary) function class, and a way to compose them with run time type checks. This is done with a combination of boost::any, boost::function and the type erasure idiom.

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>


template <typename T>
struct identity
{
    T operator()(const T& x) { return x; }
};

struct any_function
{
    template <typename Res, typename Arg>
    any_function(boost::function<Res, Arg> f)
    {
        impl = make_impl(f);
    }

    boost::any operator()(const boost::any& x)
    {
        return impl->invoke(x);
    }

    static any_function compose(const any_function& f,
                                const any_function& g)
    {
        any_function ans;
        ans.impl = compose_impl(f.impl, g.impl);
        return ans;
    }

    template <typename T>
    static any_function id()
    {
        using boost::function
        return any_function(function<T(T)>(identity<T>()));
    }

    template <typename Res, typename Arg>
    boost::function<Res(Arg)> to_function()
    {
        using boost::function;
        return function<Res(Arg)>(to_function_helper(impl));
    }

private:
    any_function() {}

    struct impl_type
    {
        virtual ~impl_type() {}
        virtual boost::any invoke(const boost::any&) = 0;
    };

    boost::shared_ptr<impl_type> impl;

    template <typename Res, typename Arg>
    static impl_type* make_impl(boost::function<Res(Arg)> f)
    {
        using boost::function;
        using boost::any;
        using boost::any_cast;

        class impl : public impl_type
        {
            function<Res(Arg)> f;

            any invoke(const any& x)
            {
                const Arg& a = any_cast<Arg>(x);
                return any(f(a));
            }

        public:
            impl(function<Res(Arg)> f) : f(f) {}
        };

        return new impl(f);
    }

    impl_type* compose_impl(boost::shared_ptr<impl_type> f,
                            boost::shared_ptr<impl_type> g)
    {
        using boost::any;
        using boost::shared_ptr;

        class impl : public impl_type
        {
            shared_ptr<impl> f, g;

            any invoke(const any& x)
            {
                return g->invoke(f->invoke(x));
            }

        public:
            impl(const shared_ptr<impl>& f,
                 const shared_ptr<impl>& g)
                : f(f), g(g)
            {}
        };

        return new impl(f, g);
    }

    struct to_function_helper
    {
        template <typename Res, typename Arg>
        Res operator()(const Arg& x)
        {
            using boost::any;
            using boost::any_cast;

            return any_cast<Res>(p->invoke(any(x)));
        }

        to_function_helper(const boost::shared_ptr<impl>& p) : p(p) {}

    private:
        boost::shared_ptr<impl> p;
    };
};

Now, let's use standard algorithms and do this (this even works on empty sequences):

// First function passed is evaluated first. Feel free to change.
template <typename Arg, typename Res, typename I>
boost::function<Res(Arg)> pipeline(I begin, I end)
{
    return std::accumulate(begin, end, 
        any_function::id<Arg>,
        std::ptr_fun(any_function::compose)
    ).to_function<Res, Arg>();
}

and use the following to apply it

std::vector<any_function> f;
std::vector<double> v;
std::vector<int> result;

std::transform(v.begin(), v.end(), 
    result.begin(), 
    pipeline<double, int>(f.begin(), f.end())
);

You can even use boost::transform_iterator

typedef boost::transform_iterator<
    boost::function<double, int>, 
    std::vector<double>::const_iterator
> iterator;

boost::function<double, int> f = pipeline<double, int>(f.begin(), f.end());
std::copy(iterator(v.begin(), f), iterator(v.end(), f), result.begin());
蓝梦月影 2024-10-26 05:03:09
template<class T>
class compose {
    typedef T (*f)(T);

    f first_func;
    f second_func;

public:

    compose(f one,f two) :
        first_func(one),
        second_func(two)        
    {}

    T operator()(T const &input) {
        T temp = first_func(input);
        return second_func(temp);
    }
};

#ifdef TEST

int f(int x) { return 8 + x; }
int g(int x) { return 2 * x; }
int h(int x) { return x * x; }

#include <iostream>

int main(int argc, char **argv) {
    compose<int> x(f, g);
    compose<int> y(g, f);

    std::cout << x(6) << std::endl;
    std::cout << y(6) << std::endl;

    typedef int (*func)(int);

    func funcs[] = {f, g, h};

    compose<int> z(funcs[atoi(argv[1])], funcs[atoi(argv[2])]);
    std::cout << z(6);

    return 0;
}

#endif

对于 C++0x,我们应该能够使用 auto 来消除指定参数/返回类型的麻烦。目前我假设它们是相同的,但从理论上讲,您可能希望能够在组合中包含转换。

template<class T>
class compose {
    typedef T (*f)(T);

    f first_func;
    f second_func;

public:

    compose(f one,f two) :
        first_func(one),
        second_func(two)        
    {}

    T operator()(T const &input) {
        T temp = first_func(input);
        return second_func(temp);
    }
};

#ifdef TEST

int f(int x) { return 8 + x; }
int g(int x) { return 2 * x; }
int h(int x) { return x * x; }

#include <iostream>

int main(int argc, char **argv) {
    compose<int> x(f, g);
    compose<int> y(g, f);

    std::cout << x(6) << std::endl;
    std::cout << y(6) << std::endl;

    typedef int (*func)(int);

    func funcs[] = {f, g, h};

    compose<int> z(funcs[atoi(argv[1])], funcs[atoi(argv[2])]);
    std::cout << z(6);

    return 0;
}

#endif

With C++0x, we should be able to use auto to eliminate having to specify the argument/return type. For the moment I've assumed they're the same, though in theory, you might like the ability to include conversions in the mix.

梅倚清风 2024-10-26 05:03:09

您应该使用仿函数而不是函数,并将所需的变换函数传递到仿函数的构造函数中,

例如

typedef int (*FunctionType)(int);

class Functor
{
    FunctionType m_f1;
    FunctionType m_f2;
    FunctionType m_f3;
public:
    Functor(FunctionType f1, FunctionType f2, FunctionType f3):
      m_f1(f1), m_f2(f2), m_f3(f3)
    {}
    int operator()(int n)
    {
        return (*m_f1)((*m_f2)((*m_f3)(n)));
    }
};

// ...

transform(source.begin(), source.end(), back_inserter(temp1), Functor(f1,f2,f3));

如果您需要可变数量的函数,则更改仿函数构造函数签名以使用函数向量并在调用变换之前填充该向量。

you should use a functor instead of function and pass needed transform functions into functor's constructor

something like

typedef int (*FunctionType)(int);

class Functor
{
    FunctionType m_f1;
    FunctionType m_f2;
    FunctionType m_f3;
public:
    Functor(FunctionType f1, FunctionType f2, FunctionType f3):
      m_f1(f1), m_f2(f2), m_f3(f3)
    {}
    int operator()(int n)
    {
        return (*m_f1)((*m_f2)((*m_f3)(n)));
    }
};

// ...

transform(source.begin(), source.end(), back_inserter(temp1), Functor(f1,f2,f3));

if you need variable number of functions then change Functor constructor signature to use vector of functions and fill that vector before calling transform.

混吃等死 2024-10-26 05:03:09

Just 定义一个迭代器来执行您想要的操作:

template<typename T>
struct source
{
    virtual source<T>& operator++(void) = 0;
    virtual T operator*(void) = 0;
    virtual bool atend() = 0;
};

struct source_exhausted
{
};

template<typename T>
bool operator==(const source<T>& comparand, const source_exhausted&)
{ return comparand.atend(); }

template<typename T>
bool operator!=(const source<T>& comparand, const source_exhausted&)
{ return !comparand.atend(); }

template<typename T>
bool operator==(const source_exhausted&, const source<T>& comparand)
{ return comparand.atend(); }

template<typename T>
bool operator!=(const source_exhausted&, const source<T>& comparand)
{ return !comparand.atend(); }

template<typename T, typename iterT, typename endT>
struct source_iterator : source<T>
{
    iterT m_iter;
    endT m_end;
    source_iterator(iterT iter, endT end) : m_iter(iter), m_end(end) {}

    virtual source<T>& operator++(void) { ++m_iter; return *this; }
    virtual T operator*(void) { return *m_iter; }
    virtual bool atend() { return m_iter == m_end; }
};
template<typename T, typename iterT, typename endT>
auto make_source_iterator(iterT iter, endT end) -> source_iterator<decltype(*iter), iterT, endT>
{
    return source_iterator<decltype(*iter), iterT, endT>(iter, end);
}
template<typename TContainer>
auto make_source_iterator(TContainer& c) -> source_iterator<typename TContainer::value_type, decltype(c.begin()), decltype(c.end())>
{
    return source_iterator<typename TContainer::value_type, decltype(c.begin()), decltype(c.end())>(c.begin(), c.end());
}

template<typename TIn, typename TOut, typename TXform>
struct source_transformer : source<TOut>
{
    source<TIn>& m_src;
    TXform const m_f;
    source_transformer( source<TIn>& src, TXform f ) : m_f(f), m_src(src) {}

    virtual source<TOut>& operator++(void) { ++m_src; return *this; }
    virtual TOut operator*(void) { return m_f(*m_src); }
    virtual bool atend() { return m_src.atend(); }
};
template<typename TIn, typename TOut, typename TXform>
auto make_source_transformer(source<TIn>& src, TXform f) -> source_transformer<TIn, decltype(f(*(TIn*)0)), TXform>
{
    return source_transformer<TIn, decltype(f(*(TIn*)0)), TXform>(src, f);
}

Just define an iterator that does what you want:

template<typename T>
struct source
{
    virtual source<T>& operator++(void) = 0;
    virtual T operator*(void) = 0;
    virtual bool atend() = 0;
};

struct source_exhausted
{
};

template<typename T>
bool operator==(const source<T>& comparand, const source_exhausted&)
{ return comparand.atend(); }

template<typename T>
bool operator!=(const source<T>& comparand, const source_exhausted&)
{ return !comparand.atend(); }

template<typename T>
bool operator==(const source_exhausted&, const source<T>& comparand)
{ return comparand.atend(); }

template<typename T>
bool operator!=(const source_exhausted&, const source<T>& comparand)
{ return !comparand.atend(); }

template<typename T, typename iterT, typename endT>
struct source_iterator : source<T>
{
    iterT m_iter;
    endT m_end;
    source_iterator(iterT iter, endT end) : m_iter(iter), m_end(end) {}

    virtual source<T>& operator++(void) { ++m_iter; return *this; }
    virtual T operator*(void) { return *m_iter; }
    virtual bool atend() { return m_iter == m_end; }
};
template<typename T, typename iterT, typename endT>
auto make_source_iterator(iterT iter, endT end) -> source_iterator<decltype(*iter), iterT, endT>
{
    return source_iterator<decltype(*iter), iterT, endT>(iter, end);
}
template<typename TContainer>
auto make_source_iterator(TContainer& c) -> source_iterator<typename TContainer::value_type, decltype(c.begin()), decltype(c.end())>
{
    return source_iterator<typename TContainer::value_type, decltype(c.begin()), decltype(c.end())>(c.begin(), c.end());
}

template<typename TIn, typename TOut, typename TXform>
struct source_transformer : source<TOut>
{
    source<TIn>& m_src;
    TXform const m_f;
    source_transformer( source<TIn>& src, TXform f ) : m_f(f), m_src(src) {}

    virtual source<TOut>& operator++(void) { ++m_src; return *this; }
    virtual TOut operator*(void) { return m_f(*m_src); }
    virtual bool atend() { return m_src.atend(); }
};
template<typename TIn, typename TOut, typename TXform>
auto make_source_transformer(source<TIn>& src, TXform f) -> source_transformer<TIn, decltype(f(*(TIn*)0)), TXform>
{
    return source_transformer<TIn, decltype(f(*(TIn*)0)), TXform>(src, f);
}
陪我终i 2024-10-26 05:03:09
typedef int (*f_t)(int);

int f1(int a) { return a + 1; }
int f2(int a) { return a * 2; }
int f3(int a) { return a * a; }

int main()
{
    std::vector<f_t> ff = {f1, f2, f3};
    std::vector<int> source = {1, 2, 3, 4}, target;

    std::transform(source.begin(), source.end(), std::back_inserter(target)
    , [&](int a) { for (f_t &f : ff) a = f(a); return a; });

    // print target
    std::copy(target.begin(), target.end(), std::ostream_iterator<int,char>(std::cout,"\n"));
    system("pause");
    return 0;
}
typedef int (*f_t)(int);

int f1(int a) { return a + 1; }
int f2(int a) { return a * 2; }
int f3(int a) { return a * a; }

int main()
{
    std::vector<f_t> ff = {f1, f2, f3};
    std::vector<int> source = {1, 2, 3, 4}, target;

    std::transform(source.begin(), source.end(), std::back_inserter(target)
    , [&](int a) { for (f_t &f : ff) a = f(a); return a; });

    // print target
    std::copy(target.begin(), target.end(), std::ostream_iterator<int,char>(std::cout,"\n"));
    system("pause");
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文