Const Isa Virus

(...from UseConstMemberFunctions)

Using "const" in C/C++ programs is considered a good thing by many. See ConstCorrectness for discussion.

But you should realize that if you start declaring things "const" in your system, and your code does not declare things const even if it doesn't modify them, then it'll have a ripple effect: you'll find that because you declared some data value "x" const, all the functions you pass it to must accept a const parameter and any methods you call on x must be const.

For instance, "strcpy()" never modifies its 2nd parameter so it declared "const char *". If it wasn't and you declared functions like that in your program, you'd find that declaring a few variables "const", here and there, forces you to more precisely define the interfaces of functions/methods you call, and functions/methods they call, etc.

To stop the ripple, you can either put in a nasty hack to cast away the constness (it is generally considered bad to declare something is const and then modify it anyway) or make a non-const copy of the object and pass that on instead.


"declaring a few variables "const", here and there, will force you to more precisely define the interfaces of functions/methods you call"

This sounds like a good thing to me, that is, precisely defining what a function will do. Granted, it can be difficult to define what const means when applied to real life objects (thank goodness for mutable). For example, declaring a read-only file class as const seems logical, even though that requires the underlying file handle to be mutable (allowing it to be opened and closed). I usually find it makes sense if you apply const logically to the physical object and adjust the software class to cooperate. This will avoid the need to cast away const at higher levels, i.e., the const definition is logical for the program. -- WayneMack


An example of the "ripple effect": (slightly fictionalized to make me out to be the hero :-)

Once I was working in an EmbeddedSystem where memory was a bit tight. We were always scrambling to find some way to reduce the amount of RAM needed. The original boot subroutine copied the initial values of all global and static variables from ROM into RAM. I discovered that if I used the "const" keyword when I defined strings, bitmaps, font information, etc., then the boot subroutine would keep them in ROM rather than copying them to RAM. I went a little crazy declaring every string and array (bitmaps, menus, etc.) as "const", trying to conserve as much RAM as possible. Often, however, the compiler complained, because some string would be passed to a function whose prototype did not use "const". Sometimes it turned out that the function really did modify the string; then I would take the "const" off the original definition of the string. Other times, I discovered that the string was never modified -- that function passed it to some other function, and that other function passed it off yet again, until I reached a leaf function that inspected the string and did something useful. Every one of those functions had to have its prototype modified from "char * message" to "const char * message" to allow them to handle ROM strings. That's the ripple effect. The newly-modified functions then had the additional ability to accept a pointer to a string in ROM, and they still worked when passed a (non-const) string in RAM, and I saved a few bytes of RAM by keeping that string only in ROM. (By the way, I actually used Flash memory, not ROM.)

Holy Mackerel, there, Saphire! I HaveThisPattern. Had one MCS-51 project where I was trying to use the Franklin C compiler's idea of const for ROM-able constant data and just ran into reams of data addressing problems. Eventually I ended up copying the entire program and data into RAM and executing out of that. It turned out to be a better solution for the instrument, too, since there was plenty of speedy RAM and the ROM access was kinda pokey. Still, I felt like an idiot that I had to move even the executable code into RAM just to alleviate the 2-vs-3-byte addressing problems. Duh.

A C compiler where "const" implies switching to a different address space entirely is a pretty unusual beast, though. (Sounds like that's what happened in the story. Sorry if I misunderstood.) For example, on GCC for the AVR architecture (which has separate address spaces for flash vs. RAM) uses a GCC extension to explicitly request data be placed in Flash, because in that case a completely different set of machine instructions is required to access it and pointers to this data are a different size. "Pointer to RAM" and "Pointer to Flash" are thus completely incompatible types. I don't believe standard C allows for different pointers to have different sizes, even if "const" is used.


Note that declaring a parameter "const" in a leaf function never has ripple effects: adding that "const" expands the functionality of the subroutine so it can now accept (pointers to) items in ROM; no changes anywhere else in the program are ever needed.


By the way -- most compilers have switches that allow for the bad practise of passing consts or vars to functions kinda willy-nilly. We often do this in EmbeddedSystems because of the reasons already discussed. That doesn't make it right; it's just what gets the bills paid on time.


Contributors: JeffGrigg, MartySchrader, WayneMack

CategoryCpp


EditText of this page (last edited November 26, 2014) or FindPage with title or text search