Initializing a struct member with a certain value if left undefined - Page 1
Forum section: electronics / beginners

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 12, 2018, 08:02:30 pm
content: Hallo ladies and gents,yet again I want to ask a question regarding C.I am using a struct to interface with a library I am writing, I want that if the user doesn´t initialize a member of the struct (which he/she is not supposed to initialize but rather only readas information available at anytime to him/her) for that member to default to a certain value.My case is this:I´m using a struct to interface to a couple functions, specifically to initialize the CCP modules of a PIC in PWM mode using the XC8.I have this struct:typedef struct SimplePWM_Setup{uint8_t CCP_Module;bool use_TMR4;uint16_t Max_Step;uint8_t Prescaler;int32_t Frequency;uint16_t Duty_Step;};The frequency is determined by some other values during the Init function. The user is not supposed to write the frequency, it should be a read only value to the user that can be accessed bythe init function to set the value.I want the frequency to always be initialized to -1 as that would mean that the Init function is not yet run.I don´t want to predetermine the the structs that are going to be used in the main program as for example, mainly for RAM considerations.What can be the solution to my problem?Thanks in advance,Lefteris

author: helius
author_rank: Super Contributor
timestamp: March 12, 2018, 08:18:22 pm
content: This is not straightforward to achieve in C, because data types don't have default values. What you can do is to start with a statically defined object that serves as a template, and return copies of it from yourinit()function. You could also do some macro hack likeCode:#define NEWPWM { .Frequency=-1 }and require that it be used likeCode:struct SimplePWM_Setup mysetup = NEWPWM;mysetup.CCP_Module=42; //etc

author: Jeroen3
author_rank: Super Contributor
timestamp: March 12, 2018, 08:29:49 pm
content: You are in need of C++. Try itBut, for normal C, I always start variables private to a module with an underscore (_) so that syntax prediction doesn't hint on it.I have, on some occasions, played it dirty. Specifically to warn about illegal configurations or size errors of arrays using #error.Code:#if SimplePWM_Setup.frequency != -1#error You're not supposed to initialize SimplePWM_Setup:frequency#endifExample:Code:typedef struct SimplePWM_Setup{uint8_t CCP_Module;uint8_t Prescaler;uint16_t Max_Step;uint16_t Duty_Step;bool use_TMR4; // is a charuint8_t padding; // useless space/* Private */int32_t _frequency;};You also need to learn aboutstruct packing.

author: Ian.M
author_rank: Super Contributor
timestamp: March 12, 2018, 08:33:44 pm
content: Edit:CAUTION - SEE BELOW!C does clear uninitialised variables by default. If you replace the bool use_TMR4 flag with some bitfields, you could add an initialised flag, that is set by your Init function, so you can detect whether or not Init has been run, without increasing the size of the struct. Also it makes little sense to have negative frequency, so once you have a separate flag, make it uint32_t Frequency.@Jeroen: The C preprocessor cant 'see' the value of C runtime variables or constants, so how is that #if going to do anything useful?

author: Jeroen3
author_rank: Super Contributor
timestamp: March 12, 2018, 08:42:31 pm
content: The preprocessor can see variables if set by literals. Not runtime, no.

author: glarsson
author_rank: Frequent Contributor
timestamp: March 12, 2018, 08:59:32 pm
content: Quote from: Ian.M on March 12, 2018, 08:33:44 pmNo.Uninitialized variables are only set to all bits zero for variables with lifespan of the whole program, either declared at file scope (outside of a function) or inside a function and explicitly declared static using the reserved word 'static'.Variables declared inside a function without 'static' will have random values in them (they will have bits and pieces of earlier variables and other stuff placed on the stack).Variables whose memory are allocated using 'malloc', 'sbrk', etc will also have random content (except where otherwise noted, e.g. 'calloc').

