Fundamental C question about "if" statements

Thanks Jacob -- that's informative.

--
Tim Wescott 
Wescott Design Services 
 Click to see the full signature
Reply to
Tim Wescott
Loading thread data ...

I said there is no sensible reason to use them in a design; I never claimed there were no people doing things which do not make sense :D :D .

Dimiter

Reply to
Dimiter_Popoff

This hobbyist (at least in the embedded world; I'm a commercial programmer by day) would love to use Ada for embedded stuff but finally gave up on that a few years ago and went back to using C.

The main problem are the compilers - there's a freely available C compiler for all embedded platforms/environments out there. This isn't true for Ada which is a problem when you want to write some common libraries to run on a range of MCU architectures.

Even when there's a free compiler available, it might be based on the free ACT version of gcc. As the Ada runtime library for this version of gcc is under the GPL, this means your binaries also become covered by the GPL as well.

The FSF version of gcc doesn't have this problem but this compiler situation with Ada is a real mess.

A related example that gets quoted in Ada circles is an article from about 15 years ago when a university course used C and then Ada to complete a train modeling project:

formatting link

The interesting bit is towards the end in the "Software" section. When the code was written in C, no team successfully implemented the minimum project requirements. When the code was written in Ada, about 50% of them did.

I don't have anything more recent to hand sorry. That's unfortunate because it would be interesting to see what the results are today if the project was done again using the options available these days.

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP 
Microsoft: Bringing you 1980s technology to a 21st century world
Reply to
Simon Clubley

I missed Jacob's posting before my reply. :-(

Oh well. Now you have a text only HTML version as well. :-)

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP 
Microsoft: Bringing you 1980s technology to a 21st century world
Reply to
Simon Clubley

That's very interesting, and relevant to me because I use the gnu toolchain in my work -- so Ada would be ruled out unless I wanted to start by making a run-time library (and, presumably, selling it to recoup my investment).

I believe the original paper is here:

formatting link
McCormick.pdf

(Thanks Jacob)

At least one of the points he noted about C makes me wonder if it isn't a biased instructor, rather than a bias between the languages, that was the primary cause of the problem. The point was in a section of why Ada was better than C, and reads:

"Representation clauses for device registers (record field selection rather than bit masks)"

But, unless you're using a horribly obsolete version of C, you can use bit fields in structures. And while this is, to some extent, excuse- making, even with horribly obsolete versions of C you can damned well hide all of your bit mask ugliness inside of nice tidy macros.

He also mentions that C lacks exception handling, which is a moderately good point -- but I've never felt comfortable with using exception handling in an embedded environment, because in the mostly-headless stuff that I do, there's not much you can do with an exception other than to shut down in a manner that mirrors your best guess at being safe.

--
Tim Wescott 
Wescott Design Services 
 Click to see the full signature
Reply to
Tim Wescott

Yup.

I've built fsf gnat for various targets: MIPS, ARM, x86, MSP430. No runtime though.

What target are you wanting?

Luke

Reply to
Luke A. Guest

Yes, I missed Jacob's original posting until I saw it quoted by you after I posted. :-(

Good luck doing that in a reliable way on ARM with gcc when you are accessing device registers.

If you access a bitfield in the lower 8 bits of a register, the gcc optimiser can turn that into 8-bit ldrb/strb opcodes instead of 32-bit ldr/str opcodes and thereby causing the program to break if you need to access your registers in units of greater than 8 bits.

Ada's not immune as well if you try accessing the registers directly using bitfields; I helped someone out in comp.lang.ada a few weeks ago with a similar issue.

BTW, I've got a couple of Ada Issues open which talk about this and the related issue of directly updating multiple bitfields as one Read/Modify/Write operation instead of as multiple RMW operations.

It would be nice to be able to do that in C as well instead of having to use an intermediate variable (as you currently have to do with Ada as well).

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP 
Microsoft: Bringing you 1980s technology to a 21st century world
Reply to
Simon Clubley

