Tuesday, 1 July 2014

Fun with standards

This, as I understand it, is completely legitimate c99:

>$ cat main.c
#include <stdio.h>

inline void foo()
{
        printf("inline\n");
}

int main()
{
        foo();
        return 0;
}


>$ cat outline.c
#include <stdio.h>

void foo()
{
        printf("out-of-line\n");
}

>$ clang -O0 -c main.c -o main.o && clang -O0 -c outline.c -o outline.o && clang -o inline-test-O0 main.o outline.o
>$ clang -O2 -c main.c -o main.o && clang -O2 -c outline.c -o outline.o && clang -o inline-test-O2 main.o outline.o
>$ ./inline-test-O0
out-of-line
>$ ./inline-test-O2
inline

Wednesday, 18 June 2014

Nice Languages Finish Last

I am writing things in Lua. Lua is a nice language, everybody keeps telling me so.

A very compelling use case for Lua is to use it as a scriptable configuration file. You can imagine how wonderful that would be:

Client wants to get configuration elements from the environment. They can already do that.

Client wants to get configuration from a different file with a weird proprietary format. They can already do that.

Client wants to use the most recent different file from a whole directory of them. They can already... Wait. They can't do that! They want to perhaps sleep for a bit and then check again. They can't do that either. You see Lua is a nice language and doesn't have a massive bloated API that can do everything. Python, Perl, or PHP could have done those things, because they are not nice.

They are useful.

Friday, 12 April 2013

Lessons From Working With Timezones.

I have been reviewing, and then when the reviewing turned up a load of pre-existing bugs actually fixing the damn things myself, a lot of code to do with handling timestamps. Here are some of the lessons learned.

Use boost date_time.
It's one of the boost bits that comes in a library rather than just a header file but don't let that put you off, it makes easy some things that were hard, and makes possible some things that were impossible. It is a good thing.

The rest of these rules will focus on the more prevalent posix types and functions.

Never put anything other than posix time in a time_t.
By posix time I mean time since midnight 1st Jan 1970 UTC, aka epoch time, or unix time, or timestamp. You will have a hard time persuading anything to treat your time_t value as anything else so just don't do it.

Never put anything other than the local time in a struct tm.
Effectively this means never calling gmtime. There is only one reason to ever call gmtime, and that is if you need to get a UTC time string, in which case don't keep the value around for any longer than it takes to create the string. Doing so is asking for trouble.

Anybody writing code that relies on being run in a particular timezone shall be taken outside and shot.
This should be obvious and strikes me as lenient, personally I would beat them with sticks until dead. Shooting's too good for them.

Anybody writing tests that rely on being run in a particular timezone shall be taken outside and beaten with sticks until they are sorry.
It may look like an easy and harmless shortcut but No, just No!

Never go near the TZ environment variable.
Oh TZ environment variable, how I hate thee? Let me count the ways:
- You are not thread safe
- You can be changed invisibly by other parts of the codebase.
- You are hard to clean up correctly.
- You do not even bloody work properly.
Yes, it turns out that Windows is, as you'd expect, expertly engineered to work in every location you can imagine except if you change the TZ environment variable, after which it will get the timezones right (but of course), but likely get the dates of change from standard to daylight saving and back again wrong. it will use the American dates, even when it is not in America.

Alter your timezone and check it again.
I have come to the belief that writing time-handling code in a timezone that coincides with UTC for half the year is an actual disadvantage allowing bugs to go unspotted. Now, whenever I go near it, I tell my dev box it is in Road Town.

anegada sunrise
The weather's better too.

Tuesday, 9 April 2013

Different Ways Of Thinking

An engineer's mind is focussed on objective reality, it needs to be, it is the medium in which he or she works. When an engineer tells you they will do something it means they intend to do it, and believe they will be able to do it.

A manager's mind is focussed on subjective reality, it needs to be, it is the medium in which she or he works. When a manager tells you they intend to do something it means they agree it would be good if it were done.

When a salesman tells you they intend to do something, they are just making noises they think will make you happy.

Monday, 18 March 2013

How Code Reviews Lead To Crappy Code

I have finally figured out what it is about code reviews I do not like.

There is a lot to like about code reviews, they extend knowledge, they allow developers with little experience in certain areas to work in those areas with the confidence that any dumb mistakes they make will be caught, and most importantly, they genuinely catch bugs - often very nasty subtle bugs at that.

At other shops I have done reviews of the 'cast your eyes over this before I press commit would ya' form, and these were low impact and high value. Here we are having a real quality drive in an (I think successful) attempt to pull a legacy product back from the brink of becoming a bug-ridden mess, and are doing long, involved, line-by-line dissections of every commit.

Here is what happens, like any department anywhere, some people are better at the job than others. Always fond of a massive generalisation I shall divide this entire gamut of human ability into two sorts of people which I shall refer to as senior devs and peons. Quickly, I find, the department falls into the state where peons do most of the work and the senior devs review it. Senior devs would love to do the work themselves, and would do it faster and better, and would use things like Patterns, and Modern Language Features, and all the wealth of experience and knowledge that has made them senior; but they are too busy with their reviews.

Ahha, you might say, but peons code slower, and so produce less work to review, so it should even out, right?

Would this were so, but peon work is often poor, or more often sort of okay but with subtle errors and missing some corner cases. It gets sent back, with constructive criticism carefully worded not to offend, and then returns, with a few different errors, and cycles backwards and forwards, and becomes a mix of intents, of the parts where the reviewer has put her foot down and explicitly dictated a design (sometimes I think I should just copy and paste a diff into the review comments) and, and here's the rub, parts where she has not had the heart, or the patience, to criticise any further, and has allowed the peon to muddle through in their own way.

