Thursday, 20 November 2008

C++: An STL-like circular buffer (Part 3)


Now it's time flesh out the class, and consider the interface we'd like to give it.

First, STL containers have a number of requirements, laid down in the C++ standard in section 23.1. This includes definitions for these nested type definitions:
  • value_type - type of the items help in the container
  • reference - type of a reference to an item in the container
  • const_reference - type of a reference to a constant item in the container
  • iterator - type of container iterator
  • const_iterator - type of container const interator
  • difference_type - type expressing the distance between two iterators (or const_iterators)
  • size_type - type that represents any non-negative value of difference_type
As a first step, we'll add to our class the following typedefs, remembering that T is the template parameter name for the container item type:
typedef T value_type;
typedef size_t size_type;
typedef int difference_type;
typedef value_type &reference;
typedef const value_type &const_reference;
This is a starting point. We'll define the iterator types later, and some of these definitions can be made a little more elegant, too.

Next time: we'll decide the mechanics of the class and how to manage resources.

Wednesday, 19 November 2008

C++: An STL-like circular buffer (Part 2)

In part 1 we looked at what a circular buffer is, and why you'd want to use (or write) an STL-compliant circular buffer. Now let's start to write one.

First, a few preliminaries:
  • How will we ensure it is thread safe? This is a real issue, as most circular buffer applications are in producer/consumer applications where the consumer and producer are running on different threads. The answer: for an STL container, this is not an issue - we will ignore threading issues, as current STL container implementations do. It is up to the client of the circular_buffer class to use it in a thread safe manner.
  • Do we want to write a container adaptor? Some standard C++ containers are actually container adaptors - data types that wrap and adapt existing container types and give them a more specialised interface. For example, std::priority_queue is an adaptor that, by default, wraps a std::vector. In this instance, it is not particularly beneficial to wrap any other container type, and so we will not make the circular_buffer an adaptor type.
Every journey has a start

So let's kick off the data type by writing a simple starting scaffold:
template <typename T> //< See note below
class circular_buffer
{
public:
circular_buffer(size_t capacity);
};
This defines the class, circular_buffer, that will store items of type T.

There is already one issue apparent that we will shelve for a while: allocators. STL containers all take an additional template parameter that describes how the container should allocate and free internal memory resources. We'll come back to that later. (Honest)

The constructor takes the fixed size of the internal data store of the circular buffer. We could chose to provide a default implementation to make using the class 'easier', but I don't see any advantage to doing so. Each use of a circular buffer is individual, and the storage requirements depend on the situation. Providing a default value could lead to problems in practice, so I'd like to force the programmer to consider the size of storage needed each time they instantiate a circular_buffer.

Although this is a very simple scaffold, let's write a unit test to i) prove that the code compiles OK, and to ii) check that it works.
int main()
{
circular_buffer cb1(10);
circular_buffer cb1(100);
}
Naturally, this compiles cleanly. However, it won't link or run, since we've not written the body of the constuctor yet. All this fun, and more, is yet to come.

Next time: the next step is to set up the basic typedefs required of an STL-compliant container.

On programming: You gotta care about the code

The following article is excerpted from a forthcoming book project.

It doesn't take Sherlock Holmes to work out that good programmers write good code. Bad programmers... don't. They produce monstrosities that the rest of us have to clean up. You want to write the good stuff, right? You want to be a good programmer.

Good code doesn't pop out of thin air. It isn't something that happens by luck when the planets align. To get good code, you have to work at it. Hard. And you'll only get good code if you actually care about good code.

Good programming is not borne from mere technical competence. I've seen highly intellectual programmers who can produce intense and impressive algorithms, who know their language standard by heart, but who write the most awful code. It's painful to read, painful to use, and painful to modify. I've seen more humble programmers who stick to very simple code, but who write elegant and expressive programs that are a joy to work with.

Based on my years of experience in the software factory, I've concluded that the real difference between adequate programmers and great programmers is this: their attitude. Good programming lies in taking a professional approach, and wanting to write the best software you can, within the Real World constrains and pressures of the software factory.

The code to hell is paved with good intentions. To be an excellent programmer you have to rise above good intentions, and actually care about the code - to foster positive perspectives and develop healthy attitudes. Great code is carefully crafted by master artisans, not thoughtlessly hacked out by sloppy programmers, or erected mysteriously by self-professed coding gurus.

You want to write good code. You want to be a good programmer. So, you care about the code:
  • In any coding situation, you refuse to hack something that seems to work. You strive to craft elegant code that is clearly correct (and has good tests to show that it is correct).
  • You write code that is discoverable (that other programmers can easily pick up and understand), that is maintainable (that you, or other programmers, will be easily able to modify in the future), and that is correct (you take all steps possible to determine that you have solved the problem, not just made it look like the program works).
  • You work well alongside other programmers. No programmer is an island. Few programmers work alone; most work in a team of programmers, either in a company environment, or in open source project. You consider programmers, and construct code that other people can read. You want the team to write the best software possible, rather than to make yourself look clever.
  • Any time you touch a piece of code you strive to leave it better than you found it (either better structured, better tested, more understandable...).
  • You care about code and about programming, so you are constantly learning new languages, idioms, and techniques. But you only apply them when appropriate.
Fortunately, you're reading this because you do care about code. It interests you. It's your passion. Have fun programming. Enjoy cutting code to solve tricky problems. Produce software that makes you proud.

Tuesday, 18 November 2008

