Jump to content
43oh

Recommended Posts

I bet you would make little fuss about someone talking about using inline ASM right ? Same difference here, think about it.

Not really.  This isn't a holy war for me, it is just a recommendation for anyone who wants to work on a performance-minded software project: HPC, gaming, microcontrollers, etc.  C is the status quo right now for these things.  Maybe you know better -- not bait, an honest proposal.  Prove it by doing something amazing.

Link to post
Share on other sites
  • Replies 32
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

Point is, you're the only one arguing about what is best, and what is not.  My whole stance has been ( going back what now ? 2-3 months ). Be flexible, and use what makes sense.  Of course, if your ex

Again . . .    Being a bad programmer makes you a bad programmer. You and Linus can both agree all you two want, but that does not make either of you right.   C can not do function overloading . .

I think you'll find, at least with the "original" digital guys ( who are still around ) assembly is the real go to language. I find that most of these guys who truly know their stuff, know assembly ve

This is not a holy war for anyone then. If not for you. Personally I could care less what languages other people use. But I do think you're doing your self an injustice( not to mention C++ ) by stating that C++ is a poor language. It is not. If anything, your understanding of C++ is poor. <--- That's not bait either, but a fact. Either way,I feel as though in this case you're not being informative, but rather for a lack of a better term, spreading FUD, or misinformation. Sorry if that is harsh, but that is how I feel about it. In my mind, you obviously know very little about C++. Hell I do not know a tremendous amount about C++ either, but I do know a good thing when I see it.

 

Also I think you're missing a major point here too. x86 C++ != embedded C++. Several things are not even included, or if are included are not wise to use within embedded C++.

 

As for the proof, its on the forums already.

Link to post
Share on other sites

But I do think you're doing your self an injustice( not to mention C++ ) by stating that C++ is a poor language. It is not. If anything, your understanding of C++ is poor.

I have worked on quite a few C++ projects.  I don't like it, but the truth is that I don't really enjoy programming in general.  I do it because it is an inescapable part of building the things I like to build.  Personally, I prefer C.  I am guessing that anyone on this board who comes from an electrical engineering background as opposed to a software/IT background will feel the same way.  That is some useful advice, I expect.

 

Also some useful advice is just to know that C++ is seldom found in firmware projects, and it is even more seldom found in firmware projects where performance seriously matters.  Obviously, C++ can do all the same things as C, so the reasoning here is just status quo and convention.  But, these things are still important considerations.  If you want to communicate internationally, the best thing to do is learn english.  If you want to work on firmware, the best thing to do is learn C.

 

The internet-of-things is the newly emerging big-volume application for microcontroller firmware in the next decade, and pretty much none of it is using C++.  There is a ton of C.  Sometimes, you will see a C project that uses a sandbox VM for threaded-style user code, but this tends to be something higher level like Forth or node.js or whatnot.  So, if you want to play with micros professionally, take my advice and learn how to use C like a pro.

Link to post
Share on other sites

I think you'll find, at least with the "original" digital guys ( who are still around ) assembly is the real go to language. I find that most of these guys who truly know their stuff, know assembly very well, and possibly better than any other language. Of some of these, you will find those whom know C, and possibly C++ very well too. A few of these guys exist on these forums. . .

 

I will agree with mpymike, C++ can be complex, and sometimes convoluted. Plus just like any other language, on top of this complexity it requires a serious time investment to fully understand.  *This* is why I think many people do not use C++ in this field.  Added on top of everything else. It is not easy being good at programming and electronics engineering at the same time. Electronics engineering in of its self is such a huge subject, that it is broken down into many sub disciplines.

Link to post
Share on other sites

I have a Ba degree in EE, so I consider myself coming from an EE background.

I do like programming, and I like C. I do like some parts of C++ and am annoyed by other parts of C++. Just like I like some parts of ECMAscript and dislike some other parts of ECMAscript.

I dislike using assembler, because it tends to only work smootly on very small projects and is different on every controller (AVR/PIC/MSP430/ARM). I avoid using assembler and even allow for a tiny amount of inefficiency in my firmwares if it allows me to use C instead of assembler. This does not mean I try to avoid understanding it. To write good C code on the MSP430 means I need understanding of the architecture (which is the cause of the assembler). For example, I know I should avoid large shifts in time critical points because the MSP430 has no barrel shifter and hence only supports assembler shifting by 1 bit at a time. Also I avoid multiplications (by non-const values) as the MSP430 has no native multiplication built in.