author: Ian.M
author_rank: Super Contributor
timestamp: March 12, 2018, 11:06:29 pm
content: Thank you for the correction.Quote from: glarsson on March 12, 2018, 08:59:32 pmSo its unfortunately *NOT* foolproof, and as XC8 is a C89 compiler that doesn't support the C99.member=struct initialiser syntax you cant easily patch it by using Helius' idea above.Quotemalloc, etc. allocate MEMORY, not variables. Also,XC8 doesn't implement malloc- it makes very little sense to do so on an 8 bit PIC!

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 12:56:12 am
content: Quote from: Jeroen3 on March 12, 2018, 08:29:49 pmDoes struct packing really matter on an 8bit? What you have there makes sense for a 32bit core but 8bit? Meh?BTW I know this is off topic but it really sparked my curiosity, thanks for bringing it up!

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 01:45:46 am
content: Quote from: Ian.M on March 12, 2018, 08:33:44 pmIt would actually make much more sense to use a float type, with low _XTAL frequencies you can achieve values in HZ or lower.But I like to avoid including float math for something so simple. Not a great deal of people are going to use sub MHz on the main clock along with PWM.What you are suggesting would make more sense with uint24_t for the frequency which is rather tempting but non standard.On the other hand this library is indeed for use with PIC18F so it only needs to be microcontroller portable (which I can live without, as it complicates things and too much drama and no...)...If you think about it a uint24_t can get the maximum value of 16777215 while the maximum theoretical frequency that can be achieved is 16MHz.So you could say that if frequency is 0xFFFFFF then frequency is not set. It would also make for a slightly faster code if not also smaller but it doesn´t follow convention.By convention a function would return -1 if it failed, same here to an extend. A negative number means you failed to initialize the frequency, but the idea is also that you arealways able to read the frequency. Or else I would just return the value of the frequency from the init function.The problem comes though when you want to change the settings of the PWM. You got to run the Init function again! That doesn´t mean that the Init function will succeed though!If it fails, you are stuck with the old frequency, which you would have no way of knowing in your runtime unless you had stored it.So that´s the mentality behind building this library, it´s very gui friendly, doesn´t need to be in my specific occasion, never the less as we say here in Greece,sensible parents' kids cook before they get hungryCheers,Lefteris

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: March 13, 2018, 01:56:44 am
content: Quote from: TrickyNekro on March 13, 2018, 01:45:46 amUnless you were already using floats elsewhere, that would be insane as it would pull lots of floating point library code in.It rarely makes sense to use FP in an embedded system, as real-world values rarely have the range that would need floating point

author: filssavi
author_rank: Frequent Contributor
timestamp: March 13, 2018, 02:10:15 am
content: Quote from: mikeselectricstuff on March 13, 2018, 01:56:44 amI disagree, sort of...While it’s true that very rarely you need FP in embedded systems, they come in really really handy in certain tasks like for example motor drives in particular and control loops more in general since the alternative (Fixed point) is really really painful to work with and there are a lot of pitfalls and possible errors to do with underflow/overflow and you have to keep track of conversion constants which often makes your head hurt just to think about itThe only caveat is that for FP to be viable you need an FPU, that means a largish, more expensive, MCU, and that is why Fixed point is still widely used in lower cost motor drives (whaling machines for example)

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 02:18:57 am
content: But of course Master I speak of no blasphemy! (Say that to the Arduino guys)

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 02:24:51 am
content: Quote from: filssavi on March 13, 2018, 02:10:15 amI disagree strongly... With floating point math there is no way to predict error when you values get near the limit, but with fixed point you can always work your way with windup delimiters.

author: filssavi
author_rank: Frequent Contributor
timestamp: March 13, 2018, 03:27:04 am
content: Quote from: TrickyNekro on March 13, 2018, 02:24:51 amAgain you are right in absolute terms but if we think about this specific case the worst case scenario in a traditional motor drive is on the current loopCurrent can vary (being generous) 4 orders of magnitude (lets say from hundreds of mA to hundreds of A) at one extreme of the range 999.999A we can find that the error due to conversion is 0.000023.. (2.3*10^-5) while at the other end at 1mA the error due to conversion is 4.7e-11.now even if your current sensing ADC has a 16 bits (which is very very very unlikely, 12 bit is already uncommon 8 bit is more likely) your conversion error is 2 orders of magnitude less than an LSBmoreover do you really think you can even control a motor's current to the milliampere level when you are pumping 900A, and even if you could do you think that your motor's torque would really change?now you have to take into account compound error but than again I can't think of any application in motor control where you have iterative calculations that can lead to significant compound errorsso wrapping up while true that errors in floating point do exist and can be dangerous, in this particular application in a narrow field of mathematics/engineering they can be safely discardedproof and point most modern (ok except for the very cheapest product lines) DSP's specifically targeted to motor control have an FPU, if it was that useless/dangerous they would have put all that silicon to another use

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 03:43:04 am
content: Hmmm yes anything Phase controlled can greatly benefit from a dedicated FPU, that´s why there is one. That´s though more for the phase control itself rather than anything else. To use with the trig functions.But motor control for Permanent Magnet DC Motors for example, meh... you won´t need more than an 8bit.You´ll never need so much fine control in the current department for motors. But I digress... we are totally off-topic

author: hamster_nz
author_rank: Super Contributor
timestamp: March 13, 2018, 04:41:59 am
content: Quote from: TrickyNekro on March 12, 2018, 08:02:30 pmI've seen two approaches- use a 'mask' field to indicate which values have been populated.- have a "initialize" or "setDefaults" function that defaults values for the structureCode:struct Foo foo;FooSetDefaults(&foo);foo.whatever = value1;foo.somethingElse = value2;FooDoSomething (&foo);You can always add a 'hidden' magic number in the structure, which you set in SetDefaults(), so you can test if the structure has been properly initialized.Maybe it is better to completely hide the structure from the user, and just use a pointer? That way the user is not made aware of the implementation at all, and you can change your backend implementation on a whim, just as long as the API is kept the same.Code:struct Foo *foo;foo = FooNew();FooSetWhatever(foo, value1);FooSetSomethingElse(foo, value2)FooDoSomething(foo);FooFree(foo);But that is just inviting memory leaks when you forget to call FooFree();