C++: An STL-like circular buffer (Part 1)

This is the first in a series of postings that will walk through the process of writing a C++ STL-like container. Specifically, we're going to look at the implementation of a circular buffer in C++.

In this first part we'll cover a little introductory ground.

What is a circular buffer?

A circular buffer is a fixed size data structure that presents its storage space as one long continuous buffer. The internal store effectively wraps around itself internally (circularly, hence the name).

Because of the internal data storage scheme, a circular buffer only lends itself to pushing a block of data onto the front of the buffer, and pulling a block of data from the back of buffer. It specifically will not support insertion in the middle of the data, and needn't support random access, either. Typically, circular buffers are FIFO.

Once the internal data store is full, a circular buffer implementation could either choose to:
  • not accept any more data until the buffer is emptied, or to
  • accept any amount of data, silently overwriting the older data with the new data.
Like a fixed-size array, you always know how much space a circular buffer will occupy - a useful property in many applications, and an possible advantage over a std::vector. It also has fixed performance characteristics.

A circular buffer is often employed in producer-consumer scenarios, where one component supplies data and another asynchronously consumes that data. We commonly see this pattern in multimedia and communications applications.

Why would you want an STL-like circular buffer?

So why would you specifically write an STL-like implementation of a circular buffer in C++?
  1. Interoperability. Any container that subscribes to the STL's interface can be used seamlessly with existing C++ algorithms (either those in the std namespace supplied in the C++ standard library, or other third-party STL-like algorithms, like those in the Boost library).
  2. Discoverability. If your container has an STL-like interface then C++ programmers will be easily able to pick it up and work with it. Sure, they'll have to understand its specific performance characteristics, and to which situations it is particularly suited (as you do with any C++ container). But an STL-like container will be easy to learn, and also easy to understand in order to modify.
  3. Good taste. The STL is the benchmark for good C++ library design. Sure, there are a few oddnesses and warts in there. No library is perfect. But it's a generally well thought out, and carefully constructed beast. There's a lot to be said for fitting into its world view.
  4. Reusability. Resuse is often a shadow that programmers chase unnecessarily. However, in most cases giving a simple container an STL-like interface, rather than a very application-specific interface will allow you to use it in more than one situation; a small sliver of a silver bullet, if ever there was one.
  5. Learn the inner workings of the STL. And if you're really bored, here's another great reason to write your own STL-like container: If you want to understand how the STL really works, how to extend it, and how to use it like a master then there's nothing better than writing your own STL code.
Hopefully that's a compelling list of reasons.

In the next posting, I'll launch into some C++. Buckle your std::seatbelts!

Closing down. Everything must go.

Anyone want to buy a small, cute software development team? One previous careful owner. House-trained (mostly).

There's a direct connection between bankers in the US having a little private disaster party and the job security of the rest of the world. The ripples are moving gradually further out, and sadly the writing's now one the wall for my development team in Cambridge, UK. It's a real shame, but largely unavoidable: when you make consumer products, and distributers are not buying any stock at Christmas time, or can't get credit to do so, then you have a problem. And there aren't many solutions.

So our office will almost certainly close its doors for the last time in a few days.

Personally, it's a shame. It's not often you get to work on something you are genuinely passionate about. It's not often you are able to construct something truly excellent from scratch, to work on an excellent codebase, fully unit tested from the start, and to do so with a team of excellent programmers who you're happy to meet in the pub as much as the work place. It's a shame that the economic conditions will consign our codebase and products to a very small chapter of history. Plans for world domination have been put on hold.

Still, onwards. To new challenges. The first of which will be finding a job. I'm going to have to buy some shoes...

Article: This "Software" Stuff

The next issue of ACCU's C Vu magazine is out now, containing my latest Professionalism in Programming column. Entitled This "Software" Stuff, it's the first part of a mini-series investigating what software development is really all about. It's your chance to find out what happens when you mix custard and spaghetti.

This issue of C Vu was editted by Gail Ollis, and contains articles on virtualisation, PC-Lint, debugging, as well as Matthew Wilson's ruminations on C and C++.

For the cover design. I thought I'd break the mold and go for an industrial "grunge" feel this month. I think it worked pretty well. It was the first time I've used Gimp 2.6 for print work, and it was great fun.

Friday, 7 November 2008

Kubuntu 8.10 on Parallels

Kubuntu 8.10 is out, full of KDE 4 goodness (honestly, that's not sarcasm - KDE4 is quite usable these days). I've been running it under Parallels on Mac OS, and there's good news and bad news...

The good news

Kubuntu 8.10 works really well under the beta of Parallels Desktop 4. But it's not out yet, and we're not allowed to talk about it. You never heard it from me.

The bad news

Kubuntu 8.10 does not work under the current release version of Parallels Desktop 3. As soon as KDE4 attempts to start, the video mode goes wobbly and X restarts. kdm goes on an infinite start-crash-start-crash loop. It's great fun for the first thirty restarts or so.

So what's going wrong? I'm not entirely sure. You can start X by hand fine. And you can run X apps, so it wouldn't appear to be a video support problem. I've been tinkering with bits of the startkde script. The failure stems from the call to the ksmserver, which handles the majority of the bringing up the KDE session. It's around about here that my enthusiasm for debugging left, and I escaped back into Parallels betaland for a simpler experience.

Helpfully, if you try to start a failsafe X session under Parallels 3, it fails for entirely different reasons.