views:

126

answers:

6

I'd like a generic method for retrieving the data from a vector.

I have a the following class and vector:

class myClass
{
    public:

    myClass(int myX, float myZ, std::string myFoo)
     : x ( myX )
     , z ( myZ )
     , foo ( myFoo )
    {

    }
    myClass()
    {

    }

    int x;
    float z;
    std::string foo;

} ;
std::vector < myClass > myVector;

(The complete code can be seen here: http://codepad.org/iDD1Wme5 )

In this example I would like to be able to retrieve objects in the vector based on the "z" or "foo" members without having to write another 2 functions similar to "FindDataById".

Is that possible?

A: 

You can use functors and pass it to your lookup method. That I mean is, define class which will overload bool operator( vectorElement element) and within this operator you will choose method how do you want to lookup the values.

template <typename T>
class ILookUp
{
    bool operator( vector<T> elem)
    {
      if (elem == something)
          return true;
      false;
    }
};

class VectorStorage
{
     std::vector<Elements> lookup( ILookUp<Elements> lookup)
     {

         .....         

         if ( lookup(elem))
         {
              //add element to vector or whatever.
         }

          .....

         return result;
     }

          .....

}
Artem Barger
A: 

It might be worth taking a look at std::find defined in algorithm and boost::lambda

Amos Robinson
+4  A: 

You can use a template and pointer to member.

typedef vector<myClass> myVector;

template<typename T>
bool FindDataById(const T &id, T myClass::* idMember, myClass &theClass,
                                                       const myVector &theVector)
{
   for(myVector::const_iterator itr = theVector.begin(); itr != myVector.end();
                                                                          ++itr){
       if((*itr).*idMember == id){
          theClass = *itr;
          return true;
   }

   return false;
}

Then call using, e.g.,

FindDataById(string("name"), &myClass::foo, theClass, theVector)
FindDataById(5, &myClass::x, theClass, theVector)
FindDataById(5.25f, &myClass::z, theClass, theVector)

Or, go with the find_if idea:

template<typename T>
struct Finder {
   T val_;
   T myClass::* idMember_;

   Finder(T val, T myClass::* idMember) : val_(val), idMember_(idMember) {}
   bool operator()(const myClass &obj) { return obj.*idMember_ == val_; }
};

And use:

find_if(theVector.begin(), theVector.end(), Finder<string>("name", &myClass::foo))
find_if(theVector.begin(), theVector.end(), Finder<int>(5, &myClass::x))
find_if(theVector.begin(), theVector.end(), Finder<float>(3.25f, &myClass::z))

See the answer of MSalters for a way to deduce the template argument automatically.

Ari
The "Finder" bit doesn't work, missing template parameter. See my code how to do Template Argument Deduction.
MSalters
Right. Edited to provide another (perhaps less elegant) way of doing it.
Ari
You don't need the string("name") anymore now.
MSalters
Right again. Edited to that effect. Thanks.
Ari
+1  A: 

std::find_if has already been suggested, but without a code sample, so here's a more detailed version:

Define two functors to identify the object you're interested in:

struct z_equals {
  z_equals(float z) : z(z) {}

  bool operator()(const myClass& obj)
    return z == obj.z;
  }

  float z;
};


struct foo_equals {
  foo_equals(const std::string& foo) : foo(foo) {}

  bool operator()(const myClass& obj)
    return foo == obj.foo;
  }

  const std::string& foo;
};

And now, to search for elements where z == 42.0f, or foo == "hello world":

std::find_if(myVector.begin(), myVector.end(), z_equals(42.0f));
std::find_if(myVector.begin(), myVector.end(), foo_equals("hello world"));
jalf
A: 
MSalters
A: 

Wow! Excellent solutions. Thanks everyone, especially Ari! I will use your solution. It works just as i wanted.