Currently PIC32 and ARM bare metal. If some things I am currently doing with the PIC32 work out, then I am going to forget about the PIC18 and drop it from the list of MCUs I am interested in.

The last time I looked at your work however, you were having real problems with things being fragile or simply breaking from gcc version to gcc version.

I know about Brian's work with the MSP430 stuff and I've used AVR-Ada in the past.

Given that RTEMS supports Ada, I've been toying with the idea of doing a RTEMS port for PIC32 in the future and seeing if Ada will be usable on it.

It doesn't address the bare metal issue but it might be worth exploring.

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP 
Microsoft: Bringing you 1980s technology to a 21st century world
Reply to
Simon Clubley

Sure, all other things being equal - but you are severely restricted as to your choice of MCU. I gather Simon was particularly interested in

32bit (cortex) parts so there the choice narrows from thousands to about 2.
--

John Devereux
Reply to
John Devereux

Ada and similar languages tend to both be easier to optimize and produce shorter faster code because they are better defined than C. The problem is historical. C as a language can't be cleaned up as tight as languages like Ada are defined. Optimization for Ada can be very aggressive because in general it lacks ambiguous definitions and "conventional wisdoms".

w..

Reply to
Walter Banks

That's a very interesting comment, because that's basically How It Is Done here, and I've had several years worth of success on both ST and TI/ Luminary devices. I'm not sure if that's because of the way I define my bitfields (absolutely everything is defined as a struct of bitfields, then a union of that struct and a 32-bit integer), or if I've been lucky, or what.

So it looks like:

struct SPeriphRegisterBits { unsigned int bit0 : 1; unsigned int bits1_10 : 10; unsigned int bit11 : 1; unsigned int : 16; unsigned int bits28_31 : 4; };

union UPerihRegister { struct SPeriphRegisterBits bits; unsigned int all; }

All of the various register definitions then get collected into a structure for the peripheral, which gets declared as "extern const", and defined in the linker command file.

It would probably require another built-in optimizer directive, like "volatile".

For that matter, unless you define your intermediate variable as volatile, I could easily see a read/modify/write cycle that only modifies the lower 8 bits come through as an 8-bit access.

Come to think of it (sorry for rambling, I'm just letting this stuff dribble out my brain as it comes to me) that may be what I'm doing with my elaborate struct -> union -> const struct construction. Whatever I'm doing, it works...

--
Tim Wescott 
Wescott Design Services 
 Click to see the full signature
Reply to
Tim Wescott

Am 23.09.2015 um 20:08 schrieb Tim Wescott:

Yes, you can. But unless you're also in charge of configuring or designing the C compiler for them to be used on, you can't rely on any particular mapping between the bit fields in a C struct and their physical address layout. That's because just about every single aspect of that mapping is implementation-defined (or worse). I.e. they're close to 100% unportable from one platform to the next. Even just flipping a single switch on the same compiler can completely jumble the entire, carefully tuned data layout.

Now that's not too bad for a micro controller's internal registers, because data structure definitions for those will most likely be supplied by the compiler makers themselves, anyway. Or if they're supplied by the chip maker, there'll be some ABI specification they enforce, which ties down all the loose ends.

But it is indeed quite bad concerning predefined data structures external to the CPU. Even some relatively experienced C programmers will believe they should use C structs and bit fields to write a driver module for an external device or, even more commonly, implement a communication protocol. Well, that won't work. And by the time they realize it, they will be so convinced of their original decision's correctness by "proof in the field", that they won't even be able to _see_ their error.

Absolutely.

Reply to
Hans-Bernhard Bröker

Do your MCUs _require_ register access in units of 16/32 bits ?

If so, you may be doing something which works for now, but may not work in the future.

It might be worth having a quick look with objdump just to be sure.

I have personally had this happen to me and it broke my code due to the register access size constraints no longer been true.

This is a bit more involved than what I was doing. I wonder if this is what is saving you for now.

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP 
Microsoft: Bringing you 1980s technology to a 21st century world
Reply to
Simon Clubley

For sure it's possible write buggy Ada code.

But also, some errors that can be made in C are detected as compile-time errors in Ada. A good static checker for C, or a good C compiler, could also detect many of those errors.

Perhaps these errors are of a kind that good and experienced C programmers have learned to avoid, so the benefit will vary.

For best results, the programmer must adopt an Ada mindset, which means lots of application- and subject-specific types. A program that uses Standard.Integer for all integer variables will not benefit from strong typing.

The Zeigler/Rational paper, which was referenced in other posts, shows lower bug densities for Ada code than C code, in that environment, in that application.

A good analogy :-)

