I wouldn't say it's necessarily safer - with so many pointers flying around; I think you have about as much, if not more, chance of doing Something Bad. The old joke about C++ being more difficult to shoot yourself in the foot, but blowing away your whole leg when you do, has some truth in it.
What you're saying about timing-critical is spot on. On the vehicle we had two boards, both using the same chip. The one that powered the in-dash LCD display was written using the C++ library, while the one that drives the motor was written using C and without any library.
It's hard to compare code size, since I don't have two similar pieces of code to compare, and it would be entirely dependant on how much you access the library. In my case, the code size was almost double, and ran about half the speed I would expect, this was mostly because it was fairly IO-heavy. But the main issue for me was that it's not particularly any more straightforward to write code for it, there's only one main noticeable difference, and that's being able to device drivers that were port-agnostic, for example you could have different instances of a function that accessed an I2C EEPROM chip, each with a pointer to their relevant I2C objects, such as this snippet from my library:
- Code: Select all
EEPROMc::EEPROMc(I2Cc * setPort, unsigned char setAddress) {
I2CPort = setPort;
deviceAddress = setAddress;
}
void EEPROMc::writeByte(unsigned short address, unsigned char data) {
unsigned int timeout = 0;
(*I2CPort).autoInit();
(*I2CPort).buffer[0] = deviceAddress;
(*I2CPort).buffer[1] = (address >> 8) & 0x7f; // ADDR high
(*I2CPort).buffer[2] = address & 0xff; // ADDR low
(*I2CPort).buffer[3] = data; // Data
(*I2CPort).engine((*I2CPort).buffer, 4, 0, 0);
// Poll until internal write cycle clears
(*I2CPort).setState(I2C_NACK);
while(((*I2CPort).getState() == I2C_NACK) && (timeout++ < I2C_TIMEOUT)) {
(*I2CPort).buffer[0] = deviceAddress;
(*I2CPort).engine((*I2CPort).buffer, 1, 0, 0);
}
}
So now all a user has to do to create an EEPROM object is to pass a pointer to the relevant I2C port instance, and the EEPROM's I2C address:
- Code: Select all
EEPROMc EEPROM(&I2C1, 0xa0);
Doing this is far neater than if you were to achieve the same thing using C function pointers (it's possible to do the same in C, but your namespace gets really messy very quickly). There are also some caveats: you'll have to throw around some 'extern "C"'s around to deal with the IRQ Handlers within the library, I haven't figured out a way to do this in C++ yet.
I've not come across any microcontroller application that needs to deal with objects, other than it being a neater way of storing information (and most of the time, a few structs would do the same job). So the application code itself does not necessarily benefit from being in C++.
On the balance, from this experience, I don't feel the benefits of C++ for microcontrollers are great enough to justify the lower speed and bigger code size. When you start entering the applications processor/SOC territory, that becomes a different story.
The compilers I have used for this was Keil's ARM MDK, and also YAGARTO (based on GCC), the difference between the output of the two in terms of size and speed appeared to be negligible, Keil perhaps being slightly smaller and faster though I have not done any objective tests.