Click here to Skip to main content
15,889,200 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi everybody,

I'm reading the documentation regarding boost's filter_iterator[^] and I'm trying to use it as follows.

I have two classes: employee and catalog. The latter is a container for the former. The container, internaly, uses a std::list to store the data and thus I have full access to std::list begin() and end() methods. So, I'm exposing those iterators rather than the list itself. An example:

C++
class employee {
    public:
        string name;
        int age;
};

class catalog
{
    public:
        typedef list<employee> list_type;
        typedef list_type::iterator iterator;
        // ...etc...

    private:
        list_type _list;

    public:
        iterator begin_all() { return _list.begin(); }
        iterator end() { return _list.end(); }
    
};


In this manner I'm attempting catalog to be used in functions such as std::for_each.

Now, say that I want the class to filter for those employees whose name starts with a specific letter, or to filter for those within a specific age range. The exposed interface i'm expecting is something like this:

C++
    ...

public:
    iterator begin_age_range(int min, int max) {
        // ???
    }


Now, I have read that Boost's filter_iterator might be able to help me on this one. The doc's samples show how to use it within a method, but I haven't quite determined how to adapt it for my needs.

So, the question is: would any of you, kind CPians, shred a light, a clue, an idea, on how could I solve this problem? Or, perhaps, am I approaching it in a wrong way?

¡Thanks in advance!
Posted

You need to create a functor, like
C++
struct Range
{
    Range(int min, int max): _min(min), _max(max){}
    bool operator(int value_)
    {
        return (_min <= value_ <= _max) ? true : false;
    }
    int _min;
    int _max;
}


Now expose a new type (for boost range iterator) from your class
C++
....
public:
typedef boost::filter_iterator<range,> FilterIter;

// and these member functions

FilterIter begin_age_range(int min, int max) 
		{
			return boost::make_filter_iterator<range>(Range(min, max), _list.begin(), _list.end());
		}
		
		FilterIter begin_age_range(const Range& range) 
		{
			return boost::make_filter_iterator<range>(range, _list.begin(), _list.end());
		}

		FilterIter end_age_range(int min, int max) 
		{
			return boost::make_filter_iterator<range>(Range(min, max), _list.end(), _list.end());
		}

		FilterIter end_age_range(const Range& range) 
		{
			return boost::make_filter_iterator<range>(range, _list.end(), _list.end());
		}


</range></range></range></range>


And you can use it like this-
C++
std::copy(cl.begin_age_range(20, 3000), cl.end_age_range(20, 3000), std::ostream_iterator<int>(std::cout, " "));
</int>


finally your whole code could look like this-
C++
#include <iostream>
#include <string>
#include <list>
#include <boost/iterator/filter_iterator.hpp>
#include <random>
#include <sstream>
#include <algorithm>

using namespace std;



class employee {
    public:
		// I added this construtor for ease....
		employee(const string& n, const int a): name(n), age(a){}
		// I added this for converting into int
		operator int() const { return age;}
		// I added this to pring employee details
		//operator employee() const { cout << "\n Name : " << name << "\t Age : " << age; }

        string name;
        int age;
};

struct EmployeePrinter
{
	EmployeePrinter(){}
	void operator()(const employee& emp)
	{
		cout << "\n Name : " << emp.name << "\t Age : " << emp.age; 
	}
};
 
// For Predicate passed to boost 
// The Predicate argument must be Assignable, Copy Constructible, 
// and the expression p(x) must be valid where p is an object of type Predicate, 
// x is an object of type iterator_traits<iterator>::value_type, and where the type of p(x) must be convertible to bool.
struct Range
{		
	Range(const Range& r): _min(r._min), _max(r._max){}
    Range(int min, int max): _min(min), _max(max){}
    bool operator()(const employee& emp)
    {
		return ((_min <= emp.age) && (emp.age <= _max)) ? true : false;
    }
    int _min;
    int _max;
};

class catalog
{
    public:
        typedef list<employee> list_type;
        typedef list_type::iterator iterator;
        // ...etc...
		typedef boost::filter_iterator<range,> FilterIter;
		

    private:
        list_type _list;
 
    public:
        iterator begin_all() { return _list.begin(); }
		iterator end() { return _list.end(); }
		// and these member functions

		FilterIter begin_age_range(int min, int max) 
		{
			return boost::make_filter_iterator<range>(Range(min, max), _list.begin(), _list.end());
		}
		
