I have been implementing a delegate class with C++11 that supports class member functions. I am interested in knowing if there are any potential problems with this implementation. My main concern is the private Key
struct of the Delegate
class. Can I always rely on the key being correct in all situations? Meaning, can I always identify certain function of certain object with it?
In there I am storing 3 things:
- hash of the member function pointer
- pointer to the object
std::function
wrapping the actual function
The hash is calculated from the function pointer with the getHash()
function. I've originally used only that as key, but I run into a problem with inherited classes. I've gotten the same hash for the inherited function with 2 different classes inheriting from same base class. For that reason, I've also stored the pointer to the actual object, using these 2 as key I am able to identify certain entry in the list of stored keys. I'd be really interested to know any potential issues with this implementation. This compiles with VS2013, and I am hoping it will also work with Clang, but I haven't tried it out yet.
template <typename...Params>
class Delegate {
private:
typedef std::function < void(Params...)> FunctionType;
struct Key
{
size_t m_funcHash;
void* m_object;
FunctionType m_func;
Key(void* object, size_t funcHash, FunctionType func) : m_object(object), m_funcHash(funcHash), m_func(func)
{}
bool operator==(const Key& other) const {
return other.m_object == m_object && other.m_funcHash == m_funcHash;
}
};
std::vector <Key> m_functions;
public:
template <typename Class>
void connect(Class& obj, void (Class::*func) (Params...) )
{
std::function < void (Class, Params...) > f = func;
FunctionType fun = [&obj, f](Params... params) { f(obj, params...); };
size_t hash = getHash(func);
m_functions.push_back(Key( &obj, hash, fun));
}
template <typename Class>
size_t getHash(void (Class::*func) (Params...)) {
const char *ptrptr = static_cast<const char*>(static_cast<const void*>(&func));
int size = sizeof (func);
std::string str_rep(ptrptr, size);
std::hash<std::string> strHasher;
return strHasher(str_rep);
}
template <typename Class>
void disconnect( Class& obj, void (Class::*func) (Params...) ) {
size_t hash = getHash(func);
for (unsigned int i = 0; i < m_functions.size(); ++i) {
auto key = m_functions[i];
if (key.m_funcHash == hash && key.m_object == &obj) {
m_functions.erase(m_functions.begin() + i);
--i;
}
}
}
template <typename ... Args>
void operator() (Args...args)
{
for (auto& key : m_functions) {
key.m_func (args...);
}
}
};
class BaseClass {
public:
virtual void print(const std::string& str) = 0;
};
class A : public BaseClass {
public:
void print(const std::string& str) {
std::cout << " Class A : Print [" << str << "]\n";
}
};
class B : public BaseClass {
public:
void print(const std::string& str) {
std::cout << " Class B : Print [" << str << "]\n";
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B b;
Delegate <const std::string&> delegate;
delegate.connect(a, &A::print);
delegate.connect(b, &B::print);
delegate("hello");
delegate("world");
delegate.disconnect(a, &A::print);
delegate("bye world");
delegate.disconnect(b, &B::print);
delegate("nobody there."); // should not print anything
std::cin.ignore();
return 0;
}