author: Jeroen3
author_rank: Super Contributor
timestamp: March 13, 2018, 07:19:10 am
content: Quote from: hamster_nz on March 13, 2018, 04:41:59 amHmm... You can leverage casting struct pointers and have the user view a different struct then private to the module. But still work with the same block of memory.Code:typedef struct ModulePublic {int userParam1;int userParam2;int userParam3;unsigned char _alloc_private;};typedef struct ModulePrivate {int userParam1;int userParam2;int userParam3;int privateStuff1;int privateStuff2;};

author: David Chamberlain
author_rank: Frequent Contributor
timestamp: March 13, 2018, 07:36:27 am
content: Going to agree with hamster_nz here on his solution, I've used both approaches many times myself.

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 06:43:10 pm
content: Quote from: hamster_nz on March 13, 2018, 04:41:59 amOne thing is that I can not allocate memory, it´s just not supported on 8bit PICs, or else I would have used a macro by that time.The problem with the setDefaults function is that it has to be run in the runtime, it has to be included in an one time init function.That´s what I want to avoid or at least hide.Quote from: hamster_nz on March 13, 2018, 04:41:59 amAnyhow I have to use pointers cause there is no other way for a function to alter a variable in the function from the main, other than the value returned.I´ve been looking into it for some time and yes I am in need of C++ indeed, but for now you only get the XC8 which is a C89 compiler so touph luck for me.I will have to find another work around or maybe just leave it as it is. We´ll see.Thanks for the help and the ideas so far!Cheers,Lefteris

author: Ian.M
author_rank: Super Contributor
timestamp: March 13, 2018, 06:57:54 pm
content: Hmm.What about an array of static (file scope) structs in the same file as access functions for them? Pass in a #define at build time for the number of SimplePWMs to support - either as a compiler command line -D define or as a user supplied include file, and rely on the C startup code zeroing them to detect use before initialisation. The only interface exposed would be the access functions declared in the associated header.

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 07:26:33 pm
content: It´s really becoming a headache at this point though.As far as I know all variables are initialized as zeros when first called in C. So from this perspective trying to pass the structure without initializingthe values which effect the frequency value will change the frequency value and the init will fail. This is all made in the init implementation.The only thing to do is to actually include a first init made flag, nothing else. Also turn the bool value that I use to a bitfield to conserve a bit of RAM,but you pay it in the program space as bit checks are required. These are all but small trade offs that should be made. Can´t really find a better solution.For the moment I think that I can live with that.

author: Ian.M
author_rank: Super Contributor
timestamp: March 13, 2018, 07:39:02 pm
content: Quote from: TrickyNekro on March 13, 2018, 07:26:33 pmAs Glarsson pointed out earlier, that isn't true of auto (function or block scope) variables, so the *ONLY* way of guaranteeing that in the face of user idiocy is to not let the user declare their own copies of the struct, hence my advise to use an array of static (file scope) structs declared in a source library .c file.That still doesn't help you if the user does something really dumb like disabling zeroing uninitialised variables 'to speed it up', but if the user's that wilfully *STUPID*, there is little you can do except run away as far as possible - stealthily and quickly - to avoid being sucked into the resulting clusterf**k.Incidentally, there is a third party C++ compiler for 8 bit PICs (excluding baseline parts) - check outhttp://www.sourceboost.com/Products/BoostCpp/Overview.htmlHowever its quite a long way from HiTech PICC / Microchip XC8 syntax and due to its small market penetration compared to XC8, using it would decrease the target audience for your library considerably.

author: David Chamberlain
author_rank: Frequent Contributor
timestamp: March 13, 2018, 07:40:13 pm
content: Quote from: TrickyNekro on March 13, 2018, 07:26:33 pmNo -https://stackoverflow.com/questions/21152138/local-variable-initialized-to-zero-in-cand even if it did double no, never rely on a compiler to initialize memory for you.