C is in its origins a "portable assember", it's almost as efficient (even more than assember written by me, as I probably make choices which aren't the most efficient) as assember. C is very strict in its description of things, the way a stack is handled on a function call, the way variables are handled, the way structs are built, etc.

C++ on the other hand is more descriptive; the way things are implemented differs from implementation to implementation, version to version and even per compiler options! This means I cannot "new" an object whose implemetation is in a dynamically linked library (dll), because it might very well use a different description of objects or function calls. This means that when using dynamic linking I must fall back to a "plain C" interface to avoid screwing up my memory.

C++ is very strong on abstraction, which is good when designing software, but may hide overhead when writing deep low level code. That's why you will probably use C on the value line MSP430s rather than C++. On an ARM I'd probably prefer using C++. C++ allows me to write more complex applications in a cleaner way at the expense of some efficiency in binary code.

On the other hand, C++ allows for linking only those functions I need, while C can only link or not link a single unit (c file). There are non-standard extensions to fix this, but still.

C++ suffers from its C legacy and made some poor design choices (in my optinion), these choices cannot be undone to avoid breaking legacy code. For one thing I am disgusted by the template notation; < and > are used for comparison, they should never have been reused for template parameters (imo).

 

So, in my opinion, C++ is a good language, but has some defects. C is a simpler language which allows for more control, but also for less clean design. In devices where I need to worry about single bytes/words of memory, I prefer C over C++. I don't use assembler, unless I can't fix things in any other way (which hasn't happened to me in the last 8 years or so).

Link to post
Share on other sites

I agree that template notation is visually very ugly, and can make for very difficult reading / understanding of the code at times. However, with that said, templated classes seems to be one of the more useful features of C++ where embedded devices are concerned.

 

In fact, as I am learning more about the more advanced features of C++, so far templated classes are all that I personally use. Or more "correctly" ( at least from what I understand ) what some people refer to as meta-templates. My reasoning for using this feature is simple. I can write one implementation, and use multiple instances of similar object with little to no over head. Also the more that is known of these object instances at compile time( by the compiler), the more that can be handled at compile time vs, run time. Thus, making the resultant code more efficient, e.g. smaller, and faster.

 

Past all that, more of what I like ( and in fact am still learning a bit about ) is static polymorphism. The ability to write the implementation once for similar objects, then use as many instances of these objects as needed with little to no calling overhead seems very attractive to me. I think the most obvious usage on the MSP430G2 line would be GPIO. But if you had the need or want to keep related but not similar objects consolidated . . . how would you do that in C generically ?

 

Something along the lines of . . .

Thermocouple1.ChipSelect(LOW); // Composite GPIO / SPI / Thermocouple objects
Thermocouple1.Read();
Thermocouple1.ChipSelect(HIGH); // Composite GPIO / SPI / Thermocouple objects

 

Notation quibbles aside, this to me is very attractive to me, and can be done generically enough to where only one implementation needs be done per object type. Again, with little to no calling overhead. Also again, if much of the implementation is known by the compiler at compile time. The code generated will be smaller, and faster. How would it compare to equivalent C ? *That* is something to be determined on a user per user / implementation per implementation basis. YMMV

Link to post
Share on other sites

When using no inheritance (and thus no polymorphism or overloading) C is just as capable of doing "objects" as C++; just bundle a bunch of function references in a struct. Or just prefix them with the "class" name.

 

You're pointing out the bad things of using C++ in embedded systems; using templates will generate a copy of each method/function that depends on the template parameters! So in fact, the overhead can be huge. Templates save you maintenance on the code part, as you only need to alter one code base to alter much template instances (=classes). But the binary code might very well bloat as a result of this. On the other side, using C to do this would pass (for example) a "pin" paramter with each call, thus there is only one instance of the function, but with a little more logic in it to deduce the actual pin to toggle or use.

 

I've never heard of the concept of meta-templates in all those training sessions. I did hear about template meta-programming. This isn't a feature owned by C++, C can do meta-programming as well, but using function-like macros. The drawback (on the C part) is that you need to be more careful not to break things (template meta-programming can be made type-safe).

 

In my opinion, templates are just a tiny part of what makes C++ what it is. Using static polymorphism (which I know as inheritance) is an essential part of the idea of object oriented programming. Using C++ without using objects is just C with a little more bloat/fluff to it. It won't give you any new constructs, but it will hide some logic which you'd rather see on an embedded system.

 

Keeping related stuff together in C or C++ is similar; C uses units (c files) or prefixes (Serial_xxxx()) while C++ can use namespaces and objects in addition to these. A good unit isn't that much different from a good object (apart from the polymorphism). But the object hides some logic (allocation, memory management, etc.) from you that you might rather see on a low RAM embedded system.

Link to post
Share on other sites

meta-templates I guess is slang for pre-typed templates. Not to be confused with template specialization. Also not related to meta programming from what I've read. So something like . . .

 

template< class T>             //Generics

template <> <type param> // Specialization

template <type param>      // Meta-template. 

 

Anyway, it seems I misread your post there on the first go so let me apologize( sorry ), and start over.

 

First, I do not see how I am "talking about all the bad things in C++". C++ on the desktop is a completely different animal than that used for embedded projects. Mainly, because we're severely resource limited by comparison. So just like with any other language, we should only use what makes sense for the given application. We also need to pay attention to what we use, and how we use it. Again just like with any other language.

 

Second, templates are not objects. They do not necessarily add any overhead or bloat. Templates are a feature of the C++ to allow classes and functions to take generic parameters. A feature which also allows more code reuse in your project. With that said, it is often thought of as template abuse to use templates in a way which we've discussed many time on these forums. However, the code is legal, it works good, and in fact it works better than good in many cases. Also template objects are not only type-safe, they are very type-srtict. That has been my experience anyway, and at times it has given me no end of grief. That is, until i learned what I was really expected to do. 

 

Thirdly, on your discussion about C vs C++ header files. C as standard will pull in anything referenced such as header files, etc. While C++ on the other hand will only pull in what it needs to use. However, from what I understand C can be made to do this as well, but not as default like with C++.  One superb side effect of this, is that you can create a very large implementation of anything you like, and until your instantiated object actually calls those methods, those method implementations are not pulled into your code.

 

Anyway, the idea of the discussion, at least from my own perspective was to discuss the use of C++ in the context of the MSP430. Templates may only be a small portion of the language, but I really do not care about any feature of the C++ language that will not lend towards smaller, faster more efficient code. C++ can also do dynamic polymorphism, do you think that would be a wise thing to use on any system let alone a MSP430 ? Also C++ will add bloat or fluff( as you call it ) only if you let it. Something that is not exclusive to C++ only.

Link to post
Share on other sites

Let me try some examples:

Say I have a PWM template that takes as a template parameter the pin and port used.

 

template<typename port, typename pin>
class PWM{
...
};

PWM<P1IN, BIT3> MyPWM1;
PWM<P1IN, BIT4> MyPWM2;

This will not pull any functions into my code, but when I call MyPWM1(); (the function operator) this will call a different function than when I call MyPWM2();! Because the objects aren't of the same class; their classes are respectively PWM<P1IN, BIT3> and PWM<P1IN, BIT4>. These two classes are both template instantiations (which are also called classes) of the templatised class PWM.

This is even more of a problem when both are using a base class, since this will actually hide the template instantiation from us.

class PWM;

template<typename port, typename pin>
class PWM_pin : public PWM{
...
};

PWM MyPWM[] = {PWM_pin<P1IN, BIT3>, PWM_pin<P1IN, BIT4>};

Pardon my C++, I mainly work with C. I might very well have done some things above that are not C++, but I hope you get the idea.

Now I can iterate over MyPWM, but the elements aren't of the same class; just the same base class. In all the rest of my code, I won't be aware of the fact that for every templatised method of PWM I use, one method is pulled in for every instantiation of PWM_pin! So if this array contains 6 elements, I will probably be using 5 times 6 methods! (for each class a constructor, destructor, initialisation/enable, execute/advance and set method). So without explicitly writing it I just loaded thirty functions into my code! These functions might be very lean and mean, but they still all include a stack push, a stack pop and a return call. Summing up to at least 6 bytes each (6 * 30 = 90 bytes), even when doing nothing at all.

In addition, for each object a pointer is stored in RAM, I store a set PWM duty cycle, etc.

So for a "reasonable" use case, these 6 PWM drivers will use about 150 bytes of flash and 20 bytes of RAM. On the C side, I could fix this stuff with only 50 bytes of flash and 12 bytes of RAM. This might seem a small difference, but for each tamplate/class/object you use, this figure grows in a same rate. Just business logic (actually processing code) will be about the same in size (a multiplication in C is also a multiplication in C++, the size will be identical).

Link to post
Share on other sites

Well I would have to see your full code to know exactly whats going on( No I really do not feel like looking at it ), but I suspect the code may be less than optimal. I have noticed in some cases that when using C++, multiple objects can produce slightly larger code per instance vs multiple calls in C. Initially that is. I've been able to optimize-out most code overhead using C++ in most cases, by paying attention to what is going on in the code. In one case so far however, I have not. Eventually I'll either figure it out, or if not, and it becomes important. I'll just not use C++ in that case. Or in this specific case, there just would not be any code, as it is a C++ feature only( overloading the indirection operator so I can use it in a cout like syntax as done with std C++ ).

 

Anyway I have two UART text printing implementations right now. One in C, and the other in C++. The code is exactly the same, except where not possible. Well actually, there is additional code in my C++ implementation as noted above to overload the indirection operator. This feature as it stand actually adds overhead to the C++ implementation when I use it, versus when i do not. With that said, the C implementation uses 2 bytes more than twice size when compared to the C++ implementation on target.

 

C implementation :


msp430-gcc.exe   -c -g -Os -Wall -ffunction-sections -fdata-sections -mmcu=msp430g2553    -c main.c -o obj\Debug\main.o
msp430-gcc.exe  -o bin\Debug\test.elf obj\Debug\main.o   -Os -Wl,-gc-sections,-u,main -mmcu=msp430g2553  
Output size is 9.26 KB
Running target post-build steps
C:\HighTec\msp430\bin\msp430-size D:\projects\Ctest\bin\Debug\test.elf
   text	   data	    bss	    dec	    hex	filename
    418	      0	      2	    420	    1a4	D:\projects\Ctest\bin\Debug\test.elf
Process terminated with status 0 (0 minutes, 0 seconds)
0 errors, 0 warnings (0 minutes, 0 seconds)

 

C++ implementation:

-------------- Build: Debug in msp430 (compiler: GNU GCC Compiler for MSP430)---------------

msp430-g++.exe   -c -g -Os -Wall -ffunction-sections -fdata-sections -mmcu=msp430g2553    -c main.cpp -o obj\Debug\main.o
msp430-gcc.exe  -o bin\Debug\msp430.elf obj\Debug\main.o   -Os -Wl,-gc-sections,-u,main -mmcu=msp430g2553  
Output size is 8.72 KB
Running project post-build steps
C:\HighTec\msp430\bin\msp430-size D:\projects\msp430\bin\Debug\msp430.elf
   text	   data	    bss	    dec	    hex	filename
    216	      0	      2	    218	     da	D:\projects\msp430\bin\Debug\msp430.elf
Process terminated with status 0 (0 minutes, 0 seconds)
0 errors, 0 warnings (0 minutes, 0 seconds)

 

A quick couple of things to note however.  Types can play an important role sometimes. In the context of signed versus unsigned there can be a huge difference in what you chose. uint32_t versus int32_t can bloat the code size by around 100 bytes ! As I recall, this was the C implementation where i noted this, note sure if the same effect applies in C++ or not. Either way, I noted this, and used the "correct" type for minimal code size(on target). Also note, that I am using the same compiler / linker flags in both cases, and I use no code optimization in either. With CCS this may be a non issue, as I do know there can be some distinct differences between CCS, and mspgcc.

 

Also, another thing I did notice was that C uses minimal code space on target when calling the same functions multiple times. However, I believe this to be the same for C++ as well. I just have not done any comparison yet for this specific case. Initial code size without any instancing / calls aside, I do know that overloading the indirection operator in the case of C++ does take on more code space. Though, I do *not* have to use this feature if I so choose.

 

I may possibly do a video illustrating this code, but I really do not know. Because really in the end, what do I care what others think about  C vs C++. In my mind, it is like the "age old" discussion between using Linux vs Windows. Meaning, use what you like, and I'll use what I like. I would also like to do more experimentation to see if I can improve on either implementation.

Link to post
Share on other sites

Thanks yyrkoon, these comparisons are most useful !

 

I think the relative efficiency between C and C++ should be important to all microcontroller developers.

and size, performance, ease of development need to be the metrics. 

 

Some questions about the uart example:

1) This is a small example. I may be wrong about this, but the differences you see may have more to do with the

   different initialization functions for the different compilers or some other arbitrary compiler features.

  The overhead between the two might be different and have nothing to do with the C vs C++ discussion. 

  Would a larger example be better to reduce this type of difference?

