C++/STL: std::transform with given stride?

2020-03-19 06:19发布

I have a 1d array containing Nd data, I would like to effectively traverse on it with std::transform or std::for_each.

unigned int nelems;
unsigned int stride=3;// we are going to have 3D points
float *pP;// this will keep xyzxyzxyz...
Load(pP);
std::transform(pP, pP+nelems, strMover<float>(pP, stride));//How to define the strMover??

4条回答
迷人小祖宗
2楼-- · 2020-03-19 06:59

The answer is not to change strMover, but to change your iterator. Define a new iterator class which wraps a float * but moves forward 3 places when operator++ is called.

You can use boost's Permutation Iterator and use a nonstrict permutation which only includes the range you are interested in.

If you try to roll your own iterator, there are some gotchas: to remain strict to the standard, you need to think carefully about what the correct "end" iterator for such a stride iterator is, since the naive implementation will merrily stride over and beyond the allowed "one-past-the-end" to the murky area far past the end of the array which pointers should never enter, for fear of nasal demons.

But I have to ask: why are you storing an array of 3d points as an array of floats in the first place? Just define a Point3D datatype and create an array of that instead. Much simpler.

查看更多
家丑人穷心不美
3楼-- · 2020-03-19 07:02

use boost adapters. you can get iterators out of them. the only disadvantage is compilation time.

vector<float> pp = vector_load(pP);
boost::for_each(pp|stride(3)|transformed(dosmtn()));
查看更多
放荡不羁爱自由
4楼-- · 2020-03-19 07:04

Well, I have decided to use for_each instead of transform any other decisions are welcome:

generator<unsigned int> gen(0, 1);
            vector<unsigned int> idx(m_nelem);//make an index
            std::generate(idx.begin(), idx.end(),gen);
            std::for_each(idx.begin(), idx.end(), strMover<float>(&pPOS[0],&m_COM[0],stride));

where

template<class T> T op_sum (T i, T j) { return i+j; }
template<class T> 
class strMover
    {
    T *pP_;
    T *pMove_;
    unsigned int stride_;
    public:
        strMover(T *pP,T *pMove, unsigned int stride):pP_(pP), pMove_(pMove),stride_(stride)
            {}
        void operator() ( const unsigned int ip )
            {
            std::transform(&pP_[ip*stride_], &pP_[ip*stride_]+stride_, 
                pMove_, &pP_[ip*stride_], op_sum<T>);
            }
    };

From first look this is a thread safe solution.

查看更多
Explosion°爆炸
5楼-- · 2020-03-19 07:06

This is terrible, people told you to use stride iterators instead. Apart from not being able to use functional objects from standard library with this approach, you make it very, very complicated for compiler to produce multicore or sse optimization by using crutches like this. Look for "stride iterator" for proper solution, for example in c++ cookbook.

And back to original question... use valarray and stride to simulate multidimensional arrays.

查看更多
登录 后发表回答