author: Jeroen3
author_rank: Super Contributor
timestamp: March 13, 2018, 07:54:18 pm
content: Quote from: TrickyNekro on March 13, 2018, 07:26:33 pmQuote from: ISO/IEC 9899:TC3 6.7.8:10 (old one, I know)Quote from: 6.2.4:1So yes, for statics and globals. Some linkers offer to zero initialize all memory, but this means inserting a loop that must run beforemain()is entered.However, always explicitly initialize variables. Since this is one of the easiest things for implicit bugs to crawl in.I crushed one just today, since some compilers, especially those for 8 bit, do not honor all parts of the C standard. Such as this one, adding unnecessary loops and start delay.Quote from: misra Rule 9.1 (required)

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 07:59:27 pm
content: Quote from: Ian.M on March 13, 2018, 07:39:02 pmThe struct is initialized in main and then the pointer of the struct is send to the function, it´s not initialized in the function, I really wouldn´t do thatStill your idea is not bad. It doesn´t have to be an array, a single struct can suffice. Then the user can always take the risk to call their own struct of the same type.Yeah, dunno that´s something to think of.

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 13, 2018, 08:02:07 pm
content: But then there is also another question can I have a piece of code to run when I include my .h file? If that´s the case then we have over-discussed this.I kinda doubt it though :-/

author: helius
author_rank: Super Contributor
timestamp: March 13, 2018, 08:19:22 pm
content: .h files are just snippets of code that get #included in other files. There's no restriction on them beyond the restrictions that are already in place. In this case, there is a constraint that the initializer for a static variable must be a constant expression: you can't call a function or do arithmetic to decide what the initial value will be. And the top-level members of a translation unit can only be declarations; you can't place statements to be executed outside of a function declaration.

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 14, 2018, 12:28:17 am
content: I really feel like I found the solution myself in a way.So I would guess as Ian suggested to include a struct in my .c for the "end user",then I could use a premain function to set the struct values and that´s possible with the__attribute__((__constructor__)) method?I have to try this myself but the compiler doesn´t really seem to mind the above. So... hmmm yeah I guess that´s possible.

author: David Chamberlain
author_rank: Frequent Contributor
timestamp: March 14, 2018, 02:15:52 am
content: Quote from: TrickyNekro on March 13, 2018, 06:43:10 pmI'm wondering if you can explain your requirements / constraints a little more because I think you are making your life hard for yourself by trying to stupid proof your library.Now throwing around __attribute__ and other weird 'hacks' as I would call them is only asking for trouble. Do you want your library to be cross compiler / platform friendly? or is that not an issue in this case?You mentioned you do not want the checks to be performed in run-time? It is not possible (in a comp-sci theoretic sense) to write a program to check if the user code has or has not modified your structure especially on a PIC micro.At some point you are going to use the struct in your library so why not do validation on it at that stage?(borrowing and expanding from Hamster_NZ)Code:int FooDoSomthing(struct Foo &foo) {if(foo->Max_Step == SOME_CRAZZY_INVALID_NUMER) {return MAKE_THEM_PAY_WITH_THIS_ERROR_CODE;}... normal stuff goes here.}Most libraries I see and use get around the stupid users issue by documenting / commenting / and providing example code. If they can't be saved after that then is it really your job to solve the world and introduce a whole bunch of you own bugs and issues?Even if you did do all that helpful initialization stuff for the user, then the same user could just buffer overrun all over your struct and you would never know. That's why validating a struct when you use it.. AT RUNTIME.. is safer.

author: TrickyNekro
author_rank: Frequent Contributor
timestamp: March 14, 2018, 02:45:43 am
content: Quote from: David Chamberlain on March 14, 2018, 02:15:52 amI do that alreadyMy initial and remaining question is whether or not I can initialize a struct whenever it´s called with a predefined setup, so that in my exampleevery time I write in my code:struct my_type fooA foo member gets a certain value. You can do that in C++ easily with constructors yes totally easily and normally. Can you do it with a C89 compiler?Yes I don´t like to provide the struct to be used to the user. Yes I can write the documentation and say please run this init function to initialize the structand then run the main init but that gets ugly fast. Then again if you don´t initialize the struct you may throw garbage in the function that might work!It would be rare but not impossible. If I can not guard against these problems easily then I may just let it there and basta!

author: helius
author_rank: Super Contributor
timestamp: March 14, 2018, 12:59:12 pm
content: Quote from: TrickyNekro on March 14, 2018, 02:45:43 amI think that you can't. If providing initial values is really what you want then I think the macro approach is still the only one:Code:#defmacro new_struct(id) struct SimplePWM_Setup id = {0,0,0,-1,0,0}...new_struct(mysetup);GCC has thedesignated-initializer syntaxfrom C99 even in C89 mode, whilst most other C89 compilers do not have it, but it is not actually required in this case.

author: Ian.M
author_rank: Super Contributor
timestamp: March 14, 2018, 01:19:19 pm
content: Yes, Although C89 initialiser lists are a PITA, the C99 designated initialiser syntax is only essential when you are dealing with unions and want to initialise any member other than the first.However loading your library down with excessive runtime error checking to catch design-time mistakes is *NOT* a good thing. It wastes performance to no good purpose. At the very least, there should be an option to strip it all out for a release build. Remember: C programmers expect the right toshoot themselves in the footif they want to!