0

I would like to understand how to use the Lambda functions in C++.

I'm using a custom types, as follows:

struct my_toy_t {
    uint32_t    id;
    cv::Rect    box;
    cv::Mat     data;
}

typedef std::map<uint32_t, my_toy_t*> my_toy_map_t;

And:

int main() {

    my_toy_map_t input_map;
    my_toy_map_t output_map;

    // Some insertions in input_map...

    my_toy_map_t::iterator it;
    for (it = input_map.begin(); it != input_map.end(); ++it)
    {
        if (check_cond(it->second->box)) {
            output_map.insert(std::make_pair(it->first, it->second));
            input_map.erase(it->first);
        }
    }
    return 0;
}

bool check_cond(cv::Rect box) {
    if (box.area > 100)
        return true;
    else
        return false;
}

It may be noted that my_toy_map_t is just a std::map<uint32_t, my_toy_t*>, and check_cond function checks a simple condition.

Is it possible to translate this code (insertion in output map and remotion from input_map if check_cond returns true) using a Lamba function?

Delimitry
  • 2,987
  • 4
  • 30
  • 39
vdenotaris
  • 13,297
  • 26
  • 81
  • 132
  • 2
    You'd probably be better off without the pointers. – chris Aug 15 '13 at 10:44
  • You are overusing the postfix `_t`. You would have a type name like `my_marvellous_type_container_t` in the future – Manu343726 Aug 15 '13 at 10:44
  • 2
    `std::copy_if` together with `std::inserter` are your friends here. – Xeo Aug 15 '13 at 10:46
  • I dont think lambda function would be the way to solve the problem. Lamdba functions were created when you had to create the data on the fly. In your case, you want to append/erase known data. I think this http://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11 should help. – Lokesh A. R. Aug 15 '13 at 10:49
  • 1
    Avoid remotioning while iterationing – doctorlove Aug 15 '13 at 10:49
  • I have never used this type of function, so I'm sorry if there are​ mistakes in my example. I just would like to understand how to use correctly this feature. – vdenotaris Aug 15 '13 at 10:49
  • @LokeshRavindranathan *"Lambda functions were created when you had to create the data on the fly"* What? Lambda functions are the eassiest and most common way to provide boolean predicates, for example. `` header is full of algorithms dessigned to be used in that way. – Manu343726 Aug 15 '13 at 11:02
  • 4
    The original code has undefined behavior. You erase the element the iterator points to, which invalidates the iterator. – James Kanze Aug 15 '13 at 11:08

3 Answers3

2

May be std::copy_if and std::inserter ?

std::copy_if(input_map.begin(),input_map.end(),
             std::inserter(output_map,output_map.end()) ,
    [](const std::pair<uint32_t, my_toy_t*>& x ){ 
                             return (x.second)->box.area > 100;    
                              } 
                );

For removing I can think of this :

my_toy_map_t temp;

std::remove_copy_if(input_map.begin(), input_map.end(), 
                    inserter(temp, temp.end()),
                    [](const std::pair<uint32_t, my_toy_t*>& x ){ 
                                 return (x.second)->box.area > 100;    
                                  } );

input_map.swap(temp);
P0W
  • 46,614
  • 9
  • 72
  • 119
  • In this way I have to visit two time `input_map`, but I could perform insertion/remotion during the same cycle. – vdenotaris Aug 15 '13 at 11:13
  • @vdenotaris agreed, but the point was **"I would like to understand how to use the Lambda functions in C++"** – P0W Aug 15 '13 at 11:16
  • That ugly pair is more readable as `my_toy_map_t::value_type const&` – MSalters Aug 15 '13 at 13:42
2

First of all, pointers are not needed. If you want to avoid the copies, use std::reference_wrapper.

Second, you are iterating ove a container, and modifying the container in the process. That has undefined behaviour: The previous iterators are invalid after a insertion/deletion operation.

What you are trying to do is a really a filtering process: Getting the elements of a container that pass a given condition, and fill another container with it.
That could be easily done with certain generic algorithms provided by the standard library. For example, the easiest way here is to use std::copy_if:

int main()
{
    std::copy_if( std::begin( input ) , std::end( input ) , std::inserter( output , std::end( output ) ) , [](const std::pair<uint32_t,my_toy_t>& pair ) { return pair->second.box.area > 100; });
}

As you can see, the function std::copy_if()expects a callable entity as the predicate of the filtering. In this case the eassiest way, and because you asked about how to improve the filtering with a lambda, is to pass a lambda function.

Reference:

Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • how come you're using a `back_inserter` on maps ? – P0W Aug 15 '13 at 11:02
  • `back_inserter` on map? – vdenotaris Aug 15 '13 at 11:02
  • 1
    @doctorlove Oh, thats true. I forget that is a map. I provide the generic solution. Give me a minute to fix it – Manu343726 Aug 15 '13 at 11:03
  • There's not necessarily a problem with iterating over a container and modifying it at the same time; you just have to ensure the validity of the iterators. – James Kanze Aug 15 '13 at 11:10
  • the element of input_map is pair, you can simply convert to `const my_toy_t& toy`? – billz Aug 15 '13 at 11:12
  • @JamesKanze yes, but it could be a problem, right? And what is better, checking iterators before any operation, or do the traserse-erasing in two separated phases? (Its a question, not a critic). What do you think? – Manu343726 Aug 15 '13 at 11:28
  • @Manu343726 It depends on what you're doing. In this case, which involves erasing from a map (so `std::remove_if` can't be used), he probably needs to write the loop himself, and since he has to do the test to know whether to do the erase anyway, using a single loop, correctly managing his iterators, is probably the best solution. – James Kanze Aug 15 '13 at 14:12
1

Could use std::for_each with mutable lambda

std::for_each(input_map.begin(), input_map.end(),
         [&output_map](const std::pair<uint32_t,my_toy_t*> &it) mutable{
    //   ^^                                                     ^^^^^^^
         if (it.second->box.area > 100)
         {
             output_map.insert(it);
         }
    });

Suggestion: you'd better use smart pointers + STL container instead of raw pointers raw pointer + STL container.

billz
  • 44,644
  • 9
  • 83
  • 100
  • And if would I like to transfer the element from `input_map` to `output_map`? – vdenotaris Aug 15 '13 at 10:58
  • you still need to erase it from input_map – billz Aug 15 '13 at 11:05
  • Is there a way to perform insertion/remotion directly? I'm reading that is not good to remove an element while iterationing. – vdenotaris Aug 15 '13 at 11:07
  • it's ok as long as you do it carefully. see: http://stackoverflow.com/questions/263945/what-happens-if-you-call-erase-on-a-map-element-while-iterating-from-begin-to – billz Aug 15 '13 at 11:09
  • 1
    Have a try with POW's new solution, looks like he has two nice solutions for you. – billz Aug 15 '13 at 11:12