Hello!
I would like to ask how to search for a container element which
matches some criteria.
Here is what I mean.
Suppose I have a structure:
struct my_struct {
my_struct(const std::string &lname, const std::string &lvalue,
const std::string &lothername, const bool lsomething,
const bool lanotherone, const unsigned lunsignedValue)
: name(lname), value(lvalue), othername(lothe rname),
something(lsome thing), anotherone(lano therone),
unsignedValue(l unsignedValue) {}
my_struct() {}
std::string name;
std::string value;
std::string othername;
bool something;
bool anotherone;
unsigned unsignedValue;
};
And I have a vector (or other container) which holds elements of
type my_struct:
std::vector<my_ struct>
I want to search for an element in the container which for example
has name "hello" and an unsignedValue less than 83333.
Or search for an element
which has name "world", value "world", any othername, true for
something
and false for anotherone and any value of unsignedValue. etc.
I have come up with two solutions, both are basically the same, they
use std::find_if using a specially for this purpose written predicate.
They differ only by method how search parameters are passed to the
predicate.
Solution 1:
The constructor of class my_struct_searc her can take all parameters to
be
searched. The ones which do not matter are left default initialized.
To search my_struct::unsi gnedValue a unary predicate can be specified
to my_struct_searc her. If none is specified the default
"DefaultFunctor "
is used which just returns true.
struct DefaultFunctor : public std::unary_func tion<unsigned, bool>
{
bool operator()(cons t unsigned c) const { return true; }
};
template <typename CmpFunctor = DefaultFunctor>
class my_struct_searc her {
private:
CmpFunctor CompareFunction ;
boost::logic::t ribool b_something;
boost::logic::t ribool b_anotherone;
protected:
my_struct local;
public:
my_struct_searc her(
const std::string &name = std::string(),
const std::string &value = std::string(),
const std::string &othername = std::string(),
const boost::logic::t ribool &something =
boost::logic::i ndeterminate,
const boost::logic::t ribool &anotherone =
boost::logic::i ndeterminate,
const CmpFunctor &f = DefaultFunctor( ))
: local(name, value, othername, true, true, 0),
b_something(som ething), b_anotherone(an otherone),
CompareFunction (f) {}
bool operator()(cons t my_struct &ms) {
bool found = true;
found = found && (local.name.emp ty() ||
(ms.name.find(l ocal.name) != std::string::np os));
if (found == false) return false;
found = found && (local.value.em pty() ||
(ms.value.find( local.value) != std::string::np os));
if (found == false) return false;
found = found && (local.othernam e.empty() ||
(ms.othername.f ind(local.other name) != std::string::np os));
return found && baseCompare(ms) ;
}
bool baseCompare(con st my_struct &ms) {
bool found = true;
found = found && (boost::logic:: indeterminate(b _something) ||
(ms.something == b_something));
if (found == false) return false;
found = found && (boost::logic:: indeterminate(b _anotherone) ||
(ms.anotherone == b_anotherone));
if (found == false) return false;
return found && CompareFunction (ms.unsignedVal ue);
}
};
Example of usage:
// find an element in a container which has part of
// name "hell" and unsignedValue < 280
iter = std::find_if(co ntainer.begin() , container.end() ,
my_struct_searc her<std::binder 2nd<std::less<u nsigned> > >("hell",
"", "", boost::logic::i ndeterminate,
boost::logic::i ndeterminate, bind2nd(std::le ss<unsigned>(),
280)));
What I dont like in this solution is that to search using only a
single
criteria which happens to be at the end of constructor (can be
specified
as one of the last parameters of ctor) all the other parameters must
be specified as default. Like: my_struct_searc her<>("", "", "", true),
to search just for my_struct::some thing being true.
Solution 2:
Use the same mechanism as in solution 1, just make specifying search
criteria easier by "chaining up" search parameters by having a
structure (struct nice_params) with member functions which set the
search parameter and return reference to *this.
template <typename CmpFunctor = DefaultFunctor>
class other_searcher {
public:
struct nice_params {
my_struct local;
boost::logic::t ribool b_something;
boost::logic::t ribool b_anotherone;
CmpFunctor CompareFunction ;
nice_params(con st CmpFunctor &f = DefaultFunctor( )) :
CompareFunction (f),
b_something(boo st::logic::inde terminate),
b_anotherone(bo ost::logic::ind eterminate) {}
nice_params &name(const std::string &name) { local.name = name;
return *this; }
nice_params &value(const std::string &value) { local.value =
value; return *this; }
nice_params &othername(cons t std::string &othername) {
local.othername = othername; return *this; }
nice_params &something(cons t boost::logic::t ribool &something)
{ b_something = something; return *this; }
nice_params &anotherone(con st boost::logic::t ribool
&anotherone) { b_anotherone = anotherone; return *this; }
};
protected:
nice_params params;
public:
other_searcher( const nice_params &p) : params(p) {}
bool operator()(cons t my_struct &ms) {
bool found = true;
found = found && (params.local.n ame.empty() ||
(ms.name.find(p arams.local.nam e) != std::string::np os));
if (found == false) return false;
found = found && (params.local.v alue.empty() ||
(ms.value.find( params.local.va lue) != std::string::np os));
if (found == false) return false;
found = found && (params.local.o thername.empty( ) ||
(ms.othername.f ind(params.loca l.othername) != std::string::np os));
return found && baseCompare(ms) ;
}
bool baseCompare(con st my_struct &ms) {
bool found = true;
found = found &&
(boost::logic:: indeterminate(p arams.b_somethi ng) || (ms.something ==
params.b_someth ing));
if (found == false) return false;
found = found &&
(boost::logic:: indeterminate(p arams.b_another one) || (ms.anotherone ==
params.b_anothe rone));
if (found == false) return false;
return found && params.CompareF unction(ms.unsi gnedValue);
}
};
Example of usage:
// search for an element in a container which
// value member contains "is a"
//
iter = std::find_if(vm s.begin(), vms.end(),
other_searcher_ exact<>(other_s earcher<>::nice _params().value ("is
a")));
I like this solution better because search criteria can be
specified nicer.
Could you suggest other ideas/methods how to search such data
structure?
Here is a compilable code of both versions with more usage examples.
http://www.catonmat.net/tmp/cpp.searching.container.txt
Thanks!
--
P.Krumins