2) In my mind what we are trying to determine, is the relative code efficiency between 

 a natural C implementation vs a natural C++ implementation. So both examples need to

 be written optimally using the respective C and a C++ approaches. Is this true for your

 example? You say that they the code for both is very similar.

Link to post
Share on other sites

... In my mind what we are trying to determine, is the relative code efficiency between 

 a natural C implementation vs a natural C++ implementation. So both examples need to

 be written optimally using the respective C and a C++ approaches.

A while back, I had grabbed some code that opossum posted to print values using the hardware UART and I created a comparable C++ version. I think he writes quality code so his code would be a good test of C vs C++. In this case, the C++ version turned out to be smaller. I'm using msp430-gcc 4.6.3 on linux. The C version was 578 bytes and the C++ was 528 bytes. With some minor c-runtime tweaking, I can get the C++ down to 494 bytes.

 

You can find the 2 examples here;

 

https://github.com/RickKimball/msp430_code/tree/master/fabooh/examples/serial/compare

 

Opossum told me that using CCS with optimization turned on, the code was 444 bytes. He is using CCS 4 so the fact that CCS 4 puts out COFF instead of EABI binaries probably accounts for some of the smaller size. If I supply my own low level init routines to override the default ones, I can get the C++ size down to 494 bytes. Neither version, C or C++, creates any global data or uses any BSS memory. Generally, it seems CCS does seem to generate slightly better code. But I would hope you get something for your $500 : )

 

