Повторно использовать один и тот же код для выполнения одной и той же логики для разных членов данных объектов в списке.

в частности, у меня есть список объектов класса с несколькими строковыми членами-объектами (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 ++ есть в любом случае повторно использовать код?

1

Решение

Самый гибкий способ сделать это — предоставить предикат в качестве параметра шаблона:

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);
2

Другие решения

Похожий на 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

Да, вы хотите передать 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 время поиска по вашим различным полям.

1

Может быть, вы хотите использовать указатель на члены:

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
/* ...*/
0

Еще один способ — использовать указатели на члены (возможно только в том случае, если все члены одного типа):

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.

0