		FilterIter begin_age_range(const Range& range) 
		{
			return boost::make_filter_iterator<range>(range, _list.begin(), _list.end());
		}

		FilterIter end_age_range(int min, int max) 
		{
			return boost::make_filter_iterator<range>(Range(min, max), _list.end(), _list.end());
		}

		FilterIter end_age_range(const Range& range) 
		{
			return boost::make_filter_iterator<range>(range, _list.end(), _list.end());
		}

		// I added this function for adding employees...
		void add(employee& emp){ _list.push_back(emp);}
    
};

int main()
{	
	catalog cl;
	for(int i = 0; i < 100; i++)
	{
		int r = rand();
		stringstream ss;
		ss << "test_name " << r;
		cl.add(employee(ss.str(), r));	
	}
	
	// now you can use it like this...
	std::copy(cl.begin_age_range(20, 3000), cl.end_age_range(20, 3000), std::ostream_iterator<int>(std::cout, " ")); // Int conversiton is used here
    std::cout << std::endl;

	std::cout << "\n or like this" << std::endl;

	// or like this
	Range range(5000, 70000);
	std::for_each(cl.begin_age_range(range), cl.end_age_range(range), EmployeePrinter());
    std::cout << std::endl;
	
	return 0;
}
 
Share this answer
 
v2
Hi Ashish,

Your solution worked perfectly, with a slight modification. Thank you very much, it really helped a lot. For the record, I'll leave my working program. Now I'll try to make it word with std::function and using lambdas... :-)

C++
#include <iostream>
#include <string>
#include <list>
#include <functional>
#include <algorithm>
#include 

using std::string;
using std::cout;
using std::endl;
using std::list;
using std::function;
using std::for_each;
using std::shared_ptr;
using boost::filter_iterator;
using boost::make_filter_iterator;

class employee;

class employee
{
    public:
        employee()
            : income(0.0), age(0), manager(nullptr)
        { }
        employee(const string& name, double income, int age, employee* manager)
            : name(name), income(income), age(age), manager(manager)
        { }
        employee(const employee& copy)
            : name(copy.name), income(copy.income), age(copy.age), manager(copy.manager)
        { }

        string manager_name() const { 
            return manager != nullptr ? manager->name : "[Sin jefe]"; 
        }

        void print() const { cout << name << endl; }

        string name;
        double income;
        int age;
        employee* manager;
};

struct range_func
{
    range_func()
        : _min(0), _max(0)
    { }
    range_func(const range_func& copy)
        : _min(copy._min), _max(copy._max) 
    { }
    range_func(int min, int max)
        : _min(min), _max(max) 
    { }

    bool operator()(const employee& emp) const {
        return _min <= emp.age && emp.age <= _max;
    }

    int _min;
    int _max;
};

struct substr_func
{
    substr_func() 
    { }
    substr_func(const string& str) 
        : _str(str)
    { }
    substr_func(const substr_func& copy)
        : _str(copy._str)
    { }

    bool operator()(const employee& emp) const {
        return emp.name.find(_str) != string::npos;
    }

    string _str;
};

struct mgr_func
{
    mgr_func()
        : _ptr(nullptr)
    { }
    mgr_func(employee* ptr)
        : _ptr(ptr)
    { }
    mgr_func(const mgr_func& copy)
        : _ptr(copy._ptr)
    { }

    bool operator()(const employee& emp) const {
        bool val = false;
        if (_ptr == nullptr)
            val = emp.manager == nullptr;
        else 
            val = emp.manager_name() == _ptr->name;
        return val;
    }

    employee* _ptr;
};


class catalog
{
    public:
        typedef list<employee> employee_list;
        typedef employee_list::size_type size_type;
        typedef employee_list::iterator iterator;
        typedef employee_list::const_iterator const_iterator;
        typedef filter_iterator<range_func,> range_filter_iterator;
        typedef filter_iterator<substr_func,> substr_filter_iterator;
        typedef filter_iterator<mgr_func,> mgr_filter_iterator;

    private:
        employee_list _employees;
        
    public:

        catalog() { }

        size_type size() const { return _employees.size(); }
        void clear() { _employees.clear(); }

        void add(const employee& emp) { _employees.push_back(emp); }
        void add(const string& name, double income, int age, employee* mgr) {
            add(employee(name, income, age, mgr));
        }

        iterator begin_all() { return _employees.begin(); }
        iterator end() { return _employees.end(); }

        range_filter_iterator begin_age_range(int min, int max)  {
			return begin_age_range(range_func(min, max));
		}
		
