Asio C++ library: it gets nearly everything right

posted in Computer food
Published January 25, 2011
Advertisement
Bur nearly everything doesn't mean everything. For those who believe I'm talking about boost.asio, you're pretty much right as Asio has been incorporated in Boost. However, it exists as a non-Boost library, so I will continue to speak about Asio as Asio, and not boost.asio (there are a few differences, for example the namespace where the declarations are stored, but it's mostly the same thing).

So, Asio is supposed to be a well designed library. When it comes to generic AIO (asynchronous I/O), I tend to agree to that statement. When it comes to the network part, I protest : there are obvious design errors, and they lead to real design issue in th client code.

Let'sbegin by the first, obvious design limitation: the definition of socket types. There are two problems with this one:

* sockets are non-copiable. This is lame: I want to copy my sockets! I can transfer a file descriptor to another thread, but I can't transfer a socket to another thread - unless I store a pointer to it in a shared_ptr<>. Wow. I need to do a dynamic allocation to be able to use a socket in multiple threads?

No, guys. Sockets needs to be copiable. First, they are supposed to be lightweight objects (we're speaking of an object with only a few properties). Second, some well-known server architecture patterns mandates this (you accept the socket in the main thread, and yo pass it to another thread).

If you can't make it copiable, make it movable (that's going to be super-easy with the move constructors in C++1x, but it can be implemented today on th model of auto_ptr<>), or allow for the creation of weak socket (something along the line of weak_ptr<> ; this is bad: no one owns the socket and this is as well a design issue. You shall always know who is owning what).

* bad interface design. Both synchronous ans asynchronous behaviors in the same interface? that's an Error. Either you do synchronous operations (read, write, readfrom, writeto) or you do async operations. There is a good reason for that: it's not the same thing, it's not the same use. Of course, one can mix both types of operations in the same application, using the same socket - in which case he'd better instantiate an async_socket from his socket or the other way round. Two different behaviors in the same interface, this is a clear violation of the single responsibility principle. Bad, bad, bad.

Let's continue with another annoying design issue: symbol names. On most occasions, Asio decided to keep the names that have been used for years in the network programming world in order to mimic a known interface and soften the learning curve. So we have an acceptor class, which accept() stream sockets. But on the other hand, Asio decided to renew the vocabulary of the network programmers by introducing new terms. We no longer gethostbyname(), we resolve a host name.

The latter is correct by all means: you generalize, so you provide a distinct name that explains the generalized concept and is semantically correct. The former is problematic: you use a name with a strong meaning, and you reuse it to do something different - although it's not very different. There is another problem that comes with name reuse : you can't provide an abstract meaning to your class, and that forces you to be very concrete. At the same time, your class is central to the design, and many people uses it. So you have something which is both concrete and stable (per the meaning given by R.C Martin: a stable package/class is a package/class on which many other package/class depends). And this is not a good thing.

I'm sure you all have issues with th design of a library you use. Feel free to share them !
1 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement