* I'm facing an issue with: call to abstract procedure must be dispatching @ 2015-12-08 16:45 Serge Robyns 2015-12-08 16:59 ` G.B. ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Serge Robyns @ 2015-12-08 16:45 UTC (permalink / raw) Gents, I've been trying to get my head around a way to create a solution where the storage method can be changed. For this I'm using Ada's interface language construct. So far I've only be successful with the following construct, whereas store1, store2, etc are implementations to store object1, object2, etc. These are created as child packages of their respective objects so they can access the private parts. type T_Abstract_Data_Store is limited interface and Store1_Interface and Store2_Interface ... In the implementation "class" type T_Data_Store is limited new T_Abstract_Data_Store with record Store1 : T_Store1; Store2 : T_Store2; ... This then requires to write glue code to implement a link to the different implementations of the stores. procedure Insert (Store : in out T_Data_Store; Obj : in T_XYZ'Class) is begin Store.Store1.Insert (Obj); end Insert; This could have been avoided if Ada would have allowed some form of multi-inheritance of in the instance of a multi-interface "class". Now I've been trying to resolve this through another mean but I'm now facing the "call to abstract procedure must be dispatching" error message which I cannot explain nor resolve. Below an a sample compile-able piece of code. package Clients is -- could also be in private part .... type T_Client is tagged record ID : Natural; end record; end Clients; with Clients; use Clients; package Abstract_Store is type T_Asbtract_Store is limited interface; procedure Insert (Store : in out T_Asbtract_Store; Client : in T_Client'Class) is abstract; -- actually in my code Client will be a subclass of T_Client, for example -- T_Client_DAO'Class end Abstract_Store; with Clients; use Clients; with Abstract_Store; use Abstract_Store; procedure test is type T_Store is new T_Asbtract_Store with null record; overriding procedure Insert (Store : in out T_Store; Client : in T_Client'Class) is begin null; end Insert; A_Store : access T_Asbtract_Store; The_Store : aliased T_Store; Client : T_Client; begin A_Store := T_Asbtract_Store (The_Store)'Access; A_Store.Insert (Client); end test; The compiler does not like "A_Store.Insert (Client)". Also with this construct I've to use an access variable ... Anyone can shed a light on this or suggest a smarter Ada way? Regards, Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns @ 2015-12-08 16:59 ` G.B. 2015-12-08 17:04 ` Dmitry A. Kazakov 2015-12-08 17:30 ` Jeffrey R. Carter 2 siblings, 0 replies; 34+ messages in thread From: G.B. @ 2015-12-08 16:59 UTC (permalink / raw) On 08.12.15 17:45, Serge Robyns wrote: > procedure test is > > type T_Store is new T_Asbtract_Store with null record; > > overriding procedure Insert (Store : in out T_Store; > Client : in T_Client'Class) is > begin > null; > end Insert; > > A_Store : access T_Asbtract_Store; > The_Store : aliased T_Store; > Client : T_Client; > > begin > A_Store := T_Asbtract_Store (The_Store)'Access; > A_Store.Insert (Client); > end test; > > The compiler does not like "A_Store.Insert (Client)". > Also with this construct I've to use an access variable ... > > Anyone can shed a light on this or suggest a smarter Ada way? Just dispatch: procedure test is type T_Store is new T_Asbtract_Store with null record; overriding procedure Insert (Store : in out T_Store; Client : in T_Client'Class) is begin null; end Insert; procedure Wanting_Some_Store (A_Store : in out T_Asbtract_Store'Class; A_Client : T_client) is begin A_Store.Insert (A_Client); end Wanting_Some_Store; Client : T_Client; The_Store : T_Store; begin Wanting_Some_Store (The_Store, Client); end test; ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns 2015-12-08 16:59 ` G.B. @ 2015-12-08 17:04 ` Dmitry A. Kazakov 2015-12-08 17:24 ` Serge Robyns 2015-12-08 17:30 ` Jeffrey R. Carter 2 siblings, 1 reply; 34+ messages in thread From: Dmitry A. Kazakov @ 2015-12-08 17:04 UTC (permalink / raw) On Tue, 8 Dec 2015 08:45:52 -0800 (PST), Serge Robyns wrote: [...] > procedure test is > type T_Store is new T_Asbtract_Store with null record; > overriding procedure Insert (Store : in out T_Store; > Client : in T_Client'Class) is > begin > null; > end Insert; > > A_Store : access T_Asbtract_Store; > The_Store : aliased T_Store; > Client : T_Client; > begin > A_Store := T_Asbtract_Store (The_Store)'Access; > A_Store.Insert (Client); > end test; No idea what you are trying to achieve, but the above should have been: The_Store : aliased T_Store; Client : T_Client; begin The_Store.Insert (Client); -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 17:04 ` Dmitry A. Kazakov @ 2015-12-08 17:24 ` Serge Robyns 2015-12-08 17:42 ` Dmitry A. Kazakov 0 siblings, 1 reply; 34+ messages in thread From: Serge Robyns @ 2015-12-08 17:24 UTC (permalink / raw) On Tuesday, 8 December 2015 18:05:09 UTC+1, Dmitry A. Kazakov wrote: > The_Store : aliased T_Store; > Client : T_Client; > begin > The_Store.Insert (Client); Yes, I know this does work. I did compile the example first like that. But this is an attempt to "simplify" the problem to post it here. What really happens is the following. In the main package I'm actually calling other packages passing the The_Store. For example: -- with Store.Gnatcoll_version; with Store.Inmemory_Container_version; Both implement the same interface but with different underlying storage. In the case of in_memory, I'll keep containers inside the type T_Data_Store and in case of Gnatcoll some DB parameters inside T_Data_Store and then ... Do_Something (The_Store); The signature of Do_Something is procedure Do_Something (A_Store : in out T_Abstract_Store); Inside that procedure I'm using the A_Store. This regardless how A_Store got implemented. However all "users" will just call "A_Store.Insert (Something);" or "Something := A_Store.Get (ID);", etc. If I've to use the "real" instance, I've to edit all packages that "with"'s depending on the case. In some languages this could controlled by a pre-processor directive. Any other suggestion? Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 17:24 ` Serge Robyns @ 2015-12-08 17:42 ` Dmitry A. Kazakov 2015-12-08 18:00 ` Serge Robyns 0 siblings, 1 reply; 34+ messages in thread From: Dmitry A. Kazakov @ 2015-12-08 17:42 UTC (permalink / raw) On Tue, 8 Dec 2015 09:24:58 -0800 (PST), Serge Robyns wrote: > On Tuesday, 8 December 2015 18:05:09 UTC+1, Dmitry A. Kazakov wrote: > >> The_Store : aliased T_Store; >> Client : T_Client; >> begin >> The_Store.Insert (Client); > > Yes, I know this does work. I did compile the example first like that. > But this is an attempt to "simplify" the problem to post it here. What > really happens is the following. > > In the main package I'm actually calling other packages passing the > The_Store. For example: > > -- with Store.Gnatcoll_version; > with Store.Inmemory_Container_version; > > Both implement the same interface but with different underlying storage. > In the case of in_memory, I'll keep containers inside the type T_Data_Store > and in case of Gnatcoll some DB parameters inside T_Data_Store > and then ... > > Do_Something (The_Store); > > The signature of Do_Something is > procedure Do_Something (A_Store : in out T_Abstract_Store); In which relation is Do_Something to T_Abstract_Store? 1. a primitive operation = has a separate implementation for each concrete type (from the class) 2. class-wide operation = has same implementation for all types (from the class) (anything else is a design fault) > Inside that procedure I'm using the A_Store. This regardless how A_Store > got implemented. That sounds like #2. Then it must be declared as: procedure Do_Something (A_Store : in out T_Abstract_Store'Class); > However all "users" will just call "A_Store.Insert (Something);" or > "Something := A_Store.Get (ID);", etc. You lost me here. What has this to do with Do_Something? > If I've to use the "real" instance, I've to edit all packages that > "with"'s depending on the case. In some languages this could controlled > by a pre-processor directive. Any other suggestion? I don't understand the problem. You are trying to explain a [failed] solution of some problem. I would suggest you to start with the problem. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 17:42 ` Dmitry A. Kazakov @ 2015-12-08 18:00 ` Serge Robyns 2015-12-08 18:22 ` Dmitry A. Kazakov 0 siblings, 1 reply; 34+ messages in thread From: Serge Robyns @ 2015-12-08 18:00 UTC (permalink / raw) On Tuesday, 8 December 2015 18:42:26 UTC+1, Dmitry A. Kazakov wrote: > On Tue, 8 Dec 2015 09:24:58 -0800 (PST), Serge Robyns wrote: > In which relation is Do_Something to T_Abstract_Store? > > 1. a primitive operation = > has a separate implementation for each concrete type (from the class) > 2. class-wide operation = > has same implementation for all types (from the class) > > (anything else is a design fault) > > > Inside that procedure I'm using the A_Store. This regardless how A_Store > > got implemented. > > That sounds like #2. Then it must be declared as: This is indeed Correct. Thanks for the tip. Now it works, well at least it compiles ..... procedure test is type T_Store is new T_Asbtract_Store with null record; overriding procedure Insert (Store : in out T_Store; Client : in T_Client'Class) is begin null; end Insert; procedure Do_Something (A_Store : in out T_Asbtract_Store'Class) is Client : T_Client; begin A_Store.Insert (Client); end Do_Something; A_Store : access T_Asbtract_Store; The_Store : aliased T_Store; begin A_Store := T_Asbtract_Store (The_Store)'Access; Do_Something (A_Store.all); end test; ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 18:00 ` Serge Robyns @ 2015-12-08 18:22 ` Dmitry A. Kazakov 2015-12-08 20:21 ` Serge Robyns 0 siblings, 1 reply; 34+ messages in thread From: Dmitry A. Kazakov @ 2015-12-08 18:22 UTC (permalink / raw) On Tue, 8 Dec 2015 10:00:24 -0800 (PST), Serge Robyns wrote: > On Tuesday, 8 December 2015 18:42:26 UTC+1, Dmitry A. Kazakov wrote: >> On Tue, 8 Dec 2015 09:24:58 -0800 (PST), Serge Robyns wrote: >> In which relation is Do_Something to T_Abstract_Store? >> >> 1. a primitive operation = >> has a separate implementation for each concrete type (from the class) >> 2. class-wide operation = >> has same implementation for all types (from the class) >> >> (anything else is a design fault) >> >>> Inside that procedure I'm using the A_Store. This regardless how A_Store >>> got implemented. >> >> That sounds like #2. Then it must be declared as: > > This is indeed Correct. Thanks for the tip. Now it works, well at least it compiles ..... > > procedure test is > > type T_Store is new T_Asbtract_Store with null record; > > overriding procedure Insert (Store : in out T_Store; > Client : in T_Client'Class) is > begin > null; > end Insert; > > procedure Do_Something (A_Store : in out T_Asbtract_Store'Class) > is > Client : T_Client; > begin > A_Store.Insert (Client); > end Do_Something; > > A_Store : access T_Asbtract_Store; Observe that exactly same logic applies here. You never ever use a type-specific access type when you deal with whole class. Thus, without any further considerations it must be this: A_Store : access T_Asbtract_Store'Class; <rant on> In each instance you should clearly understand whether the code deals with one specific type or with all class. Once you know that, you express this knowledge in terms of types. One of the major Ada advantages with respect to OO is that it is properly typed. The type of a specific instance and the type of all instances from the class are two *different* types. The language rejected your program not because of being cruel, but because you used one type as if it were another. <rant off> > The_Store : aliased T_Store; > > begin > A_Store := T_Asbtract_Store (The_Store)'Access; Now, of course no explicit type conversion required: A_Store := The_Store'Access; (Type conversion must raise alarm bells) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 18:22 ` Dmitry A. Kazakov @ 2015-12-08 20:21 ` Serge Robyns 2015-12-08 21:08 ` Dmitry A. Kazakov 2015-12-08 22:55 ` Jeffrey R. Carter 0 siblings, 2 replies; 34+ messages in thread From: Serge Robyns @ 2015-12-08 20:21 UTC (permalink / raw) At times it proves to be helpful to write a sample test to clear away the clutter. Actually the whole issue appeared because I've been introducing the members to the T_Abstract_Data_Store as following: type T_Abstract_Data_Store is abstract tagged limited record Clients: access T_Abstract_Client; -- was missing 'Class Store2 : access T_Abstract_Store2; -- was missing 'Class ... Before it was a limited interface and hence no record. This reminds me I haven't yet fully figured out when it is <b>best</b> use limited and when not for my cases. So far I can use limited as I don't need the assignment for T_Store. Likewise from you here I try to avoid access variable as much as possible but at times I've no choice, well at least I use programming by extension ... My real Do_Something was already having A_Store : in out T_A..._S...'Class) however I was using A_Store.Clients.Insert (Client); I've been looking and re-looking at "A_Store" whilst the problem was actually "Clients", which was lacking 'Class; I've been misled as I got so used that the Ada compiler is very good at error handling, so much better than g++ or maybe I didn't read the column number properly (shame on me!). Now to go back to the overall solution. This is one of the rare cases where multiple inheritance would have made things easier, as then the non abstract class would "inherit" from the parents the respective implementations like in C++ class T_Store : public T_Store1, public T_Store2, .... Then I could just write "A_Store.Insert (Client);" as this was my original code as I had "glue" code in the body of T_Store package, overriding all the interfaces. I know that this statement will spark some discussions between the two "camps" as per Jeffrey's comment. Yes, the Ada compiler and language is very cruel but I learned to appreciate because often, if not always, highlights (potential) bugs lurking around. In other languages they don't distinguish between class wide and non class wide, whereas Ada is very picky about it. This is what keeps me trying to pursue with Ada, even if the latest C++ offers a few nice things without getting it right although. Such as for example; "typedef enum class int some_id;" that equates to "type some_id : Integer;" instead of "typedef some_id int;" that equates to "subtype some_id : Integer;" and dynamic_cast<class_xyz*> which performs run-time conversion checks just like Ada does (except it creates a null pointer and Ada an understandable exception) and by which I got already caught once. My only long term concern is how many people can program in Ada? Who will later be able to maintain my code? I've high respect to people like Dmitry, David and others who are putting a lot of their time maintaining various projects. But this could become my "killing" concern. I recall reading someone writing he use Ada for his prototyping/design and then implementing it the target language. I could follow that line of thought as Ada is really helping me to get things "right" but I don't feel like rewriting 1000's of code. That last paragraph sounds almost like a "message in a bottle" .... Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 20:21 ` Serge Robyns @ 2015-12-08 21:08 ` Dmitry A. Kazakov 2015-12-08 21:55 ` Serge Robyns 2015-12-09 10:53 ` G.B. 2015-12-08 22:55 ` Jeffrey R. Carter 1 sibling, 2 replies; 34+ messages in thread From: Dmitry A. Kazakov @ 2015-12-08 21:08 UTC (permalink / raw) On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote: > At times it proves to be helpful to write a sample test to clear away the > clutter. Actually the whole issue appeared because I've been introducing > the members to the T_Abstract_Data_Store as following: > > type T_Abstract_Data_Store is abstract tagged limited record > Clients: access T_Abstract_Client; -- was missing 'Class > Store2 : access T_Abstract_Store2; -- was missing 'Class > ... If you really need mix-in then better do it like this: type T_Abstract_Data_Store ( Clients : not null access T_Abstract_Client'Class; Store2 : not null access T_Abstract_Store2'Class ) is abstract tagged limited record This would add a bit more safety, much needed when using access types. > Now to go back to the overall solution. This is one of the rare cases > where multiple inheritance would have made things easier, as then the non > abstract class would "inherit" from the parents the respective > implementations like in C++ class T_Store : public T_Store1, public > T_Store2, .... Then I could just write "A_Store.Insert (Client);" as this > was my original code as I had "glue" code in the body of T_Store package, > overriding all the interfaces. Why MI is needed here? Of which of two stores is Insert? There is an ugly method of achieving poor-man's full MI. Full means that you inherit multiple implementations rather than only interfaces. The latter is allowed in Ada. The method is that you inherit one implementation normally and add others through a generic. I don't want to describe it, because it is really ugly as anything that involves generics. I am not sure you need full MI in first place. > I know that this statement will spark some discussions between the two > "camps" as per Jeffrey's comment. There are people in c.l.a who believe in procedural decomposition and inference from the type structure. It is incompatible with fundamental Ada's principles of strong typing and separation of interface and implementation. But that is my opinion. > My only long term concern is how many people can program in Ada? If you do, that is one man more... Any language lives by people who want to use it. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 21:08 ` Dmitry A. Kazakov @ 2015-12-08 21:55 ` Serge Robyns 2015-12-08 22:43 ` Serge Robyns 2015-12-09 8:48 ` Dmitry A. Kazakov 2015-12-09 10:53 ` G.B. 1 sibling, 2 replies; 34+ messages in thread From: Serge Robyns @ 2015-12-08 21:55 UTC (permalink / raw) On Tuesday, 8 December 2015 22:09:13 UTC+1, Dmitry A. Kazakov wrote: > On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote: > > If you really need mix-in then better do it like this: > > type T_Abstract_Data_Store > ( Clients : not null access T_Abstract_Client'Class; > Store2 : not null access T_Abstract_Store2'Class > ) is abstract tagged limited record > > This would add a bit more safety, much needed when using access types. Was considering this option, I haven't used this construct yet. This is a place where reading the ALRM just gives headaches without a clue of the use cases. And guess what, I just have been hit by a forgotten assignment .... Then it really feels like C and the null pointers :-( So this is my next immediate step. Now I'm still fixing a "tag check failed" :-P, which was one thing a mentioned an hour ago. What I do like with Ada, is that I can do major refactoring and regularly on the first successful compile have a "bug free" version. Now with these access variables the story changes a little, I got hit by my first segmentation violation in Ada :-). > Why MI is needed here? Of which of two stores is Insert? Just stupid laziness .... > > My only long term concern is how many people can program in Ada? > > If you do, that is one man more... Any language lives by people who want to > use it. > Yes, I try to advocate and hence show by the example ... but I'm not planning being a lonesome cowboy .... Regards, Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 21:55 ` Serge Robyns @ 2015-12-08 22:43 ` Serge Robyns 2015-12-08 22:55 ` Jeffrey R. Carter 2015-12-09 8:48 ` Dmitry A. Kazakov 1 sibling, 1 reply; 34+ messages in thread From: Serge Robyns @ 2015-12-08 22:43 UTC (permalink / raw) On Tuesday, 8 December 2015 22:55:33 UTC+1, Serge Robyns wrote: > On Tuesday, 8 December 2015 22:09:13 UTC+1, Dmitry A. Kazakov wrote: > > On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote: > > > > If you really need mix-in then better do it like this: > > > > type T_Abstract_Data_Store > > ( Clients : not null access T_Abstract_Client'Class; > > Store2 : not null access T_Abstract_Store2'Class > > ) is abstract tagged limited record > > > > This would add a bit more safety, much needed when using access types. > Done, everything is completed and runs. The tag error was a "hidden" bug where I didn't return the right child class. Once I got around the issue the changes went quickly through and I would have had only one bug if I had used the above construct immediately as the compiler wouldn't allowed me to "forget". This is what amazes me with Ada. Now I've a generic question with regards to access type vs. in-out for parameters. What is the proper Ada way to pass the "Store" around? Is it: Procedure Do_Something (A_Store : in out T_Abstract_Store'Class); or: Procedure Do_Something (A_Store : not null access T_Abstract_Store'Class); ? As a former C programmer my habit goes for the not null access variant. Regards, Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 22:43 ` Serge Robyns @ 2015-12-08 22:55 ` Jeffrey R. Carter 2015-12-09 23:03 ` Randy Brukardt 0 siblings, 1 reply; 34+ messages in thread From: Jeffrey R. Carter @ 2015-12-08 22:55 UTC (permalink / raw) On 12/08/2015 03:43 PM, Serge Robyns wrote: > > Now I've a generic question with regards to access type vs. in-out for parameters. What is the proper Ada way to pass the "Store" around? > Is it: > Procedure Do_Something (A_Store : in out T_Abstract_Store'Class); > or: > Procedure Do_Something (A_Store : not null access T_Abstract_Store'Class); > ? > > As a former C programmer my habit goes for the not null access variant. As an Ada S/W engineer, you should use "in out". With "in out", the compiler does the work of passing the object in such a way that the operation can read and modify the value. With an access, you (or whoever calls your operation) has to do extra work to have an access value to pass. It's unnecessary extra work and an extra chance to get something wrong, and so to be avoided. -- Jeff Carter "Your mother was a hamster and your father smelt of elderberries." Monty Python & the Holy Grail 06 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 22:55 ` Jeffrey R. Carter @ 2015-12-09 23:03 ` Randy Brukardt 2015-12-10 8:38 ` Serge Robyns 0 siblings, 1 reply; 34+ messages in thread From: Randy Brukardt @ 2015-12-09 23:03 UTC (permalink / raw) "Jeffrey R. Carter" <spam.jrcarter.not@spam.not.acm.org> wrote in message news:n47n08$3tr$1@dont-email.me... > On 12/08/2015 03:43 PM, Serge Robyns wrote: >> >> Now I've a generic question with regards to access type vs. in-out for >> parameters. What is the proper Ada way to pass the "Store" around? >> Is it: >> Procedure Do_Something (A_Store : in out T_Abstract_Store'Class); >> or: >> Procedure Do_Something (A_Store : not null access >> T_Abstract_Store'Class); >> ? >> >> As a former C programmer my habit goes for the not null access variant. > > As an Ada S/W engineer, you should use "in out". With "in out", the > compiler > does the work of passing the object in such a way that the operation can > read > and modify the value. With an access, you (or whoever calls your > operation) has > to do extra work to have an access value to pass. It's unnecessary extra > work > and an extra chance to get something wrong, and so to be avoided. On top of which, it tends to limit how the client can use your package. In particular, the client most likely couldn't put values of your object type into a container, because Ada puts very strict limits on getting an access value into a container. (Additionally, because of the extra runtime accessibility checking, the access version is slower. That effect is small enough that it shouldn't be a major determining factor in deciding which to use, but if all other things are equal, why would anyone want to use the slower version??) Randy. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-09 23:03 ` Randy Brukardt @ 2015-12-10 8:38 ` Serge Robyns 2015-12-10 23:43 ` Randy Brukardt 0 siblings, 1 reply; 34+ messages in thread From: Serge Robyns @ 2015-12-10 8:38 UTC (permalink / raw) On Thursday, 10 December 2015 00:03:15 UTC+1, Randy Brukardt wrote: > On top of which, it tends to limit how the client can use your package. In > particular, the client most likely couldn't put values of your object type > into a container, because Ada puts very strict limits on getting an access > value into a container. (Additionally, because of the extra runtime > accessibility checking, the access version is slower. That effect is small > enough that it shouldn't be a major determining factor in deciding which to > use, but if all other things are equal, why would anyone want to use the > slower version??) > > Randy. This is an interesting topic. I've always been wondering when the compiler is generating checks and when it does know he can skip them. And what kind of test is the compiler doing on an access value. Especially coming from the C world this is very "counter intuitive" :-P. If the variable is as following: A_Reference : access not null T_something; -- whereas T_Something is just a plain record type, i.e. not a tagged one. and the procedure signature procedure Do_Something (Reference : not null access T_Something; ....); What would happen with: Do_Something (A_Reference, ...); My assumption is that the not null checks can be skipped during the call and inside the procedure. What other checks are performed inside Do_Something for A_Reference? Regards, Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-10 8:38 ` Serge Robyns @ 2015-12-10 23:43 ` Randy Brukardt 0 siblings, 0 replies; 34+ messages in thread From: Randy Brukardt @ 2015-12-10 23:43 UTC (permalink / raw) "Serge Robyns" <serge.robyns@gmail.com> wrote in message news:c7533283-04c4-49f6-abe8-4f47d70b86df@googlegroups.com... On Thursday, 10 December 2015 00:03:15 UTC+1, Randy Brukardt wrote: ... >If the variable is as following: > A_Reference : access not null T_something; -- whereas T_Something is just a plain record type, i.e. not a tagged one. If this is a stand-alone variable, it has dynamic accessibility (that is, the accessibility is that of the designated object, wherever it is). >and the procedure signature > procedure Do_Something (Reference : not null access T_Something; ....); >What would happen with: > Do_Something (A_Reference, ...); Anonymous access parameters always have dynamic accessibility. >My assumption is that the not null checks can be skipped during the call >and inside the procedure. Most likely. Janus/Ada has an "assume nothing" mode as one could use streaming or Unchecked_Conversion to put a null value into a not null component, and in that case a check would be made anything. But that's usually only used for debugging when all else has failed (and it usually turns out that compiler assumptions aren't the problem). > What other checks are performed inside Do_Something for A_Reference? Accessibility checks. As I noted, the accessibility of Do_Something is dynamic. (I've always thought "accessibility" was a terrible term -- the correct concept is "lifetime". The designated object has to live long enough for the use it's put to, and if not, an accessibility check fails.) Even if the body of Do_Something doesn't contain any accessibility checks (today), it could easily be changed to have one, so it's necessary to pass that information along with the pointer. And that can't be optimized away because to do so would make a body dependence (i.e., recompiling the body would require recompiling all of the calls). To be fair, it would be possible to optimize the overhead away if the subprogram is inlined, or if the subprogram has a precondition that requires the object to be library-level. But I don't know if any compilers actually do any such optimizations. Thus, "in out T" (which has static accessibility and thus doesn't have any accessibility overhead) is almost always cheaper than "not null access T" which has dynamic accessibility (and thus has to pass an accessibility value). As I said yesterday, the effect is small, but it's clearly non-zero (passing two words costs more than passing one word). Randy. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 21:55 ` Serge Robyns 2015-12-08 22:43 ` Serge Robyns @ 2015-12-09 8:48 ` Dmitry A. Kazakov 1 sibling, 0 replies; 34+ messages in thread From: Dmitry A. Kazakov @ 2015-12-09 8:48 UTC (permalink / raw) On Tue, 8 Dec 2015 13:55:31 -0800 (PST), Serge Robyns wrote: > On Tuesday, 8 December 2015 22:09:13 UTC+1, Dmitry A. Kazakov wrote: >> On Tue, 8 Dec 2015 12:21:31 -0800 (PST), Serge Robyns wrote: >> >> If you really need mix-in then better do it like this: >> >> type T_Abstract_Data_Store >> ( Clients : not null access T_Abstract_Client'Class; >> Store2 : not null access T_Abstract_Store2'Class >> ) is abstract tagged limited record >> >> This would add a bit more safety, much needed when using access types. > > Was considering this option, I haven't used this construct yet. This is a > place where reading the ALRM just gives headaches without a clue of the > use cases. Discriminant is a kind of read-only component with additional checks ensuring it defined when the object is created. From the SW POV discriminant is more an interface thing, while components are pure implementation. When you declare something with an access discriminant you announce that the client must take care of handling the object the discriminant refers. You basically put the burden on the client's shoulders, but also hint the compiler that it must check things like that the target object don't leave the scope prematurely. > And guess what, I just have been hit by a forgotten assignment Right, this is a safety check. Limited types have no assignment. Considering components or discriminants access types you should define the semantics of assignment. Is it shallow copy or deep copy? That normally would rule out anonymous access types. Communication objects rarely need to be copyable. If they have to, the best way is to add a handles layer to the reference-counted limited objects and copy the handles. Interfaces is a help here. You can make the handle and the target object to implement the same interface. The handle's implementation would delegate to the target's implementation. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 21:08 ` Dmitry A. Kazakov 2015-12-08 21:55 ` Serge Robyns @ 2015-12-09 10:53 ` G.B. 2015-12-09 12:03 ` Dmitry A. Kazakov 1 sibling, 1 reply; 34+ messages in thread From: G.B. @ 2015-12-09 10:53 UTC (permalink / raw) On 08.12.15 22:08, Dmitry A. Kazakov wrote: > There are people in c.l.a who believe in procedural decomposition and > inference from the type structure. It is incompatible with fundamental > Ada's principles of strong typing and separation of interface and > implementation. But that is my opinion. There are those in c.l.ada, including Dmitry Kazakov ;-) ;-) ;-), who suggested that the parameter profile of a subprogram can be seen as a type. For practical matters in single programs, this looks perfectly fine to me. Considering, if everything in a program is a reusable building block, when is there any use? (Moreover, what is the type of constructs that do actually reuse if everything became an Ada type?) ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-09 10:53 ` G.B. @ 2015-12-09 12:03 ` Dmitry A. Kazakov 2015-12-09 13:42 ` G.B. 0 siblings, 1 reply; 34+ messages in thread From: Dmitry A. Kazakov @ 2015-12-09 12:03 UTC (permalink / raw) On Wed, 9 Dec 2015 11:53:43 +0100, G.B. wrote: > On 08.12.15 22:08, Dmitry A. Kazakov wrote: > >> There are people in c.l.a who believe in procedural decomposition and >> inference from the type structure. It is incompatible with fundamental >> Ada's principles of strong typing and separation of interface and >> implementation. But that is my opinion. > > There are those in c.l.ada, including Dmitry Kazakov ;-) ;-) ;-), > who suggested that the parameter profile of a subprogram can > be seen as a type. You mean procedural types? Ada does not have them, only the access-to-subprogram kludge. > For practical matters in single programs, > this looks perfectly fine to me. Considering, if everything in > a program is a reusable building block, when is there any use? What about the process of fitting? In order to reuse you need an interface how and where does it fit. You cannot do that without types, e.g. of the parameters. The second question is the context where you may to reuse the block. How do you describe the context? In terms of types, the context is everywhere the types of the interface exist (this includes subtype constraints). That makes your "block" a mere primitive operation of those types. The bottom line, you can have your lazy blocks only when not reusable and weakly typed. > (Moreover, what is the type of constructs that do actually reuse > if everything became an Ada type?) I didn't say that everything is a type. I only said that procedural decomposition is superseded by OO decomposition. Largely because programming in terms of singular values is superseded by programming in terms of sets of values (type) and sets of types (generic programming). This is a normal evolutionary process to handle increasing SW complexity. This is orthogonal to other language constructs, though most of them do rely on types anyway, e.g. if, for, case statements require certain class of types to work. As such they could be viewed as syntax sugar for some primitive operations. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-09 12:03 ` Dmitry A. Kazakov @ 2015-12-09 13:42 ` G.B. 2015-12-09 14:23 ` Dmitry A. Kazakov 0 siblings, 1 reply; 34+ messages in thread From: G.B. @ 2015-12-09 13:42 UTC (permalink / raw) On 09.12.15 13:03, Dmitry A. Kazakov wrote: > You mean procedural types? Not exactly procedural types. I'd like a reflection of a more clear cut distinction between when a(n MI-)type is needed for (M)dispatching versus when the (MI-)type is needed for materializing actual objects. Maybe that simplifies things. Example below. > The bottom line, you can have your lazy blocks only when not reusable and > weakly typed. Put differently, at some point I can use "weakly" typed blocks for getting a job done that warrants just the necessary effort. (Yeah, I guess it's not the dream of an engineer who is looking for some kind of "just technical perfection".) For some perspective, dispatching can look a lot like a specially crafted procedural thing. I think that there are cases related to MD (as in the example of storing from Objects'Class into Databases'Class) when declaring a regular named (MI-)type would be going over the top. procedure Demo is package Root is type T is abstract tagged private; procedure Op (X : in out T; Val : Natural) is abstract; private type T is abstract tagged record Identity : Natural; -- := ...; end record; end Root; package Twigs is type A is new Root.T with record null; end record; overriding procedure Op (X : in out A; Val : Natural); type B is new Root.T with record null; end record; overriding procedure Op (X : in out B; Val : Natural); end Twigs; package body Twigs is separate; package X is procedure D12 (Left : in out Root.T'Class; Right : in out Twigs.B'Class); procedure D22 (Left : in out Twigs.A'Class; Right : in out Twigs.B'Class); end X; package body X is separate; Va : Twigs.A; Vb : Twigs.B; begin -- not Ada do X.D12 with X.D2'Type (Left => V1, Right => Vb); do X.D22 with X.D2'Type (Left => Va, Right => Vb); do X.D22 with X.D2'Type (Left => Va, Right => Va); -- no end Demo; The last lines are not really needed, but they show where there is a type lurking. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-09 13:42 ` G.B. @ 2015-12-09 14:23 ` Dmitry A. Kazakov 0 siblings, 0 replies; 34+ messages in thread From: Dmitry A. Kazakov @ 2015-12-09 14:23 UTC (permalink / raw) On Wed, 9 Dec 2015 14:42:24 +0100, G.B. wrote: > On 09.12.15 13:03, Dmitry A. Kazakov wrote: >> You mean procedural types? > > Not exactly procedural types. I'd like a reflection of a more > clear cut distinction between when a(n MI-)type is needed for > (M)dispatching versus when the (MI-)type is needed for materializing > actual objects. I think the problem is that you consider dispatching an interface-thing. It is not. Dispatching is strictly an implementation. It is important to understand that both primitive and class-wide operations are defined on the whole class. It is the implementations of these that are different. In particular a primitive operation has a body for each member of the class. Dispatching is merely a mechanism of body selection. There could be other mechanisms of body composition. For example body extension as used in constructors, destructors, aggregates, assignment, stream I/O operations. >> The bottom line, you can have your lazy blocks only when not reusable and >> weakly typed. > > Put differently, at some point I can use "weakly" typed blocks > for getting a job done that warrants just the necessary effort. Yes, you can program C in Ada. The question is what for? > For some perspective, dispatching can look a lot like a specially > crafted procedural thing. I think that there are cases related > to MD (as in the example of storing from Objects'Class into > Databases'Class) when declaring a regular named (MI-)type would > be going over the top. > > procedure Demo is > > package Root is > type T is abstract tagged private; > procedure Op > (X : in out T; Val : Natural) is abstract; > private > type T is abstract tagged record > Identity : Natural; -- := ...; > end record; > end Root; > > package Twigs is > type A is new Root.T with record null; end record; > overriding procedure Op (X : in out A; Val : Natural); > > type B is new Root.T with record null; end record; > overriding procedure Op (X : in out B; Val : Natural); > end Twigs; > package body Twigs is separate; > > package X is > > procedure D12 > (Left : in out Root.T'Class; > Right : in out Twigs.B'Class); > > procedure D22 > (Left : in out Twigs.A'Class; > Right : in out Twigs.B'Class); > > end X; > package body X is separate; > > Va : Twigs.A; > Vb : Twigs.B; > > begin > -- not Ada > do X.D12 with X.D2'Type (Left => V1, Right => Vb); > do X.D22 with X.D2'Type (Left => Va, Right => Vb); > do X.D22 with X.D2'Type (Left => Va, Right => Va); -- no > > end Demo; > > The last lines are not really needed, but they show where > there is a type lurking. I don't understand the purpose of this example. MI and MD are required for consistency. Practical use or not is irrelevant, because consistency trumps everything else. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 20:21 ` Serge Robyns 2015-12-08 21:08 ` Dmitry A. Kazakov @ 2015-12-08 22:55 ` Jeffrey R. Carter 2015-12-08 23:04 ` Serge Robyns 1 sibling, 1 reply; 34+ messages in thread From: Jeffrey R. Carter @ 2015-12-08 22:55 UTC (permalink / raw) On 12/08/2015 01:21 PM, Serge Robyns wrote: > > Before it was a limited interface and hence no record. This reminds me I > haven't yet fully figured out when it is <b>best</b> use limited and when not > for my cases. So far I can use limited as I don't need the assignment for > T_Store. Likewise from you here I try to avoid access variable as much as > possible but at times I've no choice, well at least I use programming by > extension ... If you don't need assignment because there will never be more than one of them, that's an indication that you don't need a type and a variable; you need a module. I suspect you think in terms of types because C++ only provides encapsulation and information hiding through its class construct. In Ada, your 1st thought when you want encapsulation or information hiding should be a pkg. -- Jeff Carter "Your mother was a hamster and your father smelt of elderberries." Monty Python & the Holy Grail 06 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 22:55 ` Jeffrey R. Carter @ 2015-12-08 23:04 ` Serge Robyns 2015-12-08 23:42 ` Jeffrey R. Carter ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Serge Robyns @ 2015-12-08 23:04 UTC (permalink / raw) On Tuesday, 8 December 2015 23:55:52 UTC+1, Jeffrey R. Carter wrote: > On 12/08/2015 01:21 PM, Serge Robyns wrote: > > > > Before it was a limited interface and hence no record. This reminds me I > > haven't yet fully figured out when it is <b>best</b> use limited and when not > > for my cases. So far I can use limited as I don't need the assignment for > > T_Store. Likewise from you here I try to avoid access variable as much as > > possible but at times I've no choice, well at least I use programming by > > extension ... > > If you don't need assignment because there will never be more than one of them, > that's an indication that you don't need a type and a variable; you need a > module. I suspect you think in terms of types because C++ only provides > encapsulation and information hiding through its class construct. In Ada, your > 1st thought when you want encapsulation or information hiding should be a pkg. > How do I then pass the context? I mean, now I pass the parameter around. through global variables? Also in the future I would like to have multiple "tasks" running, each with their "context", especially the db session. I do believe these are good reasons for having a variable. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 23:04 ` Serge Robyns @ 2015-12-08 23:42 ` Jeffrey R. Carter 2015-12-09 13:40 ` Jere 2015-12-13 21:37 ` Shark8 2 siblings, 0 replies; 34+ messages in thread From: Jeffrey R. Carter @ 2015-12-08 23:42 UTC (permalink / raw) On 12/08/2015 04:04 PM, Serge Robyns wrote: > > How do I then pass the context? I mean, now I pass the parameter around. > through global variables? Also in the future I would like to have multiple > "tasks" running, each with their "context", especially the db session. I do > believe these are good reasons for having a variable. The case I was saying should be a pkg is when there's only 1. In that case, the state is in the pkg body. Why should the rest of the code need to pass anything around? For multiple tasks with independent states, then clearly something needs to be passed in. Whether that's the entire state or something that lets the pkg obtain the correct state is a design decision. -- Jeff Carter "Your mother was a hamster and your father smelt of elderberries." Monty Python & the Holy Grail 06 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 23:04 ` Serge Robyns 2015-12-08 23:42 ` Jeffrey R. Carter @ 2015-12-09 13:40 ` Jere 2015-12-09 13:48 ` G.B. 2015-12-13 21:37 ` Shark8 2 siblings, 1 reply; 34+ messages in thread From: Jere @ 2015-12-09 13:40 UTC (permalink / raw) On Tuesday, December 8, 2015 at 6:04:08 PM UTC-5, Serge Robyns wrote: > On Tuesday, 8 December 2015 23:55:52 UTC+1, Jeffrey R. Carter wrote: > > If you don't need assignment because there will never be more than one of them, > > that's an indication that you don't need a type and a variable; you need a > > module. I suspect you think in terms of types because C++ only provides > > encapsulation and information hiding through its class construct. In Ada, your > > 1st thought when you want encapsulation or information hiding should be a pkg. > > > > How do I then pass the context? I mean, now I pass the parameter around. through global variables? Also in the future I would like to have multiple "tasks" running, each with their "context", especially the db session. I do believe these are good reasons for having a variable. If will be having multiple ones with their own contexts then you will have to use objects from types to do that. However, if you are only going to have one, then as Jeffrey Carter suggested, just use a package. A useless uncompiled example (I don't have GNAT in front of me here): package My_Package is type Data_Type is record Value1 : Integer := Integer'First; Value2 : Positive := Positive'First; end record; procedure Update_My_Data(Data : in Data_Type); function View_My_Data return Data_Type; end My_Package; package body My_Package is My_Data : Data_Type; procedure Update_My_Data(Data : in Data_Type) is begin My_Data := Data; end Update_My_Data; function View_My_Data return Data_Type is begin return My_Data; end View_My_Data; end My_Package; My_Data is only accessible via the package body, so any clients have to use the provided functions of the package to manipulate or look at the data. This example is over-simplistic, but I just wanted to show you the layout. It's not the same as using a global since the clients can't access the variable directly and you have the freedom to have the public interface hide some implementation details on how the data is actually handled. Since you are familiar with C++, it has a similar construct (I use it for peripherals in small embedded systems): A class with only static members and static methods. In this case, you are not using the class to generate objects but to organize methods and data with the ability to make some of them private (which you cannot do in a namespace) while also being able to retain a private non-global static state for one thing. It is not as elegant as an Ada package and there are still some differences, but it is the closest analog to this that C++ has. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-09 13:40 ` Jere @ 2015-12-09 13:48 ` G.B. 2015-12-09 15:33 ` Jere 0 siblings, 1 reply; 34+ messages in thread From: G.B. @ 2015-12-09 13:48 UTC (permalink / raw) On 09.12.15 14:40, Jere wrote: > A class with only static members and static methods. A file will do, in both C++ and in C. Ada does not have file scope, but does have packages, so the names are full names. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-09 13:48 ` G.B. @ 2015-12-09 15:33 ` Jere 0 siblings, 0 replies; 34+ messages in thread From: Jere @ 2015-12-09 15:33 UTC (permalink / raw) On Wednesday, December 9, 2015 at 8:48:02 AM UTC-5, G.B. wrote: > On 09.12.15 14:40, Jere wrote: > > A class with only static members and static methods. > > A file will do, in both C++ and in C. > Ada does not have file scope, but does > have packages, so the names are full names. In C that's really all one can do. In C++ one prefers not to do it using file scope as that just pollutes the global namespace. The preference is always to use either a namespace or class to wrap members and data. Also, one cannot get protected (c++ meaning) static methods or members using just file scope. While that is somewhat limited in use, it does have its benefits in some designs. The big reason is not to put all of one's stuff into the global namespace. Then it ends up forcing one to leave behind some meaningful names in order to not clash with another item of the same signature. Regardless, the Ada package way is much cleaner and nicer. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 23:04 ` Serge Robyns 2015-12-08 23:42 ` Jeffrey R. Carter 2015-12-09 13:40 ` Jere @ 2015-12-13 21:37 ` Shark8 2015-12-14 2:20 ` Jeffrey R. Carter 2 siblings, 1 reply; 34+ messages in thread From: Shark8 @ 2015-12-13 21:37 UTC (permalink / raw) On Tuesday, December 8, 2015 at 4:04:08 PM UTC-7, Serge Robyns wrote: > > How do I then pass the context? I mean, now I pass the parameter around. through global variables? No, you use the package. If you want a single application-wide object you can use a package. Here is a single counter: Package Counter is Procedure Inc; Procedure Dec; Function Value Return Natural; Private Data : Natural := 0; End Counter; Package Body Counter is Procedure Inc is begin Data:= Natural'Succ( Data ); end Inc; Procedure Dec is begin Data:= Natural'Pred( Data ); end Dec; Function Value return Natural is ( Data ); End Counter; You could also enclose that sort of logic in a task or protected object; and if you need multiple singletons you could always use a generic version and then make an instance for each needed singleton. What those above are warning you against is trying to write C (or C++) in Ada. There's a lot of differences that can usually greatly simplify and/or make your code more reliable. (The most obvious of those is probably the Array type, which because it contains the range/indices means you don't have to pass that sort of info in parameters. > Also in the future I would like to have multiple "tasks" running, each with their "context", especially the db session. I do believe these are good reasons for having a variable. That is doable, I did it with a mock-up test and it worked pretty nicely... though personally I have had problems getting a good DB interface/binding up and running w/ Ada. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-13 21:37 ` Shark8 @ 2015-12-14 2:20 ` Jeffrey R. Carter 2015-12-15 7:26 ` Shark8 0 siblings, 1 reply; 34+ messages in thread From: Jeffrey R. Carter @ 2015-12-14 2:20 UTC (permalink / raw) On 12/13/2015 02:37 PM, Shark8 wrote: > > Package Counter is > Procedure Inc; > Procedure Dec; > Function Value Return Natural; > Private > Data : Natural := 0; > End Counter; I wouldn't put the state in the private part. > Package Body Counter is Instead, I'd put it in the body Data : Natural := 0; > Procedure Inc is > begin > Data:= Natural'Succ( Data ); > end Inc; > > Procedure Dec is > begin > Data:= Natural'Pred( Data ); > end Dec; I'd also use "+ 1" or "- 1", since those are clearer. > Function Value return Natural is > ( Data ); > End Counter; -- Jeff Carter "I don't know why I ever come in here. The flies get the best of everything." Never Give a Sucker an Even Break 102 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-14 2:20 ` Jeffrey R. Carter @ 2015-12-15 7:26 ` Shark8 0 siblings, 0 replies; 34+ messages in thread From: Shark8 @ 2015-12-15 7:26 UTC (permalink / raw) On Sunday, December 13, 2015 at 7:20:40 PM UTC-7, Jeffrey R. Carter wrote: > > I wouldn't put the state in the private part. > Instead, I'd put it in the body I tend to go with body, though there are some instances where you need to put it in private. (Namely: when child units need to access the internal representation of the data.) > I'd also use "+ 1" or "- 1", since those are clearer. The reason I used Succ and Pred was that I was originally going to [also] include a generic version, which would need to replace Natural w/ the formal type parameter: Generic Type Data_Type is (<>); Default : in Data_Type; Package Generic_Counter is Procedure Inc; Procedure Dec; Function Value Return Data_Type; End Generic_Counter; Package Body Generic_Counter is Data : Data_Type := Default; Procedure Inc is begin Data:= Data_Type'Succ( Data ); end Inc; Procedure Dec is begin Data:= Data_Type'Pred( Data ); end Dec; Function Value return Data_Type is ( Data ); End Generic_Counter; ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns 2015-12-08 16:59 ` G.B. 2015-12-08 17:04 ` Dmitry A. Kazakov @ 2015-12-08 17:30 ` Jeffrey R. Carter 2015-12-08 17:48 ` Serge Robyns 2 siblings, 1 reply; 34+ messages in thread From: Jeffrey R. Carter @ 2015-12-08 17:30 UTC (permalink / raw) On 12/08/2015 09:45 AM, Serge Robyns wrote: > > I've been trying to get my head around a way to create a solution where the storage method can be changed. In your approach, IIUC, you would change the Insert procedures for each of the types to change the storage method. You are on the right track, but are confused by the added complexity of using programming by extension. The typical way to do this in Ada is something like with T1_Ops; with T2_Ops; ... package Storage is procedure Put (Item : in T1_Ops.T1); procedure Put (Item : in T2_Ops.T2); ... end Storage; The bodies of the Put procedures correspond to your Insert procedures. (You can call them Insert, too, if you like, but I once heard it asserted that a correctly designed Ada program has 90% of its operations named Put or Get.) This is then used as with Storage; ... Storage.Put (Item => Current_Item); which is the same regardless of the type of the item being stored. To change the storage method, you change the body of Storage. Note that programming by extension is neither necessary nor desirable for this. -- Jeff Carter "Your mother was a hamster and your father smelt of elderberries." Monty Python & the Holy Grail 06 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 17:30 ` Jeffrey R. Carter @ 2015-12-08 17:48 ` Serge Robyns 2015-12-08 18:46 ` Jeffrey R. Carter 2015-12-08 22:20 ` Simon Wright 0 siblings, 2 replies; 34+ messages in thread From: Serge Robyns @ 2015-12-08 17:48 UTC (permalink / raw) On Tuesday, 8 December 2015 18:30:59 UTC+1, Jeffrey R. Carter wrote: > with T1_Ops; > with T2_Ops; > ... > package Storage is > procedure Put (Item : in T1_Ops.T1); > > procedure Put (Item : in T2_Ops.T2); > > ... > end Storage; > > The bodies of the Put procedures correspond to your Insert procedures. (You can > call them Insert, too, if you like, but I once heard it asserted that a > correctly designed Ada program has 90% of its operations named Put or Get.) > > This is then used as > > with Storage; > > ... > > Storage.Put (Item => Current_Item); > > which is the same regardless of the type of the item being stored. To change the > storage method, you change the body of Storage. This seems indeed a workable solution. Actually, in my initial version I had one big package for the implementation. I found this "ugly" and wanted to decimate them as per package under the form of child packages. This also allows me to use private types, as children can access private elements. My question is then, how can I easily manage this from a source management perspective. As I said in a prior post, changing the "with/use" everywhere or use some file system "tricks" to fool the compiler to pick the "right" version. Maybe there is some Ada "feature" to do this nicely. As I said in a prior post, in C and C++ I would use the pre-processor to pick the "right" one. > Note that programming by extension is neither necessary nor desirable for this. I've noticed from your papers you generally disapproves "programming by extension". Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 17:48 ` Serge Robyns @ 2015-12-08 18:46 ` Jeffrey R. Carter 2015-12-08 20:28 ` Serge Robyns 2015-12-08 22:20 ` Simon Wright 1 sibling, 1 reply; 34+ messages in thread From: Jeffrey R. Carter @ 2015-12-08 18:46 UTC (permalink / raw) On 12/08/2015 10:48 AM, Serge Robyns wrote: > > This seems indeed a workable solution. Actually, in my initial version I had > one big package for the implementation. I found this "ugly" and wanted to > decimate them as per package under the form of child packages. This also > allows me to use private types, as children can access private elements. The point of this approach is that changing the storage method only requires changing one thing in the entire system. If you need to store private information then the storage operation needs to be part of the implementation of the thing with private information, and you lose this advantage. You have to consider the trade off between the values of keeping the information private and keeping the storage method in one place. > My question is then, how can I easily manage this from a source management > perspective. As I said in a prior post, changing the "with/use" everywhere > or use some file system "tricks" to fool the compiler to pick the "right" > version. Maybe there is some Ada "feature" to do this nicely. As I said in a > prior post, in C and C++ I would use the pre-processor to pick the "right" > one. I would think that one would use a storage method for a while, and if it proved unsuitable, switch to another. In such a case, one would simply modify the body of the pkg, and there would be no source-management issues. You would use your version-management system (VCS) to revert to the earlier version of that single file if you decide the change was a mistake. What you're describing sounds more like maintaining multiple versions that differ only in their storage method. VCSs don't help with this; generally the only way to use a VCS to address this is to have multiple branches that differ only in the 1 file. and merge non-storage changes from one branch to the others. That can get as messy and hard to do right as having all the implementations in a single file with all the preprocessor directives for selecting the right one. Usually additional "make" tools are used to help get it right. One approach in Ada is to have to body files, and copy the desired one to the correct name prior to building the system. If the body file name is Storage.Ada2, for example, you might have bodies Storage_M1.Ada2, Storage_M2.Ada2, ..., one of which you'd copy to Storage.Ada2 prior to building. You might use scripts or another "make" tool to reduce the chances of getting it wrong. Some people like to use a library-level renaming in cases like this: with Storage_M2; package Storage renames Storage_M2; changing the withed pkg prior to building, but that involves keeping multiple copies of an identical pkg spec, which is itself a maintenance problem. Your attempted solution using multiple withs, with all but one commented out, is similar to this, as the multiple pkgs would all have identical specs. In addition, you would need at least 1 further change in the unit that withs the storage pkg. > I've noticed from your papers you generally disapproves "programming by > extension". I investigated it extensively over 2 decades ago and concluded that its negative qualities outweighed any positive ones. I have not seen anything since to change my mind. Kazakov, on the other hand, likes multiple inheritance, so you have his balancing view posting here. In addition to my papers at http://pragmada.x10hosting.com/papers.html you might also be interested in Rosen's paper "What Orientation Should Ada Objects Take", available at http://www.adalog.fr/en/publications.html -- Jeff Carter "Your mother was a hamster and your father smelt of elderberries." Monty Python & the Holy Grail 06 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 18:46 ` Jeffrey R. Carter @ 2015-12-08 20:28 ` Serge Robyns 0 siblings, 0 replies; 34+ messages in thread From: Serge Robyns @ 2015-12-08 20:28 UTC (permalink / raw) On Tuesday, 8 December 2015 19:46:10 UTC+1, Jeffrey R. Carter wrote: > I investigated it extensively over 2 decades ago and concluded that its negative > qualities outweighed any positive ones. I have not seen anything since to change > my mind. Kazakov, on the other hand, likes multiple inheritance, so you have his > balancing view posting here. > > In addition to my papers at > > http://pragmada.x10hosting.com/papers.html > > you might also be interested in Rosen's paper "What Orientation Should Ada > Objects Take", available at > > http://www.adalog.fr/en/publications.html > Thank you for the references, when time permits I'll read time as I'm still trying to get through the very boring ALRM 2012. When I was "professionally" programming a couple of decades ago it was C, Pascal and assembler. Hence I do lack personal evidence to make an assessment but the ideas of programming by extension seems appealing to me. And indeed I noticed indeed that you and Dmitry are on the opposite side however sharing the same love for Ada. Serge ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: I'm facing an issue with: call to abstract procedure must be dispatching 2015-12-08 17:48 ` Serge Robyns 2015-12-08 18:46 ` Jeffrey R. Carter @ 2015-12-08 22:20 ` Simon Wright 1 sibling, 0 replies; 34+ messages in thread From: Simon Wright @ 2015-12-08 22:20 UTC (permalink / raw) Serge Robyns <serge.robyns@gmail.com> writes: > My question is then, how can I easily manage this from a source > management perspective. With GNAT/GNAT Project, there's package Naming: roughly, package Naming is case Option is when "simple" => for Body ("Pack.Child") use "pack-child-simple.adb"; when "complex" => for Body ("Pack.Child") use "pack-child-complex.adb"; end case; end Naming; ^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2015-12-15 7:26 UTC | newest] Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-12-08 16:45 I'm facing an issue with: call to abstract procedure must be dispatching Serge Robyns 2015-12-08 16:59 ` G.B. 2015-12-08 17:04 ` Dmitry A. Kazakov 2015-12-08 17:24 ` Serge Robyns 2015-12-08 17:42 ` Dmitry A. Kazakov 2015-12-08 18:00 ` Serge Robyns 2015-12-08 18:22 ` Dmitry A. Kazakov 2015-12-08 20:21 ` Serge Robyns 2015-12-08 21:08 ` Dmitry A. Kazakov 2015-12-08 21:55 ` Serge Robyns 2015-12-08 22:43 ` Serge Robyns 2015-12-08 22:55 ` Jeffrey R. Carter 2015-12-09 23:03 ` Randy Brukardt 2015-12-10 8:38 ` Serge Robyns 2015-12-10 23:43 ` Randy Brukardt 2015-12-09 8:48 ` Dmitry A. Kazakov 2015-12-09 10:53 ` G.B. 2015-12-09 12:03 ` Dmitry A. Kazakov 2015-12-09 13:42 ` G.B. 2015-12-09 14:23 ` Dmitry A. Kazakov 2015-12-08 22:55 ` Jeffrey R. Carter 2015-12-08 23:04 ` Serge Robyns 2015-12-08 23:42 ` Jeffrey R. Carter 2015-12-09 13:40 ` Jere 2015-12-09 13:48 ` G.B. 2015-12-09 15:33 ` Jere 2015-12-13 21:37 ` Shark8 2015-12-14 2:20 ` Jeffrey R. Carter 2015-12-15 7:26 ` Shark8 2015-12-08 17:30 ` Jeffrey R. Carter 2015-12-08 17:48 ` Serge Robyns 2015-12-08 18:46 ` Jeffrey R. Carter 2015-12-08 20:28 ` Serge Robyns 2015-12-08 22:20 ` Simon Wright
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox