3

I have a destination std vector:

 std::vector<std::pair<float,float> > allVertices;

Why I want to use pairs,because every 2 floats present locations pair(x,y) .Now,I have a source std:: vector which has all those locations but as float array (called m_vertices).

I need to copy all the data to the end of allVertices from m_vertices and perform transform of the data during the copy.

std::transform came to my mind with Lambda but I can't figure out how to do the copy from float vector to the vector of float pairs.

The naive :

    std::transform(m_vertices.begin(),m_vertices.end(),allVertices.end(),
                [](float x,float y)->std::pair<float,float>
            {
                return std::pair<float,float>(x * 100.0f,y * 100.0f)  ;
            }
            );

Gives me compile time error :

error C2064: term does not evaluate to a function taking 1 arguments

And some more ugly stuff.

Btw,if someone could point out how to transform pairs of data without need in std::pair structure it would be even more helpful in my case.

UPDATE:

Due to some answers proposal to use typical iterator,I would like to emphasize that I really would like to see functional solution.If it is possible.

Michael IV
  • 11,016
  • 12
  • 92
  • 223
  • 2
    Note that `transform` has a version that takes two iterators, in which case your lambda would work. However, you need each of those two input iterators to return alternate elements of the vector, so see http://stackoverflow.com/questions/5685983/skipping-iterator – Steve Jessop Jun 29 '14 at 10:19

2 Answers2

6

The compiler message is quite clear here: Your lambda must take one input argument, but your lambda takes two input arguments x and y. You simply cannot use std::transform for your task, as std::transform only takes single values and transforms them, not pairs of values.

Here are three possible ways of achieving your task:

Plain old imperative programming

Why not simply use the plain old non-functional way like this:

for(auto it = m_vertices.begin(); it != m_vertices.end();++it){
    float x = *it;
    ++it;
    float y = *it;
    all_vertices.emplace_back(x*100f,y*100f);
}

Make sure that the size of m_vertices is even; otherwise this code will blow up, of course.

Lamdas and functional programming are nice, but sometimes simply doing it imperatively is easier.

Writing your own pair transformation function

Here is how you could write a function that does your reduction by using a lamdba:

template< class InputIt, class OutputIt, class BinaryReducerOp >
OutputIt transformPairs( InputIt first1, InputIt last1, OutputIt d_first,
                    BinaryReducerOp reducer_op );
    for(auto it = first1; it != last1;++it){
        auto& x = *it;
        ++it;
        if(it == last1) throw; // Input length not even!
        auto& y = *it;
        *d_first++ = reducer_op(x,y);
    }
}

Now you can use this function with your lambda. I.e.:

  transformPairs(m_vertices.begin(),m_vertices.end(),allVertices.end(),
                [](float x,float y)->std::pair<float,float>
            {
                return std::pair<float,float>(x * 100.0f,y * 100.0f)  ;
            }
            );

Writing a pair iterator

As Steve Jessop correctly states in his comment, writing an own pair iterator is even more flexible but also more work. It might look like this (sketch code, do not have a compiler here, might contain minor bugs):

template<typename It> struct PairIterator {
private:
    mutable It it; // mutable so we can move around in operator*
public:
    typedef decltype(it*) Element;

    PairIterator(const It& it) : it(it) {}

    bool operator!=(const PairIterator<It>& other) const { return other != it; }

    std::pair<Element, Element> operator*() const {
        const Element& e1 = it*;
        ++it;
        const Element& e2 = it*;
        --it;
        return std::make_pair(e1,e2);
    }

    PairIterator<It>& operator++(){
        ++it;
        ++it;
        return *this;
    } 
}

template<typename It>
make_pair_it(const It& it){ return PairIterator<It>(it); }

Now you could use std::transform like this:

std::transform(make_pair_it(m_vertices.begin()),make_pair_it(m_vertices.end()),allVertices.end(),
                    [](std::pair<float,float> p)->std::pair<float,float>
                {
                    return std::pair<float,float>(p.first * 100.0f,p.second * 100.0f)  ;
                }
                );
gexicide
  • 38,535
  • 21
  • 92
  • 152
  • So what is alternative solution? I need to transform every 2 numbers in the vector. – Michael IV Jun 29 '14 at 09:57
  • Yeah,of course this is one of the potential alternatives but I wanted to try smth C++11 way :) I mean,can't it be really done functionally? – Michael IV Jun 29 '14 at 10:01
  • @MichaelIV: I guess not with standard functionality. The reason for this is that reducing two adjacent elements to one is not used *that* often and thus didn't find its way into STL. If you have this use case often in your code with varying transformations, you can use my code to build a function that takes a lambda which encodes the transformation. Then you can use lambdas. I will add some example code, one moment please. – gexicide Jun 29 '14 at 10:05
  • Wow,nice trick.Which one of your solutions do you think will be faster? – Michael IV Jun 29 '14 at 10:11
  • @MichaelIV: Good question; with enough optimization, both might be equally fast. Without optimization the first should be faster because the pairs is emplaced instead of being copied into the vector. But unoptimized performance is not important; the optimized performance is what counts. Simply measure it; I guess with `-O3` you should not see a big difference. – gexicide Jun 29 '14 at 10:13
  • 3
    Note: rather than inventing an algorithm `transformPairs`, you could invent an iterator adaptor `pair_iterator`, that returns pairs of consecutive elements from its underlying iterator. It's more work, but pays off if you also want to use algorithms other than `transform` in this way. – Steve Jessop Jun 29 '14 at 10:17
  • @SteveJessop: Indeed, also an interesting idea. Added some sketch code for this. – gexicide Jun 29 '14 at 10:28
0

Also for your problem, since the memory array in both your vectors is identical, you could directly copy the memory, which would be the fastest solution i believe.

Note that you have to be sure of what you're doing before applying this to another case, it could easily be the source of a bug. If you try to copy different types (like double to float for example) it will not work.

allVertices.resize(m_vertices.size() / 2u);
std::copy_n(m_vertices.data(), m_vertices.size(), &(allVertices.front().first));

This code also breaks if m_vertices size is not even.

You can then simply use a ranged base for-loop to apply your teatment

for (auto & pair: allVertices)
    treatment(pair);
  • "you could directly copy the memory" you mean no new memory will be allocated in the destination vector?But in such a case transforms of the data will effect the initial vector so it is probably not an option as the source vector data must remain untouched. – Michael IV Jun 29 '14 at 12:27
  • no of course not, memory is allocated in the "resize" function and then copied effiently by copy_n (with a [memmove](http://en.cppreference.com/w/cpp/string/byte/memmove)). It avoids to create temporary variable as in some of the other solutions. – peroket Jun 29 '14 at 12:36