-- The "high-level" definition of a record:
type Count7_t is mod 128;
type BitField_t is
record
Enable : Boolean;
Count : Count7_t;
Status : Natural range 0..255;
end record;
-- Now describe how it's layed out at the bit level:
for BitField_t use
record
Enable at 0 range 0..0;
Count at 0 range 1..7;
Status at 0 range 8..15;
end record;
-- Finally, ensure it's exactly 16-bit wide:
for BitField_t'Size use 16;
-- Define endian-ness:
for BitField_t'Bit_Order use Low_Order_First;
subtype Byte_t is Natural range 0..255; -- There you have your [0..255] unsigned integer. But that's not all...
-- As is, a Byte_t can't be greater than 255. It won't roll-over. It will raise an error if you attempt to increment one that is 255.
(Isn't that exactly what crashed Ariane, too? An overflow exception in code where no one thought they needed to care?)
Isn't that exactly what crashed Ariane, too? An overflow exception in code where no one thought they needed to care?
Quotesubtype Byte_t is Natural range 0..255; -- There you have your [0..255] unsigned integer. But that's not all...
-- As is, a Byte_t can't be greater than 255. It won't roll-over. It will raise an error if you attempt to increment one that is 255.
You're saying that by default, Ada won't let you do math on 8bit variables without checking each result for overflow?
(unless your declaration gets more complicated...)
Ouch? (I mean, I guess you define types the way you want them to behave, and then just use those all the time. But still - ouch!)
(Isn't that exactly what crashed Ariane, too? An overflow exception in code where no one thought they needed to care?)
Quotesubtype Byte_t is Natural range 0..255; -- There you have your [0..255] unsigned integer. But that's not all...
-- As is, a Byte_t can't be greater than 255. It won't roll-over. It will raise an error if you attempt to increment one that is 255.
You're saying that by default, Ada won't let you do math on 8bit variables without checking each result for overflow?
(unless your declaration gets more complicated...)
You think the type definition I gave just below this is complicated?
It's not a matter of being complicated? It's just what a "range" is about. Basic language stuff. If said variable gets out of its range and that can be caught at compile-time, you'll get a compiler error or warning. If it can't, and that happens at run-time, you'll get an exception. Ranges add constraints, why would you want them to basically get ignored?
Again if you want an 8-bit type that behaves as you probably expect as a low-level programmer, just declare it as "mod 256". Make sure this is what you really want, because in real programs, assigning a value greater than 255 to an 8-bit variable, unless you want it truncated on PURPOSE, is really bad, and C in that respect has only very limited abilities to detect it (assigning from a larger width integer to a smaller one without a cast will get you a warning, provided you enabled the corresponding warning... which is often not the case by default. Now that is ouch, and very commonly seen in C code.)
And as I stated after, ranges and size are two different things in Ada. Just because you declared a range of 0..255 doesn't mean you'll directly get an 8-bit variable either. You'll just get an integer the acceptable values of which are in this range. The size, I think, would depend on target and compiler decisions by default, unless you give it a clear spec with the "Size" attribute.Ouch? (I mean, I guess you define types the way you want them to behave, and then just use those all the time. But still - ouch!)
Not sure what the ouch is about actually. If you define constrained types, you'll get the constraints that go with them instead of just getting random/"implementation-defined" stuff as often happens in C.
See above once again. If you want an 8-bit type that rolls over by itself, declare it "mod 256".
If OTOH, you'd like specific features, such as having the value "capped" to the minimum or maximum of its range instead of rolling over, you'll have to define a specific type, overloading the base operators (yes Ada is also an OO language.) Really not that complicated either.(Isn't that exactly what crashed Ariane, too? An overflow exception in code where no one thought they needed to care?)
I don't quite remember, but any unexpected overflow can lead to catastrophic results. It's kind of weird to think that not taking any action when one occurs would be better than raising an exception.
Of course you have to handle exceptions properly.
Had it not raised an exception, it could have led to bogus results, possibly controlling a key element of the system unexpectedly and make it crash as well.
The problem here was "no one thought they needed to care". Not that an exception was raised, or the language!
Some people may prefer programming in a "I don't know for sure what will happen in some cases, but hey, it compiles".
I remember we had a team joke in a job years ago. One embedded developer, when asked if his code was ready to be integrated, would sometimes say: "it compiles, so it must work". We ended up using this as some kind of meme when we needed a laugh.
Of course ranges, and parameters integrity in general, can be enforced in other languages including C. You'll just have to do this manually, which can get tedious (but has also distinct advantages, I talked about parameters/variable checking in another thread...)
As to the merits of exceptions themselves, which are nothing specific to Ada, but are commonly implemented in most high-level languages these days (except C, and if I got it right, Rust?), this is yet another debate, and would actually be an interesting one.
static uint8_t blinkCounter = 0x00;
/* This is called on every loop in main() */
void MyApplication::LoopFunc(void)
{
blinkCounter++;
if(blinkCounter % 3)
*port_io1 = 0U;
else
*port_io1 = 1U;
}
void main(void)
{
TRISA = 0x00;
ANSEL = 0x0000;
LATA = 0x00;
MyApplication app = MyApplication(&LATA);
app.SetupFunc();
while(1U)
{
app.LoopFunc();
if(app.ShouldExitLoop())
break;
}
}
It's ofen useful to ignore variable range (allow overflow) in order to do fun bit hacks. For example blink LED every three iterations of main loop (software PWM):
-- Assuming your blink counter is only used in the way you showed:
type BlinkCounter_t is mod 3 with Default_Value => 0; -- The good thing with default values is that you don't need to actually initialize any variable of this type
...
blinkCounter : BlinkCounter_t ;
...
blinkCounter := blinkCounter + 1;
if blinkCounter /= 0 then
...
else
...
end if;
...
type BlinkCounter_t is mod 256 with Default_Value => 0;
...
-- The 'if' is now:
if (blinkCounter mod 3) /= 0 then
....
Why not hate C? Because I seriously *hate* people not using braces after an if-statement
if (expr) { foo(); }
Today I spent something like 3 hours at debugging u-boot for a *stupid* bug caused by someone who hadn't had included within braces the statement following "if" by a single statement and ... when his colleague applied the patch, it added something in the wrong way
It's just what a "range" is about.
Unsigned overflow works normally in C/C++.
Signed overflow is undefined behavior, meaning the compiler is free to replace your whole code with one drawing an ASCII d*ck on the screen and that's standard-compliant.
nonsense. Signed overflow is a runtime condition, so the compiler can't detect it. I suppose that it could specifically detect a signed overflow and choose to do something weird, but...
What currently is observed, is that compilers generate code that happily calculates an invalid value if a signed overflow occurs. Since very often that value is later converted to unsigned range AND because two’s complement is widely used(1) AND most processors survive signed overflows(2), the final result “magically” matches the expected outcome. But that is a pure coincidence — it is not determined by what is written in the code.
Signed overflow is a runtime condition, so the compiler can't detect it. I suppose that it could specifically detect a signed overflow and choose to do something weird, but...
In most cases we improve GCC to exploit well defined behaviors of the standard. In this case we created defined __builtin_constant_p with insufficient documentation to allow a user to reasonably predict the surprising behavior shown in this testcase.
GCC has created a path which will never be executed and used that to introduce a constant which does not exist in the source. Unless you know what jump-threading can do, this transformation isn't obvious.
gcc-7 has an "optimization" pass that completely screws up, and generates the code expansion for the (impossible) case of calling ilog2() with a zero constant, even when the code gcc compiles does not actually have a zero constant.
And we try to generate a compile-time error for anybody doing ilog2() on a constant where that doesn't make sense (be it zero or negative). So now gcc7 will fail the build due to our sanity checking, because it created that constant-zero case that didn't actually exist in the source code.
There's a whole long discussion on the kernel mailing about how to work around this gcc bug. The gcc people themselevs have discussed their "feature" in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=72785
but it's all water under the bridge, because while it looked at one point like it would be solved by the time gcc7 was released, that was not to be.
So now we have to deal with this compiler braindamage.
And the only simple approach seems to be to just delete the code that tries to warn about bad uses of ilog2().
So now "ilog2()" will just return 0 not just for the value 1, but for any non-positive value too.
It's not like I can recall anybody having ever actually tried to use this function on any invalid value, but maybe the sanity check just meant that such code never made it out in public.
What happened in C11?
I guess I'm more boggled by defining your single-byte variables as "ranges" in the first place. Is "for Byte_t'Size use 8;" a modifier, or a replacement for the "range"?
What happens with "subtype Byte_t is integer; for Byte_t'Szie use 8'" ?
QuoteUnsigned overflow works normally in C/C++.
Signed overflow is undefined behavior, meaning the compiler is free to replace your whole code with one drawing an ASCII d*ck on the screen and that's standard-compliant.nonsense. Signed overflow is a runtime condition, so the compiler can't detect it. I suppose that it could specifically detect a signed overflow and choose to do something weird, but...
A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type."
For starters, C++ stuff included, Microsoft-only (optional) "safe" I/O variants added, but not even getline() from POSIX.1. The committee is now stuffed with MS stooges, whose main focus is making sure MS can keep treating C as a subset of C++ and does not have to change their product. The C language itself is not making any progress, unless you consider pushing for C to become a strict subset of C++ progress.
See, I've helped C programmers for years, and know how useful it would be for learners to use a subset of POSIX.1 in the learning process. The key things are getline() (unlimited line lengths),
nftw() (directory tree exploration), fnmatch() and glob() (file name matching and globbing),
regcomp()/regexec() (regular expressions for text matching),
Unless something changes drastically about the C standard committee, the C language itself is fucked: it is destined to become a strict subset of C++ because a single vendor finds it suits their product better, and has spent enough money to stuff the committee.
...it’s because the language is low-level, but in fact it doesn’t correspond to the real hardware in 2019. It’s still PDP-11, relaxed a bit to allow different platforms.
That also applies to the filesystem-related suggestions. What would be the use of those in a C program? Those belong to the business logic, not interfacing system or writing firmware for a microcontroller, and they have limited use even in general. Do not confuse your specific domain with the whole world.
only defined in terms of a hosted environment