Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.arch.embedded > #30516
| From | Niklas Holsti <niklas.holsti@tidorum.invalid> |
|---|---|
| Newsgroups | comp.arch.embedded |
| Subject | Re: C++, Ada, ... |
| Date | 2021-04-19 19:16 +0300 |
| Organization | Tidorum Ltd |
| Message-ID | <ie5onhF7e7mU1@mid.individual.net> (permalink) |
| References | (3 earlier) <s5h32f$edv$1@dont-email.me> <ie32luFlvghU1@mid.individual.net> <s5hogv$77o$1@dont-email.me> <ie55l3F3ou7U1@mid.individual.net> <s5jslh$gl8$1@dont-email.me> |
On 2021-04-19 15:22, David Brown wrote:
> On 19/04/2021 12:51, Niklas Holsti wrote:
>> On 2021-04-18 19:59, David Brown wrote:
>>> On 18/04/2021 17:48, Niklas Holsti wrote:
>>>> On 2021-04-18 13:53, David Brown wrote:
>>>>> On 17/04/2021 20:55, Tom Gardner wrote:
>>>>>> On 17/04/21 17:45, David Brown wrote:
>>>>>>> And most of
>>>>>>> the advantages of Ada (such as better typing) can be achieved in C++
>>>>>>> with less effort, and at the same time C++ can give additional
>>>>>>> safety on
>>>>>>> resources that is harder to get on Ada.
>>>>>>
>>>>>> Interesting. Could you give a quick outline of that?
>>>>>>
>>>>>
>>>>> Which part?
>>>>>
>>>>> My understanding of Ada classes is that, like Pascal classes, you need
>>>>> to explicitly construct and destruct objects. This gives far greater
>>>>> scope for programmers to get things wrong than when they are handled
>>>>> automatically by the language.
>>>>
>>>>
>>>> If you mean automatic allocation and deallocation of storage, Ada lets
>>>> you define types that have an "initializer" that is called
>>>> automatically, and can allocate memory if it needs to, and a "finalizer"
>>>> that is automatically called when leaving the scope of the object in
>>>> question. The finalizer does have to explicitly deallocate any
>>>> explicitly allocated and owned resources, and it may have to use
>>>> reference counting for that, for complex data structures.
>>>
>>> I had a little look for this (these discussions are great for inspiring
>>> learning!). The impression I got was that it was possible, but what
>>> takes a few lines of C++ (excluding whatever work must be done inside
>>> the constructor and destructor bodies) involves inheriting from a
>>> specific library type.
>>
>>
>> Yes, you must inherit from one of the types Ada.Finalization.Controlled
>> or Ada.Finalization.Limited_Controlled when you create a type for which
>> you can program an initializer and/or a finalizer.
>>
>> However, you can aggregate a component of such a type into some other
>> composite type, and then that component's initializer and finalizer will
>> be called automatically when any object of the containing composite type
>> is constructed and destroyed.
>>
>>
>>> And you don't have automatic initialisation of
>>> subobjects and ancestors in a controlled order, nor automatic
>>> finalisation of them in the reverse order.
>>
>>
>> No, and yes. Subobjects (components) are automatically initialized
>> before the composite is initialized (bottom-up), and are automatically
>> finalized after the composite is finalized (top-down). But there is no
>> automatic invocation of the initializer or finalizer of the parent
>> class; that would have to be called explicitly (except in the case of an
>> "extension aggregate" expression, where an object of the parent type is
>> created and then extended to an object of the derived class).
>>
>
> OK. Am I right in assuming the subobjects here also need to inherit
> from the "Finalization" types individually, in order to be automatically
> initialised in order?
Yes, if they need more initialization than provided by the automatic
"construction" step (Default_Value etc.)
>
> Are there any overheads (other than in the source code) for all this
> inheriting? Ada (like C++) aims to be minimal overhead, AFAIUI, but its
> worth checking.
>
If the compiler sees the actual type of an object that needs
finalization (as in the critical-section example) it can generate direct
calls to Initialize and Finalize without dispatching. If the object is
polymorphic (what in Ada is called a "class") the calls must go through
a dispatch table according to the actual type of the object.
>> So the Ada "initializer" is not like a C++ constructor, which in Ada
>> corresponds more closely to a function returning an object of the class.
>>
>> An Ada "finalizer" is more similar to a C++ destructor, taking care of
>> any clean-up that is needed before the object disappears.
>>
>
> C++ gives you the choice. You can do work in a constructor, or you can
> leave it as a minimal (often automatically generated) function. You can
> give default values to members. You can add "initialise" member
> functions as you like. You can have "factory functions" that generate
> instances. This lets you structure the code and split up functionality
> in whatever way suits your requirements.
So, just as in Ada.
> For a well-structured class, the key point is that a constructor will
> always establish the class invariant. Any publicly accessible function
> will assume that invariant, and maintain it. Private functions might
> temporarily break the invariant - these are only accessible by code that
> "knows what it is doing". And the destructor will always clean up after
> the object, recycling any resources used.
>
> Having C++ style constructors are not a requirement for having control
> of the class invariant, but they do make it more convenient and more
> efficient (both at run-time and in the source code) than separate
> minimal constructors (or default values) and initialisers.
In Ada one can write the preconditions, invariants and postconditions in
the source code itself, with standard "aspects" and Ada Boolean
expressions, and have them either checked at run-time or verified by
static analysis/proof.
>>> Let's take a little example. And since this is comp.arch.embedded,
>>> let's take a purely embedded example of disabling interrupts, rather
>>> than shunned dynamic memory allocations:
>>>
>>> static inline uint32_t disableGlobalInterrupts(void) {
>>> uint32_t pri;
>>> asm volatile(
>>> " mrs %[pri], primask\n\t" // Get old mask
>>> " cpsid i\n\t" // Disable interrupts entirely
>>> " dsb" // Ensures that this takes effect before next
>>> // instruction
>>> : [pri] "=r" (pri) :: "memory");
>>> return pri;
>>> }
>>>
>>> static inline void restoreGlobalInterrupts(uint32_t pri) {
>>> asm volatile(
>>> " msr primask, %[pri]" // Restore old mask
>>> :: [pri] "r" (pri) : "memory");
>>> }
>>
>>
>> I won't try to write Ada equivalents of the above :-) though I have of
>> course written much Ada code to manage and handle interrupts.
>
> I think Ada has built-in (or standard library) support for critical
> sections, does it not?
Yes, "protected objects". See below.
> But this is just an example, not necessarily
> something that would be directly useful. Obviously the code above is
> highly target-specific.
>
>>
>>
>>> class CriticalSectionLock {
>>> private :
>>> uint32_t oldpri;
>>> public :
>>> CriticalSectionLock() { oldpri = disableGlobalInterrupts(); }
>>> ~CriticalSectionLock() { restoreGlobalInterrupts(oldpri); }
>>> };
>>
>>
>> Here is the same in Ada. I chose to derive from Limited_Controlled
>> because that makes it illegal to assign a Critical_Section value from
>> one object to another.
>>
>> -- Declaration of the type:
>>
>> type Critical_Section is new Ada.Finalization.Limited_Controlled
>> with record
>> old_pri : Interfaces.Unsigned_32;
>> end record;
>>
>> overriding procedure Initialize (This : in out Critical_Section);
>> overriding procedure Finalize (This : in out Critical_Section);
>>
>> -- Implementation of the operations:
>>
>> procedure Initialize (This : in out Critical_Section)
>> is begin
>> This.old_pri := disableGlobalInterrupts;
>> end Initialize;
>>
>> procedure Finalize (This : in out Critical_Section)
>> is begin
>> restoreGlobalInterrupts (This.old_pri);
>> end Finalize;
>>
>>
>
> Are "Initialize" and "Finalize" overloaded global procedures, or is this
> the syntax always used for member functions?
They are operations ("member functions") of the
Ada.Finalization.Limited_Controlled type, that are null (do nothing) for
that (base) type, and here we override them for the derived
Critical_Section type, to replace the inherited null operations.
The "overriding" keyword is optional (an unfortunate wart from history).
>>> You can use it like this:
>>>
>>> bool compare_and_swap64(uint64_t * p, uint64_t old, uint64_t x)
>>> {
>>> CriticalSectionLock lock;
>>>
>>> if (*p != old) return false;
>>> *p = x;
>>> return true;
>>> }
>>>
>>
>>
>> function Compare_and_Swap64 (
>> p : access Interfaces.Unsigned_64;
>> old, x : in Interfaces.Unsigned_64)
>> return Boolean
>> is
>> Lock : Critical_Section;
>> begin
>> if p.all /= old then
>> return False;
>> else
>> p.all := x;
>> return True;
>> end if;
>> end Compare_and_Swap64;
>>
>> (I think there should be a "volatile" spec for the "p" object, don't you?)
>
> It might be logical to make it volatile, but the code would not be
> different (the inline assembly has memory clobbers already, which force
> the memory accesses to be carried out without re-arrangements).
So you are relying on the C++ compiler actually respecting the "inline"
directive? Are C++ compilers required to do that?
> But adding "volatile" would do no harm, and let the user of the
> function pass a volatile pointer. >
> The Ada and C++ code is basically the same here, which is nice.
(Personally I dislike this style of critical sections. I think it is a
confusing mis-use of local variables. Its only merit is that it ensures
that the finalization occurs even in the case of an exception or other
abnormal exit from the critical section.)
> How would it look with block scope?
>
> extern int bar(int x);
> int foo(volatile int * p, int x, int y) {
> int u = bar(x);
> {
> CriticalSectionLock lock;
> *p += z;
> }
> int v = bar(y);
> return v;
> }
>
> The point of this example is that the "*p += z;" line should be within
> the calls to disableGlobalInterrupts and restoreGlobalInterrupts, but
> the calls to "bar" should be outside. This requires the lifetime of the
> lock variable to be more limited.
Much as you would expect; the block is
declare
Lock : Critical_Section;
begin
p.all := p.all + z:
end:
(In the next Ada standard -- probably Ada 2022 -- one can write such
updating assignments more briefly, as
p.all := @ + z;
but the '@' can be anywhere in the right-hand-side expression, in one or
more places, which is more flexible than the C/C++ combined
assignment-operations like "+=".)
>> But critical sections in Ada applications are more often written
>> using the Ada "protected object" feature.
>>
>> <snip PO example> >>>
> I think the idea of language support for protected sections is nice, but
> I'd be concerned about how efficiently it would map to the requirements
> of the program and the target.
I haven't had any problems so far. Though in some highly stressed
real-time applications I have resorted to communicating through shared
atomic variables with lock-free protocols.
> Such things are often a bit "brute
> force", because they have to emphasise "always works" over efficiency.
> For example, if you have a 64-bit atomic type (on a 32-bit device), you
> don't /always/ need to disable interrupts around it. If you are already
> in an interrupt routine and know that no higher priority interrupt
> accesses the data, no locking is needed.
Interrupt handlers in Ada are written as procedures in protected
objects, with the protected object given the appropriate priority. Other
operations in that same protected object can then be executed in
automatic mutual exclusion with the interrupt handler. The protected
object can also provide one or more "entry" operations with Boolean
"guards" on which tasks can wait, for example to wait for an interrupt
to have occurred. I find this works very well in practice.
> If you are in main thread code but only read the data, maybe
> repeatedly reading it until you get two reads with the same value is
> more efficient. Such shortcuts must, of course, be used with care.
Sure.
> In C and C++, there are atomic types (since C11/C++11). They require
> library support for different targets, which are (unfortunately) not
> always good. But certainly it is common in C++ to think of an atomic
> type here rather than atomic access functions, just as you describe in Ada.
The next Ada standard includes several generic atomic operations on
typed objects in the standard package System.Atomic_Operations and its
child packages. The proposal is at
http://www.ada-auth.org/standards/2xaarm/html/AA-C-6-1.html
Follow the "Next" arrows to see all of it.
In summary, it seems to me that the main difference we have found in
this discussion, so far, between Ada and C++ services in the areas we
have looked at, is that Ada makes it simpler to define one's own scalar
types, while C++ has more compile-time programmability like constexpr
functions.
Back to comp.arch.embedded | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread
C++, Ada, ... pozz <pozzugno@gmail.com> - 2021-04-17 17:48 +0200
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-17 18:45 +0200
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-17 19:55 +0100
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-18 12:53 +0200
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-18 18:48 +0300
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-18 18:59 +0200
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-19 13:51 +0300
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 14:22 +0200
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-19 19:16 +0300
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 22:19 +0200
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-20 01:03 +0300
Re: C++, Ada, ... Richard Damon <Richard@Damon-Family.org> - 2021-04-19 18:09 -0400
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-20 06:58 +0200
Re: C++, Ada, ... Paul Rubin <no.email@nospam.invalid> - 2021-04-19 23:18 -0700
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-20 09:36 +0200
Re: C++, Ada, ... Paul Rubin <no.email@nospam.invalid> - 2021-04-19 12:20 -0700
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-20 00:26 +0300
Re: C++, Ada, ... Paul Rubin <no.email@nospam.invalid> - 2021-04-19 16:01 -0700
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-20 10:47 +0300
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-19 23:30 +0100
Re: C++, Ada, ... Clifford Heath <no.spam@please.net> - 2021-04-18 08:29 +1000
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-18 10:39 +0300
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-18 14:59 +0200
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-18 19:23 +0300
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-18 19:26 +0200
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-18 19:29 +0100
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-18 22:23 +0200
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-18 23:09 +0100
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 08:56 +0200
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-19 14:04 +0300
Re: C++, Ada, ... Dimiter_Popoff <dp@tgi-sci.com> - 2021-04-19 15:16 +0300
Re: C++, Ada, ... Paul Rubin <no.email@nospam.invalid> - 2021-04-19 12:47 -0700
Re: C++, Ada, ... Niklas Holsti <niklas.holsti@tidorum.invalid> - 2021-04-20 00:37 +0300
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-20 07:50 +0200
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 14:26 +0200
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-18 19:30 +0100
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-18 22:17 +0200
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-18 23:17 +0100
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 08:52 +0200
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-19 10:08 +0100
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 12:15 +0200
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-19 12:00 +0100
Re: C++, Ada, ... Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2021-04-19 16:15 +0300
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 15:43 +0200
Re: C++, Ada, ... pozz <pozzugno@gmail.com> - 2021-04-19 18:38 +0200
Re: C++, Ada, ... Paul Rubin <no.email@nospam.invalid> - 2021-04-19 10:48 -0700
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-19 19:33 +0100
Re: C++, Ada, ... Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2021-04-20 15:09 +0300
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-20 15:29 +0100
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-19 22:48 +0200
Re: C++, Ada, ... pozz <pozzugno@gmail.com> - 2021-04-19 23:17 +0200
Re: C++, Ada, ... Paul Rubin <no.email@nospam.invalid> - 2021-04-19 15:55 -0700
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-20 08:21 +0200
Re: C++, Ada, ... David Brown <david.brown@hesbynett.no> - 2021-04-20 08:20 +0200
Re: C++, Ada, ... Dave Nadler <drn@nadler.com> - 2021-04-17 21:07 -0400
Re: C++, Ada, ... Don Y <blockedofcourse@foo.invalid> - 2021-04-17 18:53 -0700
Re: C++, Ada, ... Tom Gardner <spamjunk@blueyonder.co.uk> - 2021-04-18 09:42 +0100
Re: C++, Ada, ... dalai lamah <antonio12358@hotmail.com> - 2021-04-18 14:39 +0200
csiph-web