в частности, у меня есть список объектов класса с несколькими строковыми членами-объектами (NID, customerNumber, studentNumber, fName, lName).
Я хочу повторно использовать следующий код для поиска узла, который соответствует ключу поиска, независимо от того, является ли искомый элемент данных NID или любым другим из строковых данных класса.
nodePtr = firstPtr;
for(; nodePtr != NULL && nodePtr->str != str; nodePtr = nodePtr->nextPtr);
if(nodePtr != NULL)
//the nodePtr points to the node that matches the search key
else
//no node matched the search key
если бы это был PHP-код, я мог бы использовать значение переменной в качестве имени другой:
$node->${$var}
но в C ++ есть в любом случае повторно использовать код?
Самый гибкий способ сделать это — предоставить предикат в качестве параметра шаблона:
template <typename Pred>
Node * find_if(Node * node, Pred pred) {
for (; node && !pred(node); node = node->next);
return node;
}
В C ++ 11 вы можете назвать это лямбда-выражением:
if (Node * node = find_if(first, [&](Node * n){return n->NID == nid;})) {
// node points to the matching node
} else {
// not found
}
или, если вы застряли в прошлом, функциональный объект:
struct CompareNID {
CompareNID(std::string nid) : nid(nid) {}
bool operator() {Node * n) {return n->NID == nid;}
std::string nid;
};
Node * node = find_if(first, CompareNID(nid));
или, так как все ваши поля являются строками, вы можете пожертвовать гибкостью для использования tersity, используя указатели на члены, давая нечто похожее на ваш пример PHP:
Node * find(Node * node, std::string Node::*member, std::string const & value) {
for (; node && node->*member != value; node = node->next);
return node;
}
Node * node = find(first, &Node::NID, nid);
Похожий на std::find_if
:
template<typename N, typename P>
N* my_find_if(const N* head, P pred)
{
N* ptr;
for (ptr = head; ptr != nullptr && !pred(ptr); ptr = ptr->nextPtr)
;
return ptr;
}
Можно назвать так:
my_find_if(firstPtr,
[](Node* node){ return node->str == str; });
Изменить лямбда на любое выражение, которое вам нужно.
Конечно, я бы рекомендовал вам использовать стандартные контейнеры вместо того, чтобы создавать свой собственный список. Тогда вы могли бы использовать стандарт std::find_if
вместо.
Да, вы хотите передать 2 лямбда-выражения (C ++ 11) или работающие объекты (C ++ 03) в свой алгоритм «поиска».
В C ++ 03 вы можете передать 2 функции повышения: одну для «найденного» случая и одну для «не найденного» случая.
void search( std::string str,
boost::function<void()> ifFound,
boost::function<void()> ifNotFound )
{
//search
if( nodePtr != NULL )
{
ifFound();
}
else
{
ifNotFound();
}
}
Выберите соответствующие сигнатуры функций, но именно так вы передаете динамические функции.
Вы могли бы использовать std::function
вместо boost::function
,
Если вы хотите сделать сам поиск более гибким, то есть с какой частью вашего объекта вы пытаетесь сопоставить, используйте также динамический предикат.
void search( Pred pred// , ifFound, ,ifNotFound )
{
if( pred( nodePtr ) ) // then it is found
}
Как вы видите, предикат возьмет указатель узла и вернет истину / ложь. Таким образом, разные предикаты будут использоваться для сопоставления разных членов данных.
Если вам действительно нравится понятие «повторно используемый код», я бы предложил вам использовать стандартную библиотеку.
Если ваши списки длинные и вы постоянно выполняете эти поиски, ручной поиск идет медленно, и вы можете использовать boost::multi_index
создать log-N время поиска по вашим различным полям.
Может быть, вы хотите использовать указатель на члены:
typedef string Node::*NodeStringPtr;
NodeStringPtr nodeStrPtr = nullptr;
std::vector<NodeStringPtr> nodeStrings {&Node::NID, &Node::str, &Node::fName};
for (auto& ptr : nodeStrings)
{
nodePtr = firstPtr;
for (; nodePtr != NULL && nodePtr->*ptr != str; nodePtr = nodePtr->nextPtr);
if (nodePtr)
{
nodeStrPtr = ptr;
break;
}
}
if(nodePtr != NULL)
//the nodePtr matches the search key, nodeStrPtr matches the element
else
/* ...*/
Еще один способ — использовать указатели на члены (возможно только в том случае, если все члены одного типа):
struct Item {
std::string NID,
customerNumber,
studentNumber,
fName,
lName;
};
typedef std::vector<Item>::iterator nodePtr;
typedef std::string Item::* MemberPtr;
struct List {
std::vector<Item> list;
nodePtr search(const std::string& str, MemberPtr mem_ptr)
{
// this is the code you want to reuse, I took the liberty and used
// standard lib's algorithm
return std::find_if(list.begin(), list.end(),
[&](const Item& item){ return str == item.*mem_ptr; });
// will return list.end() if it doesn't find anything
}
};
int main()
{
List lst;
lst.search("John", &Item::fName);
lst.search("Doe", &Item::lName);
lst.search("42", &Item::customerNumber);
}
Я думаю, что это ближе всего к вашему примеру PHP.