EEVblog Electronics Community Forum

Products => Computers => Programming => Topic started by: thelearner on December 12, 2020, 04:54:24 am

Title: Best book for learning embedded c
Post by: thelearner on December 12, 2020, 04:54:24 am
Hello,

I am an absolute beginner to embedded programming. Please suggest books and a learning path for me to learn embedded c programming.

TIA
Sri
Title: Re: Best book for learning embedded c
Post by: Doctorandus_P on December 13, 2020, 03:27:45 pm
Te best way to lean C is with a PC and a decent IDE and debugger.

The good old Kerninghan & Ritchie (Ansi Edition) is probably still one of the best paper books, but in these times there are many good websites and online tutorials. My advise is to try some out as you're going, and see which fits your learning style the best.

I prefer to set my own goals and parts I want to learn. Other people want others to give them a structured approach. If you're in that last category, then some online course may be a good choice.

First learning programming on a PC is a lot easier then starting with a microcontroller immediately. On the other hand, although starting with writing small PC programs is more effective, when you start with a microcontroller and blink some LED's and more it may help to keep the motivation up.

Once you've mastered the basics of a computer language you've just started. There are also many books about approaches to solving problems with a programming language and designing of algorithms.
Title: Re: Best book for learning embedded c
Post by: Kjelt on December 13, 2020, 10:27:11 pm
K&R is still valid but dated.
A newer book:
C Programming: A Modern Approach, 2nd Edition 2nd Edition by K. N. King  (Author)

Then learning C is like learning to drive a car, if you don't have colleagues or mentor that will give feedback on your coding you can write code that works but is totally unreadable crap.
Variable naming and other coding guidelines such as correct layout, splitting code in functions, state machines coding,  etc etc there is a lot to learn.

Then you know C but still not embedded C. A good embedded programmer also knows the hardware on which it is programming. This entails the microcontroller and its peripherals or the board and OS for bigger systems. Also a good programmer understands the rest of the hardware or even machine which is running his code or in bigger systems at least the HSI and what effects it has.