Isn't that natural for embedded systems interacting with specific hardware and peripherals? Even for non-embedded systems on mainframes, at that time the more advanced OS services were often system-specific and easily made applications non-portable.

I believe that Ada supports portability better than C (assuming compilers exist...), but it is also easy to write unportable Ada. Portability takes an effort, less in Ada than in C, but still.

"Customer capture"... not limited to Ada, nor even to software...

Agreed, except that to me the difference is more than marginal.

--
Niklas Holsti 
Tidorum Ltd 
 Click to see the full signature
Reply to
Niklas Holsti

Here I go again, ruining a funny story by taking a factual view.

I enjoy satirical jokes about programming languages, but they must have some underlying truth in them.

For example, I remember a comparison of programming langages with car models (sorry -- can't find a reference, I think it was in ACM SIGPLAN Notices long ago), in which Ada was compared to an army-green Mercedes, with no options as to colour or features, with the comment "if it is good enough for the generals, it is good enough for you". APL was compared to a bus, where lots of passengers can travel at once, organized into rows and columns (of seats). Both comparisons are founded in true points: the fact that Ada was very standardized (even trademarked), and had its roots in the military; and the fact that APL works on matrix data.

But the shoot-in-foot joke that you quote has no factual basis, or the factual basis cannot be turned into a joke that satirizes Ada.

The US DoD was happy to have people use Ada -- they even tried to mandate it. So, no basis in fact.

Yes, Ada provides packages and concurrency. So?

That is a good, no? So where is the joke?

The Ada 2012 Reference Manual is 961 pages. The C11 Standard (N1570) is

701 pages. Not much difference, considering that Ada has many more features than C11 -- for one thing, a standard container library, which consumes about 100 pages of the Ada 2012 RM.

Perhaps the joke stems from the days when one would compare the Ada 83 Reference Manual with the K&R C book. That is a bit stale...

What is the point here? That C is less maintainable than Ada, as indeed suggested by the Zeigler/Rational study? But then it is not making a joke about Ada, is it?

It seems to me that this joke needs a bit of work :-)

--
Niklas Holsti 
Tidorum Ltd 
 Click to see the full signature
Reply to
Niklas Holsti

Here I go again, ruining a funny story by taking a factual view.

I enjoy satirical jokes about programming languages, but they must have some underlying truth in them.

For example, I remember a comparison of programming langages with car models (sorry -- can't find a reference, I think it was in ACM SIGPLAN Notices long ago), in which Ada was compared to an army-green Mercedes, with no options as to colour or features, with the comment "if it is good enough for the generals, it is good enough for you". APL was compared to a bus, where lots of passengers can travel at once, organized into rows and columns (of seats). Both comparisons are founded in true points: the fact that Ada was very standardized (even trademarked), and had its roots in the military; and the fact that APL works on matrix data.

But the shoot-in-foot joke that you quote has no factual basis, or the factual basis cannot be turned into a joke that satirizes Ada.

The US DoD was happy to have people use Ada -- they even tried to mandate it. So, no basis in fact -- really contradictory to fact.

Yes, Ada provides packages and concurrency. So?

That is a good, no? So where is the joke?

The Ada 2012 Reference Manual is 961 pages. The C11 Standard (N1570) is

701 pages. Not much difference, considering that Ada has many more features than C11 -- for one thing, a standard container library, which consumes about 100 pages of the Ada 2012 RM.

Perhaps the joke stems from the days when one would compare the Ada 83 Reference Manual with the K&R C book. That is a bit stale...

What is the point here? That C is less maintainable than Ada, as indeed suggested by the Zeigler/Rational study? But then it is not making a joke about Ada, is it?

It seems to me that this joke needs a bit of work :-)

