Asio is My Go-To C++ Application Framework

January 23, 2014
Asio is my current go-to framework for C++ application development. I find it to be an awesome piece of software. In many respects it's nothing new. It uses common design patterns and ways of doing asynchronous programming, but it has changed how I write non-GUI C++ software. I won't go over how to use it or even talk much about the patterns behind it- for that it has its own blog and book.
Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.

The creator Christopher Kohlhoff doesn't like to call it a framework, and instead calls it a toolkit. I think we are just playing with words here, but there is something to be said about how lightweight Asio is and how it doesn't force you into a corner like a heavy framework would. Asio is more than just a portable networking library or a portable serial library or a portable way to implement timers - and it's actually simpler to use than most of those to boot. It comes in two flavors- as a standalone library and as a part of Boost. I've almost exclusively used the Boost.Asio variant because I usually find what Boost has to offer necessary anyway.

For me, Asio has evolved into a sort of philosophy on how to do things asynchronously in a very simple way. It fills a void in application development where a full GUI framework like Qt or wxWidgets doesn't make sense or is too much. It gives a way to connect objects together and makes how they interact consistent. However, when starting a new application design, just saying you are going to use Asio, like with the bigger GUI frameworks, it gives you a pretty solid, yet flexible, way to implement common patterns and connect things together. Asio gives you an event loop for asynchronous processing of your own along with timers, sockets, and serial objects. Asio is all about asynchronous, but don't worry, you can be synchronous as well.

One thing that's got me hooked on Asio is what it will do from an architectural standpoint when it comes to multithreading object oriented code. From my experience, multithreading is hard from a design standpoint on large code base with many developers. To make matters worse, most multithreaded applications don't scale well from an implementation standpoint. It's hard to plan where multithreading is necessary up front and it's too easy to get into the "create a thread for everything" mode, for the same reason things are optimized before they need to be. Naturally, Asio will attempt to coerce you to stay single threaded and think asynchronous- and this is a good thing.

The ease of asynchronous operations without threads is where this puppy shines. If you've ever used select() or epoll() in a socket server you've done it, but probably not in a very OOP way. However, when it is obvious multithreading is necessary, Asio is right there to support you. The cool part is you are not just limited to a single event loop like most GUI frameworks try to corner you into. Have you ever tried to go multithreaded in something like wxWidgets or, shall I say, MFC? You have to start treating how you use those frameworks very differently and exceptions and corner cases become the norm. With Asio you can use as many io_service objects as you want and this makes it rather easy to go multithreaded when required.

I'm not implying it's only good for small applications either. I've used Asio as a framework for fairly large code bases and it just works.

Even though the API is an excellent design, small, and it's very easy to use there are some things that I wish the Asio documentation would stick out a little better.

  • Get familiar with boost::bind() and boost::ref(). Asio is built on these and it's at the very core of how you interface with it.
  • Use shared_ptr<> for your completion handler objects (or something like intrusive_ptr). Just, do it.
  • Deleting completion handlers is dangerous. Very dangerous. Even though you may cancel an outstanding asynchronous operation, there is still the chance that the completion handler will be called. The general recommendation is not to delete things until the io_service itself has been deleted, or use your own setup to prevent access to things that have been deleted.
  • Use boost::enable_shared_from_this() where possible, but note that this itself has some limitations. It doesn't work very well with inheritance.

Checkout the Boost.Asio examples to see just how simple it is to use. Watch this talk from the creator of Boost.Asio. You'll be glad you did and I have yet to see any documentation on the things he talks about in here.

Related Posts