So welcome, start the journey, don't be afraid and good luck.
Title: Re: Best book for learning embedded c
Post by: rstofer on December 16, 2020, 10:44:06 pm
One reason I keep my copy of K&R's "The C Programming Language" at hand is the functions provided.  Things like itoa() are useful and the examples in the book do not use the heap.  I don't want to use the standard library functions provided with GCC because many rely on my having a heap and heaps just aren't a good idea for embedded programming on small memory chips.  If you have to provide code for sbrk(), you're creating a heap (and it's obvious) so when the linker says sbrk() is undefined because you haven't written it, it's time to see what other functions are using the heap.

When I do a uC project, the first thing I do is bring up the UART.  Then I add in all the conversion functions like itoa() or perhaps some conversion to hexadecimal display of 32, 16 or 8 bits.  I copy some string functions and build up a puts function.  Once I can display debug messages with numeric values, I'm ready to start on the project.

Look at all the functions in K&R and reflect on how they could be useful for embedded programming (or any other kind of C programming).  Remember, C was invented to write Unix.  It is an excellent language for systems programming.
Title: Re: Best book for learning embedded c
Post by: Fixpoint on December 17, 2020, 10:30:26 am
You should definitely use a MODERN book. The old stuff from 70s and 80s often contains "hacker's advice", that means, they teach you things that are plain wrong and sometimes even dangerous (from a functional safety perspective). Later you have to "unlearn" all the bad advice when you are working in real-world projects (for example, when you need to apply MISRA rules).

Soon, we have the year 2021. Today, you must learn how to *design* software, not only how to *program* it. The old literature doesn't teach you that at all.

(Simple examples of bad advice is when the authors teach you to use C functions that were state-of-the-art back in the 70s but that are unsafe and long deprecated today. Or oftentimnes they basically don't understand anything about type theory and teach you to use the unsafe "long" or "int" instead of the safe versions (like uint32_t, uint8_t etc).

Horrible software errors result from these kind of books.
Title: Re: Best book for learning embedded c
Post by: rstofer on December 17, 2020, 11:47:08 pm
K&R is still valid but dated.
A newer book:
C Programming: A Modern Approach, 2nd Edition 2nd Edition by K. N. King  (Author)

Alibris has the book along with a couple of different study guides:

https://www.alibris.com/booksearch?mtype=B&keyword=c+programming+a+modern+approach (https://www.alibris.com/booksearch?mtype=B&keyword=c+programming+a+modern+approach)

So, I bought the pair as a $20 experiment.  We'll see...

Title: Re: Best book for learning embedded c
Post by: Doctorandus_P on December 19, 2020, 05:00:59 pm
I had a quick peed at "C programming, a modern approach", and  don't like it much, it also teaches you bad habits.

The example below gives a very bad example of commenting functions.
It starts with the decoration of all the stars around the comment. Some IDE's have baked in functions for that, but manual maintaning those is a big bore, which makes your code less portable, but more important: Literally stating in the comment what a function does is not only useless (just read the function) it is even harmful. You need time to write, read and maintain such comments, and they go out of sync with the sourcecode all the time. Which makes them misleading, or at least you can't trust them, and you have to read the code anyway. Just yuck.

I saw some part about stating the length of integers, long ints and such, but not the uint16_t standardized typedefs.

Quite some notes about multiline macro's and those are indeed a part of the C syntax, but bad practice.

The book seems a bit long winded to me, but maybe others call it thorough.

Today, you must learn how to *design* software, not only how to *program* it. The old literature doesn't teach you that at all.

Amen to that. Learning a programming language and learning to program are two separate subjects.
Title: Re: Best book for learning embedded c
Post by: Kjelt on December 19, 2020, 06:32:58 pm
The example below gives a very bad example of commenting functions.
Literally stating in the comment what a function does is not only useless (just read the function) it is even harmful.
You need time to write, read and maintain such comments, and they go out of sync with the sourcecode all the time.
Which makes them misleading, or at least you can't trust them, and you have to read the code anyway. Just yuck.

I totally disagree.
Every programmer will agree that the paper (pdf/docx etc) documentation is never in sync with the code.
The code is the truth. So if you change the code in such a way that you change the behavior or input/output parameters or return type/value,
you better change the comments in the code otherwise find another job since you're not a professional programmer. Besides no code review team will allow this to be committed.

I worked at a company that hadn't updated the documentation in over 12 years.
Luckily they did use coding guidelines that had some sort of Doxygen type of headers for functions,
describing the use of the function, all the input and output parameters and return value if applicable.

Now you can discuss about when, because when you have a coding guideline that states the Uncle Bob law,
that functions should never exceed 50 loc, you might have a point.
But in practice you will have functions with over 1000 locs and without some brief explanation you can draw the wrong conclusions.
Besides if you have (such as my work) over 34000 functions in your 18Mloc stack you don't want to read all the functions in detail. The description should be sufficient to know it could be a valid or invalid function for that purpose and when it is valid then you read the code to verify.

So I don't know you're background or current employment but I doubt that you are an embedded software programmer.
Title: Re: Best book for learning embedded c
Post by: RoGeorge on December 19, 2020, 09:39:24 pm
Modern Embedded Systems Programming Course
https://www.youtube.com/playlist?list=PLPW8O6W-1chwyTzI3BHwBLbGQoPFxPAPM (https://www.youtube.com/playlist?list=PLPW8O6W-1chwyTzI3BHwBLbGQoPFxPAPM)
Title: Re: Best book for learning embedded c
Post by: Doctorandus_P on December 19, 2020, 09:49:04 pm
I worked at a company that hadn't updated the documentation in over 12 years.
Luckily they did use coding guidelines that had some sort of Doxygen type of headers for functions,
describing the use of the function, all the input and output parameters and return value if applicable.
But the example I showed does neither. It does not describe how the function is used, It does not say anything about it's parameters. It is just a direct transliteration of the code, and that is useless. I have seen code like:


 /* Add 4 and 5 and assign it to "a". */

  a= 4 + 5;


and one line out of 4 with actual code is just plain horrible.

Good comments do not state the same as what the code does, but they tell something about the way a piece of code or a variable is used. Give some backround or add some context.

Indeed something like:
The description should be sufficient to know it could be a valid or invalid function for that purpose and when it is valid then you read the code to verify.

So I don't know you're background or current employment but I doubt that you are an embedded software programmer.
Some 10+ year ago my then employer used the Nucleus RTOS. That code was completely filled with the "add two number" type of comment.

My best guess about your last remark is that you have not realized how bad that comment is.
Title: Re: Best book for learning embedded c
Post by: SiliconWizard on December 19, 2020, 10:02:28 pm
The example below gives a very bad example of commenting functions.
It starts with the decoration of all the stars around the comment. Some IDE's have baked in functions for that, but manual maintaning those is a big bore, which makes your code less portable, but more important: Literally stating in the comment what a function does is not only useless (just read the function) it is even harmful. You need time to write, read and maintain such comments, and they go out of sync with the sourcecode all the time. Which makes them misleading, or at least you can't trust them, and you have to read the code anyway. Just yuck.

I agree. Stating the obvious in a comment is NEVER a good idea, and in this ugly example, I agree 100% with you. Comments should either be used as "titles" - to give a title to a function, or a block of code - or for documenting an algorithm or an approach that is NOT obvious, is the result of non-trivial thinking, or modifications due to particular bugs or conditions, etc. As you said, too detailed comments are a nightmare to keep up to date, and don't bring much to the table. Now if a comment needs to be detailed, as in the cases I talked about, that's fine: in this case, they detail a non-trivial part of the code; and if you happen to later modify a non-trivial part of the code, you better pay particular attention to that, and usually rewrite the corresponding comments if any.

I saw some part about stating the length of integers, long ints and such, but not the uint16_t standardized typedefs.

Any book that is more recent than 20 years old should definitely talk about stdint.h. This is especially important for embedded developers IMHO, but even for "non-embedded" development, we were much too often using platform- and often compiler-dependent tricks to make sure given integers were of a given fixed width in a large number of situations. That was a really bad situation.

Quite some notes about multiline macro's and those are indeed a part of the C syntax, but bad practice.

I for one am not against macros, when they make sense. Even if they have to be multi-line. So I can't say I agree with you on this. Macros are not without potential pitfalls, but everytime they can save the programmer from writing duplicate code - which is a much more severe "sin" IMHO - and that can't be solved with a function, they are better than not using them. Just learn how to. Just my 2 cents though.

Today, you must learn how to *design* software, not only how to *program* it. The old literature doesn't teach you that at all.
Amen to that. Learning a programming language and learning to program are two separate subjects.

Agree with that.
Title: Re: Best book for learning embedded c
Post by: Kjelt on December 19, 2020, 10:29:21 pm
But the example I showed does neither. It does not describe how the function is used, It does not say anything about it's parameters. It is just a direct transliteration of the code, and that is useless.
Agree, I think it is just an example to show and teach to write comments.
They could have used better comments as example.
But to dismiss the book on that grounds....
I also grew up with K&R but as course material it is outdated.
If you have a better modern book I am all ears.

Quote
My best guess about your last remark is that you have not realized how bad that comment is.
Always difficult to communicate over forums, but had enough of 1 years arduino guys that think to know it all and show the worst ugly code you ever seen. Sorry but I read in your comments that "to write comments what a function does is useless" which probably is not what you meant. BTW personally I like the
/************************************
*
************************************/
format, the reason being that not all companies have the latest and greatest ide or fancy auto highlight editors. I am currently forced to work a lot with nedit, not because I want to but because I don't have a choice (thank god they have nedit and not only vi)  :-DD and with simple text editors the above format makes me find the end or start of the next function a lot quicker. And with files over 15000 locs  |O
Title: Re: Best book for learning embedded c
Post by: PlainName on December 20, 2020, 03:44:35 pm
Quote
Literally stating in the comment what a function does is not only useless (just read the function) it is even harmful.

Disagree. We'll leave aside here out-of-date comments which are still useful (they tell me what the programmer was thinking at that time), and just go with the headline "What this function does" thing.

So, what if that function wasn't just three calls but did a fair amount of dicking around? 'Just read the code' wouldn't be appropriate to figure out quickly what this is about, so in that case the succinct function comment would be reasonably useful. trouble is, how do you know that this function is one you 'just read the code' and that function is one where you're better just getting the summary? Oh, you have to read 'em first...

No, if you're scanning function comments it doesn't matter how simple or obvious or obtuse the actual code is - you're scanning the comments. Thus all comments are useful even if the code is simple enough to essentially repeat them in that instance. The important thing is consistency, and if most of the functions have summary comments then they ALL should have, regardless.
Title: Re: Best book for learning embedded c
Post by: Nominal Animal on December 21, 2020, 06:45:23 am
In my opinion, a comment that describes what a function does are worse than useless, but comments that describe why or what the code is trying to achieve are excellent and praiseworthy.

Consider the following C function:
Code: [Select]
int linear(double x0, double x1, long count,
           int (*func)(double x, void *data), void *data)
{
    if (count < 1 || !func)
        return -EINVAL;
    if (count == 1)
        return func((double)(0.5*x0) + (double)(0.5*x1), data);

    --count;

    for (long i = 0; i <= count; i++) {
        const double  x = ( (double)((count - i) * x0) + (double)(i * x1) ) / count;
        const int  err = func(x, data);
        if (err)
            return err;
    }

    return 0;
}

Here is the same code, with good why comments added:
Code: [Select]
int linear(double x0, double x1, long count,
           int (*func)(double x, void *data), void *data)
{
    /* Count must be positive and func non-NULL for this function to work. */
    if (count < 1 || !func)
        return -EINVAL;

    /* If only one call is done, use the midpoint between x0 and x1. */
    if (count == 1) {
        /* Be careful, and avoid domain cancellation error if x0 and x1 differ a lot in magnitude. */
        return func((double)(0.5*x0) + (double)(0.5*x1), data);
    }

    /* We'll use (count) instead of (count - 1) from now on in this function. */
    --count;

    for (long i = 0; i <= count; i++) {

        /* Avoid domain cancellation error when x0 and x1 differ a lot in magnitude. */
        const double  x = ( (double)((count - i) * x0) + (double)(i * x1) ) / count;

        /* We need to abort if the callback function tells us to. */
        const int  err = func(x, data);
        if (err)
            return err;
    }

    return 0;
}

Here is the same code with good what comments instead:
Code: [Select]
/* Call callback function 'count' times, linearly distributed
   between x0 and x1.  Aborts and returns the callback function
   return value if it returns nonzero.  Returns zero when successful. */
int linear(double x0, double x1, long count,
           int (*func)(double x, void *data), void *data)
{
    /* Abort if count is not positive, or func is not a valid function pointer. */
    if (count < 1 || !func)
        return -EINVAL;

    /* The loop later on will not handle the count==1 case right,
       so we need to handle that here. */
    if (count == 1) {
        /* Cast to double, to avoid domain cancellation error when x0 and x1
            are of wildly different magnitudes; otherwise the larger one in magnitude
            would just be halved, and the smaller one ignored. */
        return func((double)(0.5*x0) + (double)(0.5*x1), data);
    }

    /* Since (count-1) occurs often in the rest of this function,
       we decrement it by one so we can use (count) instead. */
    --count;

    /* Iterate i from 0 to count, inclusive, so we have 'count+1' iterations
       of the loop body.  Note, 'count+1' is the original 'count' amount,
       as we decremented 'count' before. */
    for (long i = 0; i <= count; i++) {
        /* x = (1.0 - i/count) * x0 + (i/count) * x1.
           When |x0| >> |x1| or |x1| >> |x0|, we don't want the larger magnitude one
           to define x (that's domain cancellation error), so we're careful we multiply
           x0 and x1 by the weight before adding them together. */
        const double  x = ( (double)((count - i) * x0) + (double)(i * x1) ) / count;

        /* If the callback function returns nonzero, we need to abort the loop and return that value immediately. */
        const int  err = func(x, data);
        if (err)
            return err;
    }

    return 0;
}

And, for comparison, here are the sort of useless "what" comments that I detest, mostly because they tend to be my own first instinct too, because when I learned to program, I didn't understand how important good comments are, and have had to try and re-train myself to write better comments – something that is obviously still a work in progress:
Code: [Select]
int linear(double x0, double x1, long count,
           int (*func)(double x, void *data), void *data)
{
    /* Sanity checks. */
    if (count < 1 || !func)
        return -EINVAL;

    if (count == 1)
        return func((double)(0.5*x0) + (double)(0.5*x1), data);

    --count;

    for (long i = 0; i <= count; i++) {

        /* Calculate x. */
        const double  x = ( (double)((count - i) * x0) + (double)(i * x1) ) / count;

        /* Call callback function. */
        const int  err = func(x, data);
        if (err)
            return err;
    }

    /* Success! */
    return 0;
}
I'm particularly ashamed of the final one, the /* Success! */ comment: I like it, even when I know it is useless, and probably irks some other developers to no end.  Sorry.
Title: Re: Best book for learning embedded c
Post by: PlainName on December 21, 2020, 07:16:45 am
Quote
int linear(double x0, double x1, long count,
           int (*func)(double x, void *data), void *data)

What does it do? I'm not going to try and make sense of some potentially obfuscated code, and my IDE has collapsed everything so I just see function names and the function comment. (Or else I'm browsing a doxygen compilation, or javadoc - there are plenty of documentors around.)

Honestly, expecting someone to decipher the entire codebase just to find out what this stuff does from a 200ft view is taking the piss If you can't write good comments, in English, no less, what makes you think you can write code in some weirdo foreign language? Or that the reader will understand your dialect?
Title: Re: Best book for learning embedded c
Post by: nfmax on December 21, 2020, 08:31:11 am
The function header comment should document the contract of the function: what it does, what input it expects, what it returns, what side effects it has, and what error conditions may occur. The bounds of valid input and the range of non-error output should be stated, where these are not equivalent to those set by the parameter and function types. Where applicable, the algorithm used should be described, or a reference provided to a published description.
Title: Re: Best book for learning embedded c
Post by: Nominal Animal on December 21, 2020, 08:35:59 am
Quote
int linear(double x0, double x1, long count,
           int (*func)(double x, void *data), void *data)
What does it do?
Code: [Select]
/* Call callback function 'count' times, linearly distributed
   between x0 and x1.  Aborts and returns the callback function
   return value if it returns nonzero.  Returns zero when successful. */
Although I see I missed "with the argument" after the first comma.

If my English fails me, linear(1.0, 2.0, 3, foo, NULL) calls foo(1.0, NULL), foo(1.5, NULL), and foo(2.0, NULL), and returns zero; unless one of the foo() calls returns nonzero, in which case the function returns immediately with that return value.

I don't really know what to call it.  Linear interpolator function caller?  Somesuch.  Contrived example.

I do recommend Doxygen comments to describe each function, but many developers believe they alone suffice, so I decided to not do that for this example.

Honestly, expecting someone to decipher the entire codebase just to find out what this stuff does from a 200ft view is taking the piss If you can't write good comments, in English, no less, what makes you think you can write code in some weirdo foreign language? Or that the reader will understand your dialect?
Fuck you too.
Title: Re: Best book for learning embedded c
Post by: Nominal Animal on December 21, 2020, 08:45:51 am
The function header comment should document the contract of the function: what it does, what input it expects, what it returns, what side effects it has, and what error conditions may occur. The bounds of valid input and the range of non-error output should be stated, where these are not equivalent to those set by the parameter and function types. Where applicable, the algorithm used should be described, or a reference provided to a published description.
For library code, I disagree.  Aside from the algorithm and references to the theory behind the implementation (which should be included as a comment), everything else belongs to the man page documenting that interface function.  If you don't have man pages, you're doing things wrong anyway.

At the object level (compilation units, separate files that are part of the same project), I do agree.

(I do not recall ever seeing satisfactory documentation based on Doxygen, except possibly Doxygen documentation itself.  A typical rendered example (http://fnch.users.sourceforge.net/data/doxygen_c/html/index.html) contains the Files and Classes pages, which one can navigate to find the formatted documentation extracted from the comments.  I always find I can find+grep+less the relevant code comments faster than I can navigate the web interface generated from them.)
Title: Re: Best book for learning embedded c
Post by: PlainName on December 21, 2020, 09:11:45 am
Quote
I do not recall ever seeing satisfactory documentation based on Doxygen

I've seen some, but can't recall what they were now. But a typical one I'd agree is generally pants, mostly because of the lack of comments to document (presumably because either the code describes it perfectly or there is no need to write comments because doxygen).
Title: Re: Best book for learning embedded c
Post by: nfmax on December 21, 2020, 10:21:15 am
The function header comment should document the contract of the function: what it does, what input it expects, what it returns, what side effects it has, and what error conditions may occur. The bounds of valid input and the range of non-error output should be stated, where these are not equivalent to those set by the parameter and function types. Where applicable, the algorithm used should be described, or a reference provided to a published description.
For library code, I disagree.  Aside from the algorithm and references to the theory behind the implementation (which should be included as a comment), everything else belongs to the man page documenting that interface function.  If you don't have man pages, you're doing things wrong anyway.

At the object level (compilation units, separate files that are part of the same project), I do agree.


Sorry I should have made that clear: the function header comment is information for the software maintainer, not the end user, who may not have access to the source code at all. Library documentation - the user manual/reference manual - is another thing altogether
Title: Re: Best book for learning embedded c
Post by: Nominal Animal on December 21, 2020, 10:25:51 am
(dunkemhigh, if it was unclear, I really did not understand your point in the latter part that I quoted; it seemed to me to be just blah blah about how a non-English speaker like myself shouldn't talk about comments or write any code.)

Here is the same function I talked about above, commented in a way I myself would like to have commented it:
Code: [Select]
/**
 * Call a callback function with a linearly progressing argument
 *
 * This function linearly interpolates between two real numbers,
 * and calls the specified callback function for each value,
 * for the specified number of calls.  If the callback function returns nonzero,
 * iteration is aborted, and the function returns the callback return value.
 * If only one call is requested, it is done at the midpoint.
 *
 * @param x0     Initial value of the argument
 * @param x1     Final value of the argument
 * @param count  Number of calls (including initial and final calls
 * @param func   Callback function
 * @param data   User pointer provided to the callback function as-is
 * @return       Zero if success, callback function nonzero return value,
 *               or -EINVAL if count < 1 or func is NULL.
*/
int linear(double x0, double x1, long count,
           int (*func)(double x, void *data), void *data)
{
    if (count < 1 || !func)
        return -EINVAL;

    /* Interpolating between x0 and x1 can lead to domain cancellation error,
       if the two values differ a lot in magnitude, because a + b == a for |a| >> |b|
       using double a, b.  To avoid this, we need to scale the two values first,
       and only sum the scaled values.  We can use either
          x = ((n - 1 - i)/(n-1))*x0 + (i/(n-1))*x1
       or
          x = ((n - 1 - i)*x0 + i*x1) / (n-1)
       to do this safely; we can also ensure the multiplications are done before
       the additions by using a cast to double, because a cast requires the compiler
       to restrict the value to the range and precision of that type.
    */

    /* One call is special; the argument is at the mid point. */
    if (count == 1)
        return func((double)(0.5*x0) + (double)(0.5*x1), data);

    /* Since the rest of this function only uses (count-1), shorten
       the expressions by pre-decrementing count. Note that then
       the loop iteration range is from 0 to count, inclusive. */
    --count;

    for (long i = 0; i <= count; i++) {
        const double  x = ( (double)((count - i) * x0) + (double)(i * x1) ) / count;
        const int  err = func(x, data);
        if (err)
            return err;
    }

    return 0;
}
but obviously, I'd love to get feedback from cow-orkers or other developers, since I know my comments can definitely be improved upon.

I am also trying to say that I do not think there is one best/recommended way to comment code.  I'm just showing what I think is good and bad, and here, what I'd like to be my first instinct on writing comments.  Any suggestions for better result are welcome, just don't tell me to not write code because I fail English and it is not my native language.
Title: Re: Best book for learning embedded c
Post by: PlainName on December 21, 2020, 11:04:41 am
Quote
it seemed to me to be just blah blah about how a non-English speaker like myself shouldn't talk about comments or write any code

Well I apologise for unthinkingly forgetting that we are not all native English speakers. It was meant as shorthand for 'natural language' as opposed to 'programming language'.

Quote
Here is the same function I talked about above, commented in a way I myself would like to have commented it

IMO that's great. The function header is all I really want to know when I am figuring out what use the function is and how it fits into the program. The in-function comments are useful when I have to deal with the detail of what it's doing, so there are two separate levels of interest and requirements.
Title: Re: Best book for learning embedded c
Post by: Nominal Animal on December 21, 2020, 01:53:42 pm
The function header is all I really want to know when I am figuring out what use the function is and how it fits into the program. The in-function comments are useful when I have to deal with the detail of what it's doing, so there are two separate levels of interest and requirements.
Well put.

I did not realize before that some comments are needed for further development, and some for fixing bugs, or rather that these comment types are completely different.  The former really requires the functions to be well described; and the latter requires understanding how the original developer intended the code to work (logic, algorithm, etc).

(Quite humiliating to me, because it has been over a quarter of a century when I was first paid to develop an application; and my total SLOC count in C alone must be well over a million around half a million, based on ~ 400 LLOC per week on average for a quarter of a century.)
Title: Re: Best book for learning embedded c
Post by: SiliconWizard on December 21, 2020, 07:04:31 pm
The function header comment should document the contract of the function: what it does, what input it expects, what it returns, what side effects it has, and what error conditions may occur. The bounds of valid input and the range of non-error output should be stated, where these are not equivalent to those set by the parameter and function types. Where applicable, the algorithm used should be described, or a reference provided to a published description.

Agree with this (if what I said earlier was not clear enough). Documenting *how* a function does what it does in a function "header" is useless in general - and worse. But it should indeed document what it takes to consider the function a black box, so the "contract" as you said. I don't agree with even stating what kind of "algorithm" it uses in this header - this pertains to the comments inside the function, as long again as it's not obvious. Exception to this may be if some algorithm it uses would matter to "users" of the function. But it's usually not the case. What matters would more be, the complexity of the function's code (if it matters, say for a sorting function), whether the function acts on a given array in place or not, things like that. Keep it "high-level". The innards should stay inside.

To illustrate again with a sorting function, who cares, from the outside POV, whether you used quick sort, heap sort, or whatever. What may matter is the complexity, for instance, or how much extra memory it takes, if any. So state this in the function's header. "Users" of the function need not know how things were implemented, most of the time, but how it behaves.

Then details may be provided in the function's body, like algorithms, references to papers, etc. Just my 2 cents.
Title: Re: Best book for learning embedded c
Post by: Nominal Animal on December 22, 2020, 07:09:49 am
The function header comment should document the contract of the function: what it does, what input it expects, what it returns, what side effects it has, and what error conditions may occur. The bounds of valid input and the range of non-error output should be stated, where these are not equivalent to those set by the parameter and function types. Where applicable, the algorithm used should be described, or a reference provided to a published description.
Agree with this (if what I said earlier was not clear enough). Documenting *how* a function does what it does in a function "header" is useless in general - and worse. But it should indeed document what it takes to consider the function a black box, so the "contract" as you said. I don't agree with even stating what kind of "algorithm" it uses in this header - this pertains to the comments inside the function, as long again as it's not obvious. Exception to this may be if some algorithm it uses would matter to "users" of the function. But it's usually not the case. What matters would more be, the complexity of the function's code (if it matters, say for a sorting function), whether the function acts on a given array in place or not, things like that. Keep it "high-level". The innards should stay inside.
Do you (you as in everyone) add the comment to the header file (declaration), or the implementation, when the two are separate?

Both have issues, and the option I believe works best for such functions, as I have already stated, is putting it in an external file; I like man pages.

If that is not feasible, then I see a real need to duplicate the contract comment to both.

If the contract comment is only in the header file, anyone working on the implementation needs to essentially have the header file open also, to see the contracts.  The real risk, however, is not adding any changes (especially assumptions and requirements, like holding a specific mutex, and so on) to the contract comment.

If the contract comment is only in the implementation, others wanting to interface to the code but not having the sources for the implementation (i.e., only having the library installed), have no access to the contract comments.

The simple case is functions used within a compilation unit itself, locally.  Most of those have an interface and purpose that is obvious from the context, so I personally find I need to know why and what more – i.e., what the developer intended to accomplish with that function – than I need to have the Doxygen-style formal comments.  Recently, I needed to dig out some details on Linux USB Human Interface Device internals, including drivers/hid/hid-input.c (https://github.com/torvalds/linux/blob/master/drivers/hid/hid-input.c) of the Linux kernel.  Even though only one of the functions has a comment before its implementation, I found the comments quite sufficient for understanding the code.  (I think it has sufficient comments in my opinion – noting that functions used externally have an explicit EXPORT_SYMBOL macro statement in the file scope, and because it handles low-level USB stuff, is a relevant example for embedded C also.)

I feel I need to emphasize that I'm not challenging anyone by this.  I'm asking for opinions based on experience, because this is a field I wish I had paid more attention when I first started learning C.  The more I write C (a few hundred lines each week right now, about half experiments on distributed and parallel simulation details, half examples I write and post elsewhere using throwaway unregistered accounts – a drive-by helper :-DD), the harder it is to change ones own habits.  I believe we should point all this out to learners, and even eat some humble pie to make sure they understand the importance of comments describing developer intent, and pay attention to learning to write useful comments as well.  I do kinda claim even function contract comments are secondary to describing developer intent, but maybe that is just because of the type of code I have worked with (simulators and low-level/library code doing complex, security-sensitive stuff) and not true for general C or C++ or embedded C code.
Title: Re: Best book for learning embedded c
Post by: PlainName on December 22, 2020, 08:14:55 am
Quote
Do you (you as in everyone) add the comment to the header file (declaration), or the implementation, when the two are separate?

Ha! Good one :)

My preference is to add to both on the basis that one might be looking at either file. For instance, in my IDE when a function is previewed, it shows the actual function so a header comment there is appropriate. However, a different IDE may choose the header file so you'd want the comment there. Pick one and it's a drag for some people - for instance, the ESP-IDF has header comments only in the .h, so I never see them in my IDE unless I explicitly go looking.

The basis for doing both is that the header file is where the external contracts live. That is, if you're calling a function from somewhere else, if it's not in the header you're out of luck, so clearly that's where you look to find out what you can do. However, there will be intra-source file calls as well, and those function prototypes won't appear in the header. Without duplicates, you'll have some functions commented and some not.

However, the downside to doing both (apart from more work) is that it's easier to forget one and then get out of step. Personally, I think it's worth it (a quick copy'n'paste is all it takes) but it wouldn't surprise me if mine was a minority view :)
Title: Re: Best book for learning embedded c
Post by: Nominal Animal on December 22, 2020, 01:51:34 pm
Quote
Do you (you as in everyone) add the comment to the header file (declaration), or the implementation, when the two are separate?
Personally, I think it's worth [adding to both] (a quick copy'n'paste is all it takes) but it wouldn't surprise me if mine was a minority view :)
Like I said, I agree if external documentation is not possible/viable.

When working with other developers, that external documentation can also act as the interfacing agreement between developers.

I'm sure you too have seen cases where "the owner" of a function has refused to change the function signature/contract, basically telling the callers to just deal with it, because "the owner" considers the others' issues somebody elses problem, and doesn't want to "waste" time fixing other peoples problems.

(.. I think I just described the most annoying issue I encounter with open source projects.)

In a company, you have supervisors to deal with such conflicts.  So, I can definitely see why one would prefer just duplicating the function contract comments, and not try and maintain external documentation, especially if there are dedicated technical writers to do the documentation.

With non-commercial projects, like so many distributed open-source projects, you either need a "lead developer" or a "benevolent dictator" or a Linus someone who keeps developers in line, telling them in clear terms if their work product is unacceptable.  In this PC world, I deeply dislike the idea of socially aware soft discourse as a technical conflct resolution mechanism, and think that using the external interface documentation as the contract for interfacing different parts, monitoring any changes to the interface documentation more carefully than the code itself ("silent" changes to the documentation being forbidden!), and requiring interface changes to be discussed via email with all participants – preferably a mailing list common to all project members, so that a written record is available afterwards – is a better alternative.
Title: Re: Best book for learning embedded c
Post by: PlainName on December 22, 2020, 02:51:39 pm
Well this is adding yet another view and requirement to the ones we've already identified, so the solution may not be the same. But, quickly, comments made with a view to ultimate collation via doxygen and the like can produce pretty decent library (for want of a better descriptor) documentation. It would at least be up to date with the code. OK, well more likely to be up to date.

However, personally I do that kind of documentation separately from the code comments, partly because it's a different audience and partly because I want to say different things, or the same thing in a different way.

Having said that, and despite being a creature of habit, I change my mind if I see some other way that makes better sense or adds something, so maybe run this thread again in a couple of years and you can try the "But, before, you said..." :)
Title: Re: Best book for learning embedded c
Post by: PlainName on December 22, 2020, 02:58:20 pm
Quote
In a company, you have supervisors to deal with such conflicts.

Not in ones I've worked for. In one, my co-developer and the boss were complimentary about my comments and resolved to do start doing them too, but they never did.

Title: Re: Best book for learning embedded c
Post by: SiliconWizard on December 22, 2020, 06:30:09 pm
The function header comment should document the contract of the function: what it does, what input it expects, what it returns, what side effects it has, and what error conditions may occur. The bounds of valid input and the range of non-error output should be stated, where these are not equivalent to those set by the parameter and function types. Where applicable, the algorithm used should be described, or a reference provided to a published description.
Agree with this (if what I said earlier was not clear enough). Documenting *how* a function does what it does in a function "header" is useless in general - and worse. But it should indeed document what it takes to consider the function a black box, so the "contract" as you said. I don't agree with even stating what kind of "algorithm" it uses in this header - this pertains to the comments inside the function, as long again as it's not obvious. Exception to this may be if some algorithm it uses would matter to "users" of the function. But it's usually not the case. What matters would more be, the complexity of the function's code (if it matters, say for a sorting function), whether the function acts on a given array in place or not, things like that. Keep it "high-level". The innards should stay inside.
Do you (you as in everyone) add the comment to the header file (declaration), or the implementation, when the two are separate?

I for one usually add "header comments" for functions only in the "implementation" source files. (And I certainly do NOT duplicate this information in other files. It's already difficult to make sure your comments are always up to date, but if said comments must be duplicated on top of that, it's just a lost cause.)

My rationale is that it's the place it's usually easier to put them, and where it makes more sense for people working on the implementation.
For "users" of the implementation, documentation tools such as Doxygen can extract types, prototypes, and associated comments, so for me that's better than putting this info in header files.

Now it's also all a matter of not duplicating information. So header files may still contain some comments, for instance on type definitions or constants, because this info won't be available anywhere else.

Of course, a given team may have its own set of coding rules, in which case you usually don't have a choice.