I didn't bother picking up on the stinky bits in the first review you see, because at that point it didn't work at all and a bit of a pong was the least of its problems, and I kind of assumed they'd go when the thing was rewritten to, you know, work. But they did not, and now, on the third cycle of the whole depressing business, it seems cruel to pick up on them, and with heavy heart I am forced to let them through because, stinky though they may be, I'm just happy that they work.

And so you end up with code which, while it contains no known issues, is just not very good; it is fragile, and has bad encapsulation, and the tests are close to useless, and frankly, it sucks. Meanwhile it has actually taken longer than it would have taken the senior developer to do the damn thing themselves, and they would have done it better, and they are wondering if the department wouldn't be more productive without the peon in the first place. 

Friday, 21 September 2012

One Definition to Rule them all

Generally I am a little bit suspicious of people who can quote the C++ standard, knowing the exact definitions and all the obscure intricacies should only be necessary for compiler writers. The rest of us should get by with a broad understanding of how things work and the compiler should catch us when we stray.

In fact, and as an aside, I would actually consider too much knowledge of operator precedence to be positively harmful. People who know things can't help themselves but to use that knowledge and end up leaving out unnecessary braces in compound statements because it's obvious to them that they are unnecessary. Some idiot who hasn't memorised the rules (like me) then inherits the code, makes an incorrect assumption about what it's trying to do, and screws it up.

Bytes are cheap, just use brackets.

At least that was what I thought until I got bitten firmly in the arse by this little beauty:
http://stackoverflow.com/questions/6379422/c-multiple-classes-with-same-name
Which if I had been more familiar with standard I would have spotted immediately.

In my case I had a little interface I needed to mock for a couple of tests.

class ThingResponder
{
public: 
   virtual void respond(int) = 0;
};

In the first test I didn't care about it.

test1.cpp:

class MyThingResponder : public ThingResponder
{
public: 
   virtual void respond(int) {}
};

void test1::someTest()
{
    MyThingResponder responder;
    foo(responder);
    ...

But in the second test I wanted to verify it was called, so did something a little different

test2.cpp:

class MyThingResponder : public ThingResponder{
public: 
    std::vector<int> responces;
    virtual void respond(int responce)
    {
        responces.push.back(responce);
    }
};

void test2::someTest()
{
    MyThingResponder responder;
    foo(responder);
    ...

Can you guess what happened next.

I had already broken the one definition rule, this did not result in any build errors or warnings, instead when the calling frame wanted to allocate an object of MyThingResponder it used the local definition, but when it called the constructor it used the one from test2.cpp. This meant after I wrote test2, test1 started crashing in weird ways because the constructor for MyThingResponder was initialising a std::vector that wasn't there and screwing up a load of other local variables.

Had it been the other way around the responces vector would have never been initialised and it would have been even harder to track down the error.

These are the things that scared me.

  1. As we move to using more functors and the like, small locally defined classes with generic names will become more common.
  2. There is no way to guard against this. Anyone else's new class could break your code.
And these are the things I realised:
  1. I am going to put everything in a namespace from now on. Everything!
  2. I suppose I do kind-of have to know the obscure intricacies of the C++ standard after all.

Tuesday, 18 September 2012

Another pointless C vs C++ musing

I really wanted to agree with this blog post, I largely agreed with part1 and having once tried to integrate ZeroMQ with an application that was tied to an old version of gcc I really wished he'd written it in C too.

Ultimately I think it's a bit wrong-headed though. He seems to want to break encapsulation for a performance benefit (which is fine, so long you appreciate the trade-off and genuinely do need the speed) and for some reason thinks that's fine in C but not in C++. Personally I think ugly hard-to-maintain code is ugly and hard to maintain whatever language you write it in.

I have a pet theory that C will out-live C++. Managed languages will slowly intrude on the application space because the pain of developing in them is so much less, and the performance penalty will continue to get smaller until the one outweighs the other for all but a tiny subset of problems. Even the resource scarce embedded space, where you'd have thought C++ would have trounced, say, Java every time, seems to have been largely ceded, Android being prepared to take the hit of a JVM in return for stability and safety (how stable and safe is arguable, but certainly more stable and safe than running native code from unknown third parties).

Meanwhile C++, with horrors such as exceptions and (yuck!) template meta-programming, is never going to make much inroad into the system space. Operating systems, while they may employ more and more C++ components, are for the foreseeable future going to be written mostly in C. In fact it's clear from noises coming out of Microsoft that they'd like to do as much as possible in C# and are only stymied by performance issues; performance issues that will surely be resolved sooner or later, if only by faster hardware.

So to come full circle, one of the pain points of C++ that really irritates me, and does not seem to get talked about much, is people who insist on writing APIs in C++. In theory it's all nice, you simply compile everything up and off you go, and if they've used some later code feature then you update your compiler. In the real world it doesn't work that way. If I need to update my compiler I need to recompile about a dozen other open-source C++ libraries and go and find updated version of the two or three third party proprietary ones. All that functionality now needs to be re-tested, most of it by human beings, all at great cost.

For these reasons we rarely update compilers, and so the chances of a given C++ API not compiling on one of the (several) compilers we use is greatly increased. Greatly increasing the pain of using it.

On top of all that I can no longer dynamically load it at run time (or at least, not without jumping through more hoops than is worthwhile) which means it becomes an absolute dependency whether or not the user needs the functionality, and I either have to link it statically bloating the binary or leave myself vulnerable to version mismatches.

All of this makes me sad. I love C++; I like the philosophy behind it, believe that smart pointers trump any other form of garbage collection, that templates are great, and think that it is generally just unforgiving enough to force people into actually thinking about what their code is doing rather than muddle through by trial and error. But I think the lack of an ABI (is it even possible? I don't know) is becoming such a large pain point that it will eventually (though hopefully not in my professional lifetime) kill the language off.