These type of results aren't going to happen like this every time. However, I did want to take some decent straight C code and see how far apart a C++ version would be. I constantly use msp430-objdump to make sure I'm getting good code generation. If I don't, I look at what I'm doing, and adjust. If it turns out that using C for some parts of the code is the way to go, then that is what I do.

 

-rick

Link to post
Share on other sites

Thanks yyrkoon, these comparisons are most useful !

 

I think the relative efficiency between C and C++ should be important to all microcontroller developers.

and size, performance, ease of development need to be the metrics. 

 

Some questions about the uart example:

1) This is a small example. I may be wrong about this, but the differences you see may have more to do with the

   different initialization functions for the different compilers or some other arbitrary compiler features.

  The overhead between the two might be different and have nothing to do with the C vs C++ discussion. 

  Would a larger example be better to reduce this type of difference?

2) In my mind what we are trying to determine, is the relative code efficiency between 

 a natural C implementation vs a natural C++ implementation. So both examples need to

 be written optimally using the respective C and a C++ approaches. Is this true for your

 example? You say that they the code for both is very similar.

The code is exactly the same, except where it can not be. Function body code *is* exactly the same.

 

Let me see if I can make the different distinctions clear.

 

WIth the initilization routine, the differences is that the C++ implementation gets is parameters passed in via a template decoration. Matter of fact all the member functions in this class have template decorations.

