#include <iostream>
#include <string>
#include <functional>
#include <vector>
class B;
class A {
public:
std::function<void(B*, const std::string&)> m_callback;
void* m_dest = nullptr; // Will be used in later versions of this code.
void trigger(const std::string& message) {
m_callback(m_dest, message);
}
};
class B {
public:
void callbackFunc(void* ignore, const std::string& message) {
std::cout << message << std::endl;
}
void otherCallbackFunc(void* ignore, const std::string& message) {
std::cout << "The other function was called!" << std::endl;
}
};
int main {
A a1, a2;
B* b1 = new B();
B* b2 = new B();
a1.m_dest = (void *)b;
a1.m_callback = std::bind(&B::callbackFunc, b, std::placeholders::_1, std::placeholders::_2);
a2.m_dest = (void *)b;
a2.m_callback = std::bind(&B::otherCallbackFunc, b, std::placeholders::_1, std::placeholders::_2);
a1.trigger("Hello, world.");
a2.trigger("Hello, world.");
delete B;
}
The above code works.
However, what do I do if the B instance is not 'static'; if it's stored in a vector or some other data structure which may std::move
it? How can I rewrite A such that, provided with an up-to-date memory address for B, it can still correctly call callbackFunc()
?
One possible solution is through the use of lambdas:
int main {
A a1, a2;
std::vector<B> bees = {};
bees.push_back(B()); // b1
bees.push_back(B()); // b2
a1.m_callback = [](void* dest, const std::string& message){
((B*)dest)->callbackFunc(dest, message);
};
a2.m_callback = [](void* dest, const std::string& message){
((B*)dest)->otherCallbackFunc(dest, message);
};
/* move b instances about using std::move... */
bees.insert(bees.begin(), B()); // b3
// At this point in the program, we do not know which callback function was attached for each B instance.
a1.m_b = (void *)&bees[0]; // b1 is now at a different address, but we still know where it is and can tell a1 that information.
a2.m_b = (void *)&bees[1]; // b2 is now at a different address, but we still know where it is and can tell a2 that information.
a1.trigger("Hello, world.");
a2.trigger("Hello, world.");
}
However, this is pretty gruesome. It involves creating potentially vast numbers of barely-different lambdas, scattered throughout whatever code might want to put a callback into A. It's ugly, inefficient, and a pain to work with.
How can I do this better? How can I bind member functions of a potentially-mobile instance?