-
Callbacks
Hai guys,
This is going to look really convoluted, but please bare with me. I'm writing an app which requires A LOT of asynchronous events. Normally I handle these the generally accepted way: add a function to the class: handle_x_and_y or on_x_and_y. Before you know it you have a ton of functions, but it's fine. In JavaScript, or any other scripting/programming language with inline functors, this goes a lot smoother and feels a lot more intuitive. The reason I believe is mostly context (scoping). If the fifth function will only ever be called from within the forth function, and this is repeated a lot, the program flow can get a little confusing. Yes, in the future maybe a different function will want to call the fifth function, but this is besides the point. Most examples and source I've seen people will order the functions by the order in which they are called in a sequence (ie. resolve-> connect -> handshake -> read headers-> read content -> write response).
Anyway, so there's your standard option. Then there's boost::lambda, which is a solution, but looks even more cryptic than the next solution. Apparently you can inline function code in C++0x, which looks okay but won't be in my projects for quite a while. In a thread I read someone arguing they have little point because you could just use anonymous structs, except their functions can't be accessed directly (no name), so someone suggested just typedef struct. Which works fine. Of course, why not just go "struct A {};" versus "typedef struct {} A;"?
One of the problems encountered with event callbacks in ActionScript 2.0 was losing scope, so you couldn't access upper variables. The same seems to exist in that solution. So I would think at the very least "this" should be passed to the structs. Thus their solution, in an example situation, would look like this:
Code:
#include <iostream>
template<typename SELF_TYPE>
class callback_base
{
public: typedef SELF_TYPE self_type;
public: callback_base(SELF_TYPE& self_) : self(self_) { }
//public: SELF_TYPE& GetSelf() { return self; }
protected: SELF_TYPE& self;
};
class SomeClass
{
public: void Start()
{
std::cout << "SomeClass::Start" << std::endl;
struct Callback1 : callback_base<SomeClass> { Callback1(SomeClass& sc) : callback_base(sc) {} void Invoke(unsigned int a1, unsigned int a2)
{
std::cout << "Callback1::Invoke" << std::endl;
struct Callback2 : callback_base<SomeClass> { Callback2(SomeClass& sc) : callback_base(sc) {} void Invoke(const char* a1)
{
std::cout << "Callback2::Invoke" << std::endl;
struct Callback3 : callback_base<SomeClass> { Callback3(SomeClass& sc) : callback_base(sc) {} void Invoke(bool a1)
{
std::cout << "Callback3::Invoke" << std::endl;
} }; Callback3 cb3(self);
cb3.Invoke(true); //temp
} }; Callback2 cb2(self);
cb2.Invoke("some argument"); //temp
struct Callback4 : callback_base<SomeClass> { Callback4(SomeClass& sc) : callback_base(sc) {} void Invoke(int a1)
{
std::cout << "Callback4::Invoke" << std::endl;
self.End();
} }; Callback4 cb4(self);
cb4.Invoke(1); //temp
} }; Callback1 cb1(*this);
cb1.Invoke(2, 2); //temp
}
public: void End()
{
std::cout << "SomeClass::End" << std::endl;
}
};
int main()
{
SomeClass someClass;
someClass.Start();
std::cin.get();
}
I think my question is obvious. There's so much "wrong c++" and "unsafe c++" out there. I guess "wrongness" is sometimes debatable, but is this at least safe and fine? Are there better solutions, other than putting all callbacks together? (yes, I know good naming helps, but other languages look so much better with this solution - minus the cluttered definitions - preprocessors could help).
Sorry for the tl;dr; Thanks in advance.
-
Not that there's anything necessarily wrong with it, per se, but I honestly don't see why you would want to do things like that. It just seems overly complicated and the amount of work required doesn't justify (in my mind, at least) whatever benefits it may have.
That aside, for the constructor base-class initializer you have:
Code:
: callback_base(sc)
Some compilers will accept that, but the correct way to do it is to explicitly provide the template parameter, eg:
Code:
: callback_base<SomeClass>(sc)
And the code formatting is pretty terrible - it's very difficult to follow the way it's structured. I'd recommend something along the lines of:
Code:
#include <iostream>
template<typename SELF_TYPE>
class callback_base
{
public: typedef SELF_TYPE self_type;
public: callback_base(SELF_TYPE& self_) : self(self_) { }
//public: SELF_TYPE& GetSelf() { return self; }
protected: SELF_TYPE& self;
};
class SomeClass
{
public: void Start()
{
std::cout << "SomeClass::Start" << std::endl;
struct Callback1 : callback_base<SomeClass>
{
Callback1(SomeClass& sc)
:callback_base<SomeClass>(sc)
{ }
void Invoke(unsigned int a1, unsigned int a2)
{
std::cout << "Callback1::Invoke" << std::endl;
struct Callback2 : callback_base<SomeClass>
{
Callback2(SomeClass& sc)
: callback_base<SomeClass>(sc)
{ }
void Invoke(const char* a1)
{
std::cout << "Callback2::Invoke" << std::endl;
struct Callback3 : callback_base<SomeClass>
{
Callback3(SomeClass& sc)
: callback_base<SomeClass>(sc)
{ }
void Invoke(bool a1)
{
std::cout << "Callback3::Invoke" << std::endl;
}
};
Callback3 cb3(self);
cb3.Invoke(true); //temp
}
};
Callback2 cb2(self);
cb2.Invoke("some argument"); //temp
struct Callback4 : callback_base<SomeClass>
{
Callback4(SomeClass& sc)
: callback_base<SomeClass>(sc)
{ }
void Invoke(int a1)
{
std::cout << "Callback4::Invoke" << std::endl;
self.End();
}
};
Callback4 cb4(self);
cb4.Invoke(1); //temp
}
};
Callback1 cb1(*this);
cb1.Invoke(2, 2); //temp
}
public: void End()
{
std::cout << "SomeClass::End" << std::endl;
}
};
int main()
{
SomeClass someClass;
someClass.Start();
std::cin.get();
}
-
Thanks Sebastiani,
Fixed the constructor.
The reason for the code formatting was because I didn't want 2 tabs in between the scopes (struct is taking up it's own tab unnecessarily) - causing the code to widen quicker.
Made it a macro to solve the complex syntax and format issues:
Code:
CREATE_CALLBACK(Callback1, SomeClass,
{
std::cout << "Callback1::Invoke" << std::endl;
self.End();
}, void) cb1(*this);
cb1.Invoke(); //temp
I'm not too happy with that being the final product, but I'm a huge fan of nested callbacks. Ever use jQuery? (or a less-elegant JavaScript library) I guess I could just wrap it up in a scripting language (Python or LUA or JavaScript) but I'm not up to that atm - but it'll be easier to convert if I ever do.
There's no way to turn an std::string or const char* into a macro, right? I tried # and ##. Meaning..
#define class_A(type_A) #type_A
std::string d("class A { } a;");
class_A(d);
I'm still interested in reasons against doing this (besides 1. macros are evil, 2. other developers will dislike it). Thanks!