template<uint32_t BUAD, uint32_t MCLK_HZ>  

 

where as with the C implementation, the function parameters get passed in as "standard"

uart_initialize(uint32_t BUAD, uint32_t MCLK_HZ) 

 

Passed that, the code is essentially the same, except the C++ implementation has more code for added "features" which actually bloats the code a small bit. And that the C++ version is an actual object, instead of similar routines grouped together in a single header file like the C implementation.

 

So yeah I am not sure exactly what is going on, but it almost seems as though G++ is more efficient at optimizing code, with code optimization flags not implicitly being set. Perhaps, some or all of that difference would go away with optimization enabled ?

 

Anyhow someone has just posted a rely and I am betting its Rick, lets see what he has to say.

Link to post
Share on other sites

 

 

These type of results aren't going to happen like this everytime. However, I did want to take some decent straight C code and see how far apart a C++ would be. I constantly use msp430-objdump to make sure I'm getting good code generation. If I don't, I look at what I'm doing, and adjust. If it turns out that using C for some parts of the code is the way to go, then that is what I do.

 

-rick

 

I agree 100%. C++ is not "everything but the kitchen sink" type language. Again only using it where it makes sense. Just like ASM, Except for me personally . . . my ASM skills are severely lacking. :(  I need to work on that sometime.

Link to post
Share on other sites

Given the ratio of FLASH vs. SRAM in MSP430, particularly the larger ones like the just announced MSP430G2955, it's also important to consider where the "bloat" occurs. It might be worth to sacrifice FLASH to save SRAM (and clock cycles).

 

In the UART example the template will integrate BUAD and MCLK_HZ as constants into the generated code. The C function call on the other hand will generate code to setup registers and/or stack to pass parameters and then the UART implementation will potential store them in a global structure for later reuse.

 

Multiple instance of the UART template will use more program memory, but the SRAM footprint should be little changed.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...