--
Niklas Holsti 
Tidorum Ltd 
 Click to see the full signature
Reply to
Niklas Holsti

And that is just fine - you'll have* to adapt to an implementation dependent mapping in most cases using macros anyway. Within processor families - read endianness and what word sizes can be reliably read/written - the macros have to change too, so that's another mess :)

*although most support 32 or 8 bit access in all cases, leaving only endianness. And some FPGAs are more or less lenient about this.

In the past, I've built tools to manage all this for me, invoked as part of the make process. This for macro approaches as well as bitfields.

I.e. they're

No doubt. SO you have already committed to a nonportable thing - a register set - so a little more won't hurt too bad.

IMO the readability is worth it. I just like:

FPGA.reset = 1;

better than SET_FPGA_RESET(1);

:)

You can very nearly *always* use bitfields, but it takes a bit of care. Assuming you're talking about an FPGA register set as "external to the CPU", there's always an additional parcel of risk. but it's not hard to write a test driver that tests all the fields with the struct mapped to just-memory, with a dump of that memory after each set.

Then write a host machine script that connects to the target, executes this test, captures the output and verifies it for you.

I say this because the usual "use macros" approach has produced old and hard to find bugs that were left to me to find - although the use of a scripting language to write the macros and round-trip test them might be just as good.

TO wit:

#if _ARCH_ARM_7_ #include #endif

#if _ARCH_ARM_9_ #include #endif

etc etc.

--
Les Cargill
Reply to
Les Cargill

(snip)

I suspect that is true for all languages.

After not so long, you learn them, and avoid them, without even thinking about it.

I haven't done Ada programming, but I have worked with VHDL, which as I understand it, derives some features from Ada. After previously using verilog, I find some VHDL features that seem to be extra work for no advantage.

(snip)

-- glen

Reply to
glen herrmannsfeldt

A good C programmer never makes those kinds of mistakes. But he also uses extensive static error checking in order to catch those mistakes when he makes them.

So many C programmers compile without even the most basic warning flags enabled - and often in ANSI C mode which allows much looser (and almost inevitably, much buggier) code than C99. They use whatever defaults their point-and-drool IDE has given them, without understanding the process, reading the documentation, thinking, or making an attempt to be aware of what they are actually doing.

C as a language, and C compilers, are tools - you have to learn to use them well in order to get the best of them. Otherwise you are flailing around, hammering in your screws and hitting your nails with a screwdriver. I would argue that another reason why Ada programming leads to less bugs than the average C programmer is that when your company has spent multiple tens of kilodollars on the tools, you spend some time reading the documentation and figuring out how to use it properly.

Absolutely. And one can think in a similar way with C, but it is harder to do - and easier to cheat. (C++ gives much more opportunities for stronger typing than C - some better than Ada, some not as good.)

The trouble with C is that although you can use typedefs to make new types, it is hard to do so in a way that is both convenient and enforced by the tools.

For example, if you have a program that needs to work with distances that are sometimes in metres, sometimes in kilometres, you might do something like this:

typedef unsigned int kilometres; typedef unsigned int metres;

kilometres distanceA; metres distanceB;

That can make code clearer, and gives you the flexibility to change the underlying type later on. But it does not stop you writing

void test1(void) { distanceA = distanceB; }

With C, you can enforce the distinction between types by putting them inside a struct. (The Linux kernel does this for many types, though it uses macro accessors rather than static inline functions.)

typedef struct { unsigned int x; } kilometres; typedef struct { unsigned int x; } metres;

static inline metres kilometres_to_metres(kilometres km) { return (metres) { km.x * 1000 }; }

