Передача действия над объектом
Всем доброго времени суток. имеется мап-ка <key, Struct *>, в которой содержатся данные о пользователях. Часть мапки указывает на nullptr в значении. Изначально в коде везде шло обращение с проверкой результата в каждом месте обращения. Грубо говоря, так:
key k;
if (__arr.find(k) != __arr.end() && __arr[k])
{
//работа с данными
}
Поменял сделанное: вынес отдельную функцию-геттер, которая по ключу делает проверку и возвращает структуру, если она есть в значении. Это выглядит так:
MyStruct *Foo::get_struct_by_id(const Key& key)
{
if (__arr.find(key) != __arr.end() && __arr[key])
return __arr[key];
return nullptr;
}
Но теперь вижу, что для перебора всех элементов в массиве приходится получать указатель на структуру, проверять его на валидность,и потом уже выполнять операцию.
вопрос: Как передать действие, которое буду совершать над объектом(экземпляром структуры), в качестве параметра?
Вижу это как-то так:
- выделяю функцию "a", которая возвращает структуру по ключу, если она существует
- создаю функцию "б", в которой происходит перебор объектов - и если объект существует, то над ним выполняется операция, которую мы передали в функцию "б" параметром.
- в коде в местах, где необходимо пробежаться по всем элементам с целью выполнения операции, вызываем функцию b с указанием параметра, в котором описываем те операции над объектом, которые хотим сделать.
Буду рад приведенному примеру кода. К слову, на языке вертится "передача лямбда-выражений", "передача функций", но не понимаю, как это сделать.
Не болейте)
Ответы (2 шт):
Проверка лучше так.
MyStruct *Foo::get_struct_by_id(const Key& key)
{
if (auto it = __arr.find(key); it != __arr.end() && *it)
return *it;
return nullptr;
}
И она эквивалентна этому
MyStruct *Foo::get_struct_by_id(const Key& key)
{
if (auto it = __arr.find(key); it != __arr.end())
return *it;
return nullptr;
}
Но по любому придётся делать функции вида:
bool has(K);
V get(K);
V* try_get(K);
Без этого не обойтись.
А вообще не надо хранить указатели на структуры.
Давайте для начала напишем вот такую воспомогательную функцию
template<typename K, typename V, typename F>
void apply_to(std::map<K,V*>& m, F f)
{
for(auto& el : m) {
if (el.second != nullptr)
f(el.first, *el.second);
}
}
функция проверяет значение в мапе на nullptr и если оно не нулевое, то вызывает указанную функцию (или нечто, что можно вызвать), предварительно разыменовав.
Теперь, как такое позвать.
Можно по старинке:
void process(int k, float v) {}
apply_to(m_data, process);
то есть, первым параметром мапа, вторым функция. Самое чудесное (как по мне), то функции даже не нужно знать, как там внутри сохраняются данные. совсем. Ну и указатель ушел.
Способ два.
std::function<void(int,float)> ff = process;
apply_to(m_data, ff);
std::function, как же без них.
Теперь пойдем в функторы, и там можно
struct func
{
void operator()(int k, float f) {}
};
apply_to(m_data, func());
ну и классика, лямбда
apply_to(m_data, [](int k, float f) { std::cout << k << " " << f << "\n"; });
конечно, если у Вас там не просто int/float, то я бы задумался о передаче пареметров но ссылке или ссылке на константу. Но тут уже нужно глубоко в код смотреть.
Если компилятор позволяет, то на это все можно ещё навешать разных концептов или enable_if, но это уже следующий этап.