views:

142

answers:

4

Why are async callback socket methods usually static? (assume I understand static class, method and data objects). Would there be a fundamental design/logic error if one were to write a class using these as instance methods? Is there anything special that one should be careful to avoid?

+1  A: 

Callback functions are usually static because you don't know what anything about the class that you are making a call back on.

In fact a class instance function usually carries the "this" pointer as a hidden first parameter.

See: http://www.drdobbs.com/184403375;jsessionid=P5JGJLQBDYGWBQE1GHOSKH4ATMY32JVN?pgno=2

Edit: Also see http://stackoverflow.com/questions/2400690/why-callback-functions-needs-to-be-static-when-declared-in-class

Goz
Note that this is true only for C++, not C#.
Adam Robinson
+1  A: 

At least in C++, a non-static method expects to receive this as a hidden parameter. Since whatever's invoking it as a callback doesn't know that, it won't pass that extra parameter. If you did manage to invoke a non-static member function as a callback, anything that attempted to use member data (which would happen via this) would (almost certainly) have major problems.

I'd guess C# is at least somewhat similar, though I haven't studied the details enough to know for sure. Regardless of language, the code needs to know what data to work with. The obvious difference is that C# also supports delegates, which are extremely useful for callbacks.

Edit: as to how to avoid this, at least in C++ it's fairly simple. You'd typically want to use a library like ACE, Boost ASIO, or POCO that shields you from most of the messy details. If you do some looking around, you can probably find at least a few dozen others as well. Oh, I should add that most of the application framework-style libraries (e.g., wxWidgets and Qt) include classes to shield you from most of the messy details as well. The obvious difference is that these tend to "take over" the design of the application in general (though, in fairness, I should add that ACE tends a bit toward the same).

Of course, people have written libraries for C# as well, but I haven't used any enough to make a meaningful recommendation about them. A lot here also depends on the level of abstraction you're looking for. WCF (for example) does quite a bit more than just shield you from the messy details. Rather, it builds essentially its own complete idea of how to work with networks, that happens to (somewhere in there) be able to use sockets -- but for the most part, you're not really working with sockets anymore, you're working with WCF, which happens to use sockets.

Ultimately, however, the library is just hiding, not changing, what really happens. Somewhere inside the library, Winsock makes a callback to either a static member function or a free function that uses the basic C calling convention that it expects. That function then uses extra data (usually the parameter that's passed to it) to figure out what to invoke from there. What varies is how thick a layer of "insulation" there is between your code and the callback from Winsock.

Jerry Coffin
You are talking here about C callbacks. C++ is different, you have to pass pointer to the method that will be invoked on a class instance. If library doesn't provide that, this callback should be exposed to the user as a functor or interface (observer, for example). Otherwise this is a very bad C-style minimalistic library that is hard to use and you will end-up writing a lot of code or at least wrap callbacks with something more useful.
Vlad Lazarenko
@Vlad: did you bother reading the question? He's not asking if that's the only way to do things, but asking *why* the code he's seen is written that way. Yes, there are libraries that shield you from that, but they typically work as he describes: invoke a static member function (or free function), that uses other data to invoke what you've asked for.
Jerry Coffin
C# delegates (function pointers, basically, though slightly more than that) do not suffer from the same limitation. Delegates that point to instance methods are syntactically identical to delegates that point to static methods. Whether the delegate stays entirely inside managed code or is marshaled into unmanaged code, the .NET runtime (CLR) handles the instance-vs-static distinction transparently.
Adam Robinson
A: 

They are usually static because it is usually hard to write a good software, especially well designed library for asynchronous I/O in C++. Otherwise they are not static at all. Take a look at Boost.ASIO. It is by far the best library for asynchronous operations for C++.

As for the C language, there is no such thing as static method because there are no methods :-) But those callback usually provide you with an option to pass addition data trough the "void *" pointer that will be given you back on callback. Using that, you can write object-oriented code in C. In the most basic C++ example of asynchronous I/O the C language is used. That is why you might think this is a common case. For C it is.

Vlad Lazarenko
"They are usually static because it is usually hard to write a good software, especially well designed library for asynchronous I/O in C++" - Not true. Design decisions have nothing to do it, but a limitation in C++.
jgauffin
Limitation? Look at boost function, boost bind, boost lambda, boost callbacks.
Vlad Lazarenko
There's nothing to stop a C++ design allowing 'opaque user data' in callback functions in the same way that you suggest for C. As long as the 'opaque user data' is big enough for a pointer then it's fairly standard (IMHO) for a C++ callback design to pass the this pointer through to the callback at which point a member function can be called...
Len Holgate
There is nothing to stop you in C++ at all. Of course, this also includes bad design.
Vlad Lazarenko
+2  A: 

There is no specific reason that they should be static. It all depends on your design.

If the callback needs to access members in the class then it can be declared as a instance member. However, you will need to make sure that you correctly syncronize the access to the instance members because the callback can be invoked concurrently from different threads.

I guess the examples you looked at all passed the required data through to the callback through the IAsyncResult.AsyncState so did not require additional information from the class. If you can do this it can simplify your code, since you no longer need to worry about thread syncronization etc..

Update

Just to clarify, based on the comments it seems I should have beed more explicit. It is not that the static callback would be thread safe, but rather that in a design where the data required is passed to the callback and of course this data is not shared by other threads then locking is not required. And since the callback is passed all the data it requires, it does not need access to the class instance and can therefore be made static.

Chris Taylor
There's nothing inherently safe about accessing static members from other threads. The same synchronization would need to occur.
Adam Robinson
@Adam, I should have been more clear. I was trying to say that when the data is passed to the callback and the callback does not need to access shared data then no locking is required. While accessing data in the class.
Chris Taylor
@Chris: Perhaps my inexperience with C++ is showing, but in C# there would be no reason to believe that a static function would not be accessing shared static resources.
Adam Robinson
@Adam, Still I am not being clear and I appologize. In this regard there is no difference between C# and C++. My point that I failed to communicate is that `when NOT accessing shared data` there would not be a need to lock. And in my explanation I tried to sketch a picture where the static method receives all its data (even the components of that data are not shared) via the AsyncState member. I am certain we are all on the same page, I have just muddied the water with a badly worded explanation.
Chris Taylor
@Chris: In that case, yes, we are on the same page ;)
Adam Robinson