static inline kilometres metres_to_kilometres(metres m) { return (kilometres) { m.x / 1000 }; }

kilometres distanceA; metres distanceB;

void test2(void) { distanceA = metres_to_kilometres(distanceB); }

Now "distanceA = distanceB" is a compile-time error. But the cost is that your code is full of accessor functions.

Another possibility is to use Hungarian notation. By that I mean /real/ Hungarian notation, or "Application Hungarian notation", as developed by the Hungarian Simonyl himself - not that useless, ugly and irritating "Systems Hungarian notation" (the nonsense of calling an unsigned character variable ucVar, etc.) that Microsoft started using after they employed Simonyl then totally misunderstood what he had been saying.

With Hungarian notation, you have:

typedef unsigned int distance;

distance km_distanceA; distance m_distanceB;

void test3(void) { km_distanceA = m_distanceB; }

The compiler can't help you spot the mistake, but humans can easily spot it - the prefixes make it clear you are mixing kilometres and metres.

(Of course, this can be combined with the struct technique above if it makes code clearer.)

In C++11, it is possible to wonderful things here. (If you use C++, make sure it is C++11, or C++14, because there are several key new features that make it a vastly superior language.)

class Metre; class Kilometre { public: using rawT = unsigned int; private: rawT x; explicit constexpr Kilometre(rawT y) : x(y) {}; public: constexpr rawT get() { return x; } explicit constexpr Kilometre() : x(0) {}; constexpr operator Metre(); friend constexpr Kilometre operator "" _km(unsigned long long int y); friend class Metre; };

inline constexpr Kilometre operator "" _km(unsigned long long int y) { return Kilometre(y); }

class Metre { public: using rawT = unsigned int; private: rawT x; explicit constexpr Metre(rawT y) : x(y) {}; public: constexpr rawT get() { return x; } explicit constexpr Metre() : x(0) {}; explicit constexpr operator Kilometre(); friend constexpr Metre operator "" _m(unsigned long long int y); friend class Kilometre; };

inline constexpr Metre operator "" _m(unsigned long long int y) { return Metre(y); }

inline constexpr Kilometre::operator Metre() { return Metre(x * 1000); }

inline constexpr Metre::operator Kilometre() { return Kilometre(x / 1000); }

Kilometre distanceA; Metre distanceB;

void test3(void) { distanceA = 10_km; distanceA = (Kilometre) 10000_m; distanceB = 10_m; distanceB = 10_km; distanceA = static_cast(distanceB); distanceA = (Kilometre) distanceB; distanceB = distanceA; }

Here I have defined the conversion from Metre to Kilometre as "explicit" (new to C++11), because it is unsafe and leads to loss of data, but the conversion the other way is safe and can be done implicitly in the program. Thus "distanceA = distanceB" requires a cast, while "distanceB = distanceA" is safe and does the conversion.

Ada will let you put together distance types with basically similar rules, and may involve less typing to get them, but I don't think Ada can get any safer than the C++ here. (Ada experts are encouraged to confirm or deny this.)

Remember that this was 20 years ago - C99 is a better and (somewhat) safer language than ANSI C was, and C tools are a good deal better (20 years ago, using struct wrappers in C for type safety meant lots of macros and probably very inefficient generated code. These days you use static inline functions that are nicer than macros, and the compiler will generate efficient code). And as shown above, the modern alternative to Ada for safe programming is arguably C++ rather than C.

True. At at that time, compilers were not as good as they are now - if you wanted efficient code, you generally had to write a lot more in a system-specific manner.

I don't see Ada and C's portability being significantly different (at least not after C99 with the types). It is mainly a matter of how the programmer structures the code.

Reply to
David Brown

The best ones have a ring of truth.

I don't think these jokes are trying to be that clever - perhaps you are over analysing them. (However, it's good that this thread has someone with strong Ada experience to comment. My own experience is very minimal - not much beyond "Hello, world!", and I read a book about algorithms for language design that happened to use Ada as the main implementation language.)

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.