Global Variables

Yes, there's a better way - designated initialisers:

Define the structure in the header file. In the C file, you have:

ConfigRecord Cfg = { .x = 1; .y = 2; };

It would be nice if you could do the same thing with function call arguments.

(I don't know exactly how much of this is standard in C99 - someone will no doubt tell us! But even if it is gcc-specific, it's worth using if you can.)

Reply to
David Brown
Loading thread data ...

That's not a problem here - these are "extern" declarations, not definitions, so no storage is *ever* allocated by these lines.

Reply to
David Brown

If it's compiler specific then it's worth avoiding if you can.

Robert

** Posted from
formatting link
**
Reply to
Robert Adsett

David Brown wrote, On 18.06.2008 21:33:

Thanks for pointing this out, I didnt know ... But for documentation (I'm using robodoc) there is still the disadvantage, that I'm forced to describe the record structure and the initial values/defaults on different locations.

Regards, Rolf.

Reply to
Rolf Schroedter

You can't write code that is compatible with all compilers - especially not all compilers in the embedded world! You normally can't even write code that compiles on two different compilers for the same target, without at least using a selection of #if's to handle differences in the details.

As a general rule, I agree with you - there is no point in deliberately writing code that is specific to a target or compiler when you can equally well write more portable code. But the key point here is "equally well". If a gcc extension lets me write *better* code (either because the generated code is smaller and faster, or the source code is shorter and clearer), then that's a good reason to use it. The advantages of using the extension must be weighed against the disadvantages of losing the portability. Where that balance lies is entirely dependant on the circumstances.

Reply to
David Brown

Personally I don't mind includes inserting different code depending on the context. I do this quite often myself. The usual situaution is when coding some data structure that you want to be opaque to the surrounding program but you still need access to the innards from more than one module (e.g. one is basic low level access routines, another is higher level stuff that may on occasion still need low level access). I do something along the lines of

struct someStructure;

#ifdef SOMESTRUCTURE_INTERNAL struct someStructure { ... }; #endif

which certainly isn't bulletproof but it does provide some level of assurance that the innards aren't being tampered with at least inadvertently.

I have a saying here - Sometimes smart programmers write dumb code. There is a tendency to view extensive use of globals as unstructured but sometimes it is the simplest, easiest and most reliable way to go. This is especially true of code that naturally gets 'messy' anyway - parsers come to mind. The same is true of dynamic memory allocation - sometimes even on the desktop a fixed sized array saves so much work that it is worth going for over fancier code, even if it does impose limits on what the program can handle.

--
Andrew Smallshaw
andrews@sdf.lonestar.org
Reply to
Andrew Smallshaw

It doesn't really "protect" the innards any more than you would by replacing the "#ifdef" with a comment saying "for internal use only".

I agree with you here.

Actually, for most desktop work I avoid messy dynamic memory *and* fixed size arrays - I do most desktop programming with Python.

Reply to
David Brown
[...]

I seem to have standardised on an "architecture" which does rely heavily on global variables. This is for medium sized microcontroller projects, maybe 32k code.

I have a global "set" structure for all the (non-volatile) settings, and a global "status" structure for the continually changing quantities. The program runs in a big loop driving a load of state machines. There may be a few interrupt driven ones too for timing-critical jobs (e.g. serial comms).

All the state machines have access to all the settings and status variables, so they can do e.g.

if(status.temperature > set.temperature_max) { ...

Etc.

I really don't see a better way than this - globals and all - although I remain open to suggestions.

--

John Devereux
Reply to
John Devereux

It is a feature of the C99 standard (section 6.7.8).

Rob

Reply to
rob windgassen

I sounds like you are using more of a PLC-style state machine programming paradigm, rather than the more usual C imperative programming paradigm. You are structuring your code as a set of state machines - C is just the syntax you use to write it (just as you'd use ladder on a PLC). In a way, you are not writing C programs, at least not in way most people think of as C programming.

Since your program is build in a very specific way, it makes sense that you follow specific rules to match, rather than other rules appropriate for other program structures.

Your only risk is if other people work on your code and don't follow your rules - then things will get really messed up!

Reply to
David Brown

[...]

Could be. And I have done PLC programming in the past. Then I jumped straight to a multithreaded (task switching) paradigm. But I have recently done a few projects like I described, and now find the "PLC-style" approach refreshingly simple. State machines are very powerful for embedded control - more so than "procedural" programming for sufficiently complicated systems. There are some areas where procedural programming would be more natural. But I am finding that avoiding it (and multitasking) result in a huge simplication overall. At least for the size of projects I am working on at the moment.

One advantage of this way is that there aren't so many "rules" needed. It's not like multithreaded code where the slightest improper access of a shared resource can be a disaster! When something does go wrong the effects are direct and obvious.

--

John Devereux
Reply to
John Devereux

I must agree with Rich. I use the construct all the time and have no idea how it could be done better.

Reply to
Mr. C

It does, it that the definition is invisible by default - it depends on someone actually reading the code before modifying it. In addition I would hope most coders would at least look as to why there was an area marked 'private' or 'internal' before delving inside and understand any work in there is on their own head.

This code has highlighted areas to me in the past. On that comes to mind was an ADT with a particularly rich API - around 30 functions. That was split into five modules. The first was the truly fundamental types (create, delete, add item, remove item), the second still deeply low-level dealing with aggregate input and output data. The remaining modules provided logically higher-level functions that depending on the module were implemented in various degrees of direct access to data structures and using low-level primitives from the other modules. This arrangement provided a safegaurd against inappropriately moving functions between modules.

Another and perhaps more common reasons is that complex types need some form of initialisation and coding the headers this way forces the use of the proper constructor functions:

struct someStructure myData;

is wrong and can cause all sorts of difficult to diagnose problems since myData is not internally consistent. Fortunately someStructure is undefined and so the compiler will barf when it sees it. Instead you have to do it the way the ADT author intended:

struct someStructure *myData = newSomeStructure();

--
Andrew Smallshaw
andrews@sdf.lonestar.org
Reply to
Andrew Smallshaw

This is exactly the same as a normal global variable (or structure details) with a comment. It is "invisible" until someone has read the header to see the names used, and it is marked private, so that coders should not access it without thinking.

Additionally, your hidden structure has some limitations - you can't use "sizeof" on it, you can't use it for arrays, and you can't make an instance of the struct (although you may see that restriction as an advantage).

This sounds like you are trying to force object-oriented concepts into C. You get a half-working solution, with extra overheads (it looks a lot like you'll need to use dynamic memory here for something that should be statically allocated).

If you want to do "object oriented C", use C++. It's arguably worse for modular programming than C (your headers are full of implementation), but at least it gives you this sort of protection with minimal overhead.

Reply to
David Brown

That's insufficient excuse for writing such headers. It's what "private headers" have been invented for: a group of C files that together form a bigger, aggregate module put stuff that they want to share with each other, but not with the world, into a separate header that only they include. If you deliver aggregate modules as a binary-only library, you don't even ship the private headers. Customers can't inadvertently tamper with things they don't have.

Reply to
Hans-Bernhard Bröker

I don't consider robust ADTs to be particularly object-orientated. It seems to me that this is something that the OO advocates have claimed as their own as one of the merits of the paradigm, when in fact it predates it. Sure, OO takes the concept and runs with it but it isn't an OO concept in and of itself. For instance, I tend to use SML for my high level programming jobs which certainly isn't OO but it does protect ADTs well.

Your point regarding dynamic allocation is well taken though. How significant it is depends on the nature of the structure. I find that most of the time that I create an ADT it is some form of flexible aggregate type where dynamic allocation is natural, but this isn't always the case. Indeed it was only yesterday I was advocating fixed sized arrays in this group...

C++ inevitably brings overhead, it's just that often you can't see it. One of the things about C is that what is easy for the computer tends to be concise to write. More long-winded calculations take a more long-winded expression. This is lost in C++ so you can instinctively see what is natural and what isn't.

--
Andrew Smallshaw
andrews@sdf.lonestar.org
Reply to
Andrew Smallshaw

A pointer: yes. But we were talking about variables, not pointers to them.

The problem occurs with direct, non-arbitrary accesses.

Reply to
Hans-Bernhard Bröker

This is an overly broad statement. To which C++ feature are you referring? You only get charged for the ones you used. Please don't spew FUD, C++ is just a tool.

Speak for yourself. I find writing in C++ to be much more natural than C. Writing abstract interfaces (polymorphism) in C is painful ... structures with function pointers ... yikes. Just look at how many of these there are in the Linux kernel.

--
Michael N. Moran           (h) 770 516 7918
5009 Old Field Ct.         (c) 678 521 5460
Kennesaw, GA, USA 30144    http://mnmoran.org

"So often times it happens, that we live our lives in chains
  and we never even know we have the key."
"Already Gone" by Jack Tempchin (recorded by The Eagles)

The Beatles were wrong: 1 & 1 & 1 is 1
Reply to
Michael N. Moran

Sure you can, at least as long as they deign to call themselves C compilers. :) Mind you I've run across a few self declared C compilers that didn't deserve the moniker.

A complete program on the other hand would be impossible.

Absolutely, although I think the extension must be much better than "equally well". A differnce in emphasis only I think.

Experience lends a jaundiced eye to compiler specific features. I know there is a lot of enthusiasm for interrupt keywords for letting the compiler generate interrupt prologue and epilogue, but I consider that equally or better done in assembly. Obviously YMMV.

BTW the initialization you were referring to is, *I think*, part of C99 but not very widely implemented.

Robert

** Posted from
formatting link
**
Reply to
Robert Adsett

That's pretty much what I meant, although I didn't say it very well. Clearly you can write parts of the code in a portable way, but you can't write complete embedded programs that are portable. How much of your program is portable or non-portable will depend on the type of program in question.

Agreed.

There was a time when I wrote things like interrupt wrappers in assembly (there was a time when I programmed mainly in assembly, and only rarely in C). But compilers have got better, and fixed assembly wrappers have stayed much the same. A decent compiler on most targets will do a much better job of generating the wrappers than you can do manually, since it will preserve exactly the registers it needs, and no more. It's a different matter if you are trying to write minimally small and fast interrupt functions entirely in assembly - then you can do a better job if you put the effort in. But for the common case of the interrupt function itself being in C, the compiler extensions are normally the best choice.

That's what others have said here, so I assume that's the case.

Reply to
David Brown

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.