		range_filter_iterator begin_age_range(const range_func& func) {
			return make_filter_iterator<range_func>(func, _employees.begin(), _employees.end());
		}

        range_filter_iterator end_age_range(int min, int max) {
            return end_age_range(range_func(min, max));
        }

        range_filter_iterator end_age_range(const range_func& func) {
            return make_filter_iterator<range_func>(func, _employees.end(), _employees.end());
        }

        substr_filter_iterator begin_substr(const string& value) {
            return begin_substr(substr_func(value));
        }

        substr_filter_iterator begin_substr(const substr_func& func) {
            return make_filter_iterator(func, _employees.begin(), _employees.end());
        }

        substr_filter_iterator end_substr(const string& value) {
            return end_substr(substr_func(value));
        }

        substr_filter_iterator end_substr(const substr_func& func) {
            return make_filter_iterator(func, _employees.end(), _employees.end());
        }

        mgr_filter_iterator begin_mgr(employee* mgr) {
            return begin_mgr(mgr_func(mgr));
        }

        mgr_filter_iterator begin_mgr(const mgr_func& func) {
            return make_filter_iterator(func, _employees.begin(), _employees.end());
        }

        mgr_filter_iterator end_mgr(employee* mgr) {
            return end_mgr(mgr_func(mgr));
        }

        mgr_filter_iterator end_mgr(const mgr_func& func) {
            return make_filter_iterator(func, _employees.end(), _employees.end());
        }
};

int main(int argc, char* argv[])
{
    catalog cat;
    employee fer("Fernando", 10000, 29, nullptr);
    cat.add(fer);
    cat.add("Gabriela", 13500, 26, &fer);
    employee jime("Jimena", 9500, 22, &fer);
    cat.add(jime);
    cat.add("Mario", 4300, 37, &jime);
    employee caty("Catalina", 15000, 31, nullptr);
    cat.add(caty);
    employee gis("Gisela", 14750, 30, &caty);
    cat.add(gis);
    cat.add("Omar", 12700, 30, &gis);

    auto func = [](const employee& emp) { 
        emp.print(); 
    };

    cout << "*** Todos los empleados *** " << endl;
    for_each(cat.begin_all(), cat.end(), func);
    cout << endl;

    cout << "***** POR EDAD *****" << endl;

    cout << "Edad entre 25 y 30" << endl;
    for_each(cat.begin_age_range(25, 30), cat.end_age_range(25, 30), func);
    cout << endl;

    cout << "Edad menor a 25" << endl;
    for_each(cat.begin_age_range(0, 25), cat.end_age_range(0, 25), func);
    cout << endl;

    cout << "Edad mayor a 30" << endl;
    for_each(cat.begin_age_range(30, 100), cat.end_age_range(30, 100), func);
    cout << endl;

    cout << "***** POR NOMBRE *****" << endl;

    cout << "Nombre que contenga 'na'" << endl;
    for_each(cat.begin_substr("na"), cat.end_substr("na"), func);
    cout << endl;

    cout << "Nombre que contenga 'ri'" << endl;
    for_each(cat.begin_substr("ri"), cat.end_substr("ri"), func);
    cout << endl;

    cout << "***** POR JEFE *****" << endl;

    cout << "Le reportan a Fernando" << endl;
    for_each(cat.begin_mgr(&fer), cat.end_mgr(&fer), func);
    cout << endl;

    cout << "Le reportan a Caty" << endl;
    for_each(cat.begin_mgr(&caty), cat.end_mgr(&caty), func);
    cout << endl;

    system("pause");
    return 0;
}


</range_func></range_func></employee></algorithm></functional></list></string></iostream>


The output being:

*** Todos los empleados ***
Fernando
Gabriela
Jimena
Mario
Catalina
Gisela
Omar

***** POR EDAD *****
Edad entre 25 y 30
Fernando
Gabriela
Gisela
Omar

Edad menor a 25
Jimena

Edad mayor a 30
Mario
Catalina
Gisela
Omar

***** POR NOMBRE *****
Nombre que contenga 'na'
Fernando
Jimena
Catalina

Nombre que contenga 'ri'
Gabriela
Mario

***** POR JEFE *****
Le reportan a Fernando
Gabriela
Jimena

Le reportan a Caty
Gisela

Press any key to continue . . .
 
Share this answer
 
Comments
Ashish Tyagi 40 20-Feb-12 3:39am    
Thanks for accepting and up-voting my answer :-), Enjoy coding....

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900