Most modern compilers can optimize tail-recursion, so ideally any recursive algorithms on a resource-constrained processor should admit that optimization.
And make sure the compiler is actually doing it!
Most modern compilers can optimize tail-recursion, so ideally any recursive algorithms on a resource-constrained processor should admit that optimization.
And make sure the compiler is actually doing it!
I don't agree at all. C++ can be used to wrap up hardware into abstractions that are much more effective than the C versions. The Arduino API was a hugely wasted opportunity to do this.
For example, if you want to present an API to a serial port, but implement it on a device that may or may not have a UART, you can create a bit-banging software UART that has an identical external interface to the hardware one.
If you want to treat 3 bits in an 8-bit I/O port as if it was a dedicated 3-bit port, that's trivial - all the required shifting and masking can be managed inside a friendly API; and unlike in C, such things can be generated by a template class (which can also check - at compile time - that you haven't accidentally used those same port pins for some other purpose). This just isn't possible with C.
These problems are often *worse* with smaller processors.
Or to write re-usable software modules for highly variable hardware, like almost any 8-bit MCU.
The biggest problem is with the design of re-usable classes. They need to be arranged not to drag in big chunks of functionality that aren't always needed, and not to duplicate big chunks in template expansion. That's all achievable, but the designers of such re-usable elements need more than average skills.
Yes.
Clifford Heath.
Yes, another point is that the added complexity doesn't intrinsically lead to harder-to-debug, obfuscated code; applied appropriately it can actually help you to write _safer_ code than you could in asm or straight C.
It is worth appreciating that even the "language gurus" designing templates into C++ didn't understand what they had created. In particular they didn't believe the *discovery* that the template language was itself Turing-complete. (Personally I prefer my languages to be invented, not discovered!)
They finally got the message when a contributor wrote a short program which tied the *compiler* in a infinite loop. But not just any infinite loop; during *compilation* the compiler emitted the sequence of prime numbers!
Now if the language designers didn't understand their own creation, what chance have mere mortals go?
At that point I decided that if C++ was the answer, I didn't want to know what the question was!
The language is a big problem:
How /did/ they resolve whether it should be mandatory or impossible to "cast away constness"; I remember that debate lasting at least a year.
I have written quite a few programs in C for embedded processors, and have always stayed clear of C++, since I like to be closer to the upcode
But, I see the points about C++. Any hints to where I would get a good introduction to C++ for embedded, on the web or in a book?
Cheers
Klaus
I think the when is more important. The first time I looked (granted, in DigiKey), there were no sub-$1 processors at all, even at 1000ea (and granted, that's not "in volume"). The next year there was one or two. Now -- god knows, because I've blinked.
Take a look at the GigaDevice STM32 clone:
They are really pushing the envelope. Down to 20 cents in volume. But, one should be a little ashamed if going down that route, since they just copied the STM32 core AND peripheral, even possible to use the same DevTools.
Cheers
Klaus
It just changes the nature of the "expertise" that is required. SNOBOL is a bitch -- to someone who's not *fluent* in SNOBOL!
Etc.
How do you effect a "call to "? (i.e., RESET the CPU)
Higher level languages (increasing degrees of abstraction) are a blessing and a curse. If the world was full of folks "well-suited to their jobs", all of it would be moot.
The problem comes when you mix different skill levels of practitioners in a project (concurrently or sequentially). There's an "impedance mismatch" in their brainpans that usually spills over into the product's "quality" (reliability, etc.).
I built a "device" (in the sense of /dev/) that allowed you to "play melodies" by pushing streams of characters to the device. So, you could send a time signature to the device, a key and a stream of "notes" and the "device" would emit sounds corresponding to each note, etc. This made transcribing "music" into "code" almost trivial -- assuming the developer could "read music".
To skimp on resources, I implemented many of the "control structures" that you find in music (e.g., /D S al Coda/) as hacks. My thinking: "Hey, if the guy can read music AND write code AND understand what I'm doing, here, these hacks should be really OBVIOUS! Why waste a runtime resource on something that the developer can optimize IN HIS HEAD?!"
Wrong.
And, as they're (effectively) printf() arguments, not easy to detect problems at compile time (unless you wrote a tool JUST for that purpose).
So, I had to revisit the codebase to more literally implement these "control structures" so, literally, a MUSICIAN could do the transcribing without relying on the "programmer" to "code" the melody. The developer would have been more effective at implementing his changes correctly had he been forced to write "real code" (instead of this "application specific language")
People tend to think things are simpler than they actually are and end up getting bit when those assumptions prove wrong.
OTOH, crippling *everyone* because SOME folks are inept is equally unfair (to developers AND projects!)
I'd probably experiment with using it to write something for the PC before jumping right into it on embedded platforms. Not everything is directly translatable.
But there's no point in studying any reference that isn't teaching you C++11/14/17 idioms. In many ways it's essentially a new language after the C++11 standard, and if you follow the guidelines for writing "idiomatic" C++11 you can avoid many of the pitfalls that people griped about in the past.
For example "smart pointers" are part of the standard library now; one of the guidelines for writing "modern C++" for platforms with heap memory management where you're dynamically instantiating objects that manage memory resources is you should ideally never see a "naked new" or "delete" (the equivalent of malloc/free), unless you're writing a custom memory allocator or something. All resource-managing objects should be wrapped up in smart pointers, which manage their own lifetime.
For example if you have some class:
class Blah { //ctr Blah() { // lots of memory is acquired here in constructor } ... };
And you need to use it in local scope in a function, you would do:
void my_func() { auto blah = std::unique_ptr(new Blah()); //auto keyword automatically infers type //etc }
Then you don't have to worry about memory leaking when "blah" goes out of scope at function termination. A common error with C is returning a pointer to a local variable from a function:
char* bad() { char x = 1; return &x; }
But in "modern" C++ you can have functions that return smart pointers by value:
std::unique_ptr good() { return std::unqiue_ptr(new char{1}); }
and functions can take smart pointers by value to represent ownership exchange, or they can have regular const char* arguments that you can pass a smart pointer to by value, and the smart pointer wrapper class will employ its "user-defined conversion" operator to morph into a regular pointer, if the function only needs read-only access.
More applicable to the embedded world are STL classes like std::string and std::vector, which you can use in place of plain old arrays of characters and numeric types. A vector is a dynamic array that can be resized, searched, and appended/popped on-the-fly.
They're nice even on platforms that have small amounts of memory/no memory management, because you can supply their constructor with a custom memory allocator, whatever you want, it doesn't have to be new/malloc. You could allocate a pool of memory at system startup for your vectors, and then just use them however you want, resizing/pushing/popping, letting them go out of scope, so long as you don't try to exceed the size of the pool. The class will use whatever custom software pool allocator you supply to handle it all automatically.
This is probably a good lecture to start with:
Big points to keep in mind:
Yep, in a couple years you'll basically have the equivalent of a desktop PC from 1992 the size of your thumbnail for a couple bucks.
No reason to still be futzing around with ASM and dumpy old straight C when they can do so much more if you just give 'em a chance ;-)
The regular ol' C99 preprocessor is apparently Turing-complete given some caveats; it apparently took until 2012 and this guy who got 100 "likes" for outlining it:
And we know why ... design is a process of apply higher level abstractions to recalcitrant hardware, and John Larkin doesn't do that,
Nothing new -- though looping had to be done from the command line back in the day:
If you feel kind of murky about the capabilities of C, I suggest spending a lot of time on ioccc.org. :-) (Hopefully, you'll come out feeling, not "kind of", but supremely murky? :D )
Tim
But, I want to design my display screen and have every name and variable display in exactly the right place, even if I might get confused about the number of digits. Sticking to straight C makes this easy to do. If I was displaying a terminal stream output, how to set the starting location and maximum number of chars allowed for the field?
I've been coding in c++ for 25 years now, and I hate the iostreams library and all it stands for. ;)
It's okay for writing text to the console, but its object-orientedness is a n illusion--if you try doing anything the least bit fancy, you find yoursel f lost down the dim corridors of , wishing for the clear light of . ;)
Cheers
Phil Hobbs
+1. I never used in in anger, in 20 years of professionally using C++ (1987-2007).
Good to hear, we're just in process of transitioning from 128k AVR to
128-512k Cortex devices. We've got plenty of reusable code, but especially communications and sensing functions may vary from product to product.We're using Cpputest for unit testing and plan on at least some level of hardware in the loop testing for all the code that touches hardware.
A big concern indeed is the addition of functionality. We could easily have ti 512/64k part on board, but how to keep the flash from filling up ? With the current AVR we've done some code squeezing every now and then to keep the test/debug image size within 128k.
Personally I'm not too worried about that contest; it is possible to write obfuscated code in any language.
In particular, having "dangerous" things /designed/ into a language like C seems reasonable and inevitable.
Having them /discovered/ by is a separate issue!
I am more concerned about the number of pitfalls an average developer is likely to /unwittingly/ encounter when writing "normal" code.
STM32F301K6 is ~$1.
32K flash, 16K(!) RAM. The K8 is only 10% more and 64K flash.We are there. :)
(Nice chip by the way. I was pleasantly surprised by the ADC. Very low transition noise which is important in my application).
Have something to add? Share your thoughts — no account required.
Ask the community — no account required