3

I am trying to generalize the functions filterX() and filterY() in the following class Table to function filter().

The functions filterX() and filterY() only differ in the function they call inside the procedure. While filterX() calls getX(), filterY() calls getY().

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Row 
{
    public:
        void add(string x, string y, int val);
        string getX()   const { return d_x; } 
        string getY()   const { return d_y; } 
        int    getVal() const { return d_val; } 

    private:
        string d_x;
        string d_y;
        int    d_val;
};

class Table
{
    public:
        void add(string x, string y, int val);
        vector<int> filterX(string s);
        vector<int> filterY(string s);
    private:
        vector<Row> d_table;
};


//--------------------class Row----------------------------
void Row::add(string x, string y, int val)
{
    d_x   = x;
    d_y   = y;
    d_val = val;
}


//-------------------class Table---------------------------

void Table::add(string x, string y, int val)
{
    Row r;
    r.add(x, y, val);
    d_table.push_back(r);
}

vector<int> Table::filterX(string s)
{
    vector<int> result;
    vector<Row>::iterator it;
    for(it = d_table.begin(); it != d_table.end(); ++it) {
        if(it->getX() == s) {
            int val = it->getVal();
            result.push_back(val);
        }
    }
    return result;
}


vector<int> Table::filterY(string s)
{
    vector<int> result;
    vector<Row>::iterator it;
    for(it = d_table.begin(); it != d_table.end(); ++it) {
        if(it->getY() == s) {
            int val = it->getVal();
            result.push_back(val);
        }
    }
    return result;
}

int main()
{
    Table t;
    t.add("x1", "y1", 1);
    t.add("x1", "y2", 2);
    t.add("x2", "y1", 3);
    t.add("x2", "y2", 4);

    vector<int> vx = t.filterX("x1");
    vector<int> vy = t.filterY("y2");

    vector<int>::const_iterator it;

    cout << "Matching X" << endl;
    for(it = vx.begin(); it != vx.end(); ++it)
        cout << *it << "\t";
    cout << endl;

    cout << "Matching Y" << endl;
    for(it = vy.begin(); it != vy.end(); ++it)
        cout << *it << "\t";
    cout << endl;

    return 0;
}

I tried pointer to member function but got bogged down by compiler errors. For the following example, I would like to have the following main() if that is possible:

int main()
{
    Table t;
    t.add("x1", "y1", 1);
    t.add("x1", "y2", 2);
    t.add("x2", "y1", 3);
    t.add("x2", "y2", 4);

    // instead of filterX, need to pass getX
    // to a function named filter       
    vector<int> vx = t.filter("x1", getX);
    vector<int> vy = t.filter("y2", getY);

    return 0;
}
Anand
  • 1,122
  • 3
  • 20
  • 33
  • Thanks for all the answers. I had chose one of them as an answer and it is unfortunate that I could not check all them as answers. Sorry about that. – Anand Mar 08 '12 at 16:32

5 Answers5

2

A member function requires a pointer to the object instance. That is, think of getX as string Row::getX(const Table *this). You need to bind the member function with an instance placeholder.

E.g. using tr1,

vector<int> vx = t.filter("x1", std::bind(&Row::getX, std::placeholders::_1));

The binding creates a functor that takes in a Row object, assuming that your filter function is correctly defined. You should show code for filter. I presume it should be:

template <class function>
vector<int> Table::filter(const string &s, function f)
{
    vector<int> result;
    for (vector<Row>::const_iterator it = d_table.begin(), tEnd = d_table.end();
        it != tEnd; ++it)
    {
        if (s == f(*it)) result.push_back(it->getVal());
    }
    return result;
}
devil
  • 1,829
  • 16
  • 23
  • That works. My solution is a bit faster :p (at runtime). You should mention `#include `. – J.N. Mar 08 '12 at 04:02
  • By binding, more general members of `Row` can be employed. E.g. those that take in additional arguments. – devil Mar 08 '12 at 04:06
1

Here is how to do it by using pointer to member functions:

// helper to avoid type nightmare;
typedef string (Row::* GetterP)() const;

class Table 
{ 
public: 
    void add(string x, string y, int val); 

    // Define a templated function that can be called with GetX or GetY
    template <GetterP getter>
    vector<int> filter(string s)
    {
         int i = (d_table[i].*getter)(); // how to use getter in filter
    }

private:
    vector<Row> d_table;  
};

// Usage:
Table t;
t.filter<&Row::GetX>("");
J.N.
  • 8,203
  • 3
  • 29
  • 39
  • Remember that it will create separate copies of `Table::filter` for `GetX` and `GetY`. I think OP doesn't want separate copy (compile time), but to evaluate at runtime. – iammilind Mar 08 '12 at 04:11
  • @iammilind Well, true, it duplicates the generated code, but, if the compiler does its job, the call is faster by one less addition and (maybe) some inlining. Overall it probably doesn't matter that much, it's not like the function is huge anyway. – J.N. Mar 08 '12 at 04:19
1

If you want to use explicit PMF ( or at least see how they are used):

Declare the PMF type:

class Row 
{
    public:
    typedef string (Row::*getFilter)() const;
     // etc.
};

class Table
{
    public:
    // Call it like this:

    vector<int> pmf_filterX(string s)
    {
        return filter(s, &Row::getX);
    }
    private:
    // Use it like this:
    vector<int> filter(string s, Row::getFilter f)
    {
        vector<int> result;
        vector<Row>::iterator it;
        for(it = d_table.begin(); it != d_table.end(); ++it)
            {
            const Row& row = *it;

            if ((row.*f)() == s) 
                {
                int val = it->getVal();
                result.push_back(val);
                }
            }
        return result;
    }
};
Keith
  • 6,756
  • 19
  • 23
1

Here is the syntax for the way you want:

vector<int> Table::filter(string s, string (Row::*get)() const)
{                                  //^^^^^^^^^^^^^^^^^^^^^^^^^^^ member pointer
  ...
  if(((*it).*get)() == s) {  // call using the (*it). and not it->
  ... 
}

Call it as:

vector<int> vx = t.filter("x1", &Row::getX);
vector<int> vy = t.filter("y2", &Row::getY);
iammilind
  • 68,093
  • 33
  • 169
  • 336
1

Modify your code here:

public:
    void add(string x, string y, int val);
    vector<int> filter(string s, string (Row::*)() const);
private:
    ...

Here:

vector<int> Table::filter(string s, string (Row::*f)() const)
{   
    vector<int> result;
    vector<Row>::iterator it;
    for(it = d_table.begin(); it != d_table.end(); ++it) {
        if((*it.*f)() == s) {
            int val = it->getVal();
            result.push_back(val); 
        }
    }
    return result;
}

And here:

int main()
{
    ...
    vector<int> vx = t.filter("x1", &Row::getX);
    vector<int> vy = t.filter("y2", &Row::getY);

    ...
Manish
  • 3,472
  • 1
  • 17
  • 16