* Initialization Params for Controlled Types @ 1994-10-27 9:44 Angel Alvarez 1994-10-27 14:27 ` Tucker Taft 1994-10-27 23:06 ` Robert Dewar 0 siblings, 2 replies; 13+ messages in thread From: Angel Alvarez @ 1994-10-27 9:44 UTC (permalink / raw) Given that Initialization for Controlled types only takes a single parameter, the Object to initialize, how can one actually pass at run-time other data to help initialize the object in one way or another? ... Passing them as record fields of the object itself? It is unclear to me how this could be done for Controlled objects (by means of default field initialization for the record type itself?) and at any rate it seems like this solution might introduce "excess baggage" for the whole life of the object, just to have on hand the data required at initialization time ... Will anybody please like to comment? Thank you for your help, Angel Alvarez ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-10-27 9:44 Initialization Params for Controlled Types Angel Alvarez @ 1994-10-27 14:27 ` Tucker Taft 1994-11-04 13:44 ` John English 1994-10-27 23:06 ` Robert Dewar 1 sibling, 1 reply; 13+ messages in thread From: Tucker Taft @ 1994-10-27 14:27 UTC (permalink / raw) In article <CyBsE5.GFs@dit.upm.es>, Angel Alvarez <aalvarez@maja> wrote: >Given that Initialization for Controlled types only takes a single >parameter, the Object to initialize, how can one actually pass >at run-time other data to help initialize the object in one way or >another? ... Passing them as record fields of the object itself? The normal way to "construct" an object in a parameterized way in Ada is with a function. There is no separate notion of "constructor" -- functions are used to do construction. Internally, a function would typically use aggregates and/or allocators to complete the job of construction. E.g.: X : My_Type := Foo(a, b, c); This means that "constructors" in Ada are just normal functions, and their name can give you a clue as to what they are doing (unlike in some other unnamed OO languages ;-). Initialize is really for *default* initialization only, where parameterization (beyond discriminants that control size/shape) is generally unnecessary. The one place where a function doesn't work for parameterized initialization is for an object of a limited type (and even that is not a problem if you generally use access values to refer to such objects). In this case, one way to parameterize the initialization of a limited object is by discriminants. In Ada 9X, discriminants are more general, and can be of an access type. This allows you to "pass in" essentially anything to the (default) initialization (using 'Access if necessary). For a limited object, it is also generally reasonable to call an explicit initialization procedure with any number of parameters. Again, the primary goal of Initialize is to provide *default* initialization. Explicit parameterized initialization can be performed in other ways (via functions or procedures). >It is unclear to me how this could be done for Controlled objects >(by means of default field initialization for the record type itself?) >and at any rate it seems like this solution might introduce "excess baggage" >for the whole life of the object, just to have on hand the data >required at initialization time ... Will anybody please like to comment? See above. It is true that for limited objects, the data used for default initialization needs to be accessible via the discriminants. But again, remember we are talking about default initialization. Explicit parameterized initialization can be accomplished using functions and/or procedures. >Thank you for your help, >Angel Alvarez S. Tucker Taft stt@inmet.com Ada 9X Mapping/Revision Team Intermetrics, Inc. Cambridge, MA 02138 P.S. Even if the size of the object being constructed is large, there is no need for a "constructor" concept distinct from functions. Functions are generally just as efficient in decent implementations. In most implementations, if a function returns a large object, the caller passes in the address of a location where to put the result. When used in an initialization context, this location can be the target object itself (since the object doesn't exist yet, there is no harm in using it as the "temp" in which to put the return value). Furthermore, it has become quite common for compilers to implement a "return" optimization where if all return statements return the same local variable, then that "local" variable actually resides in the return "temp" allocated by the caller. The net effect is that the function "constructs" the result directly in its final resting place, and no extra copying (or "adjusting") is performed. -T ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-10-27 14:27 ` Tucker Taft @ 1994-11-04 13:44 ` John English 1994-11-04 22:16 ` Norman H. Cohen 0 siblings, 1 reply; 13+ messages in thread From: John English @ 1994-11-04 13:44 UTC (permalink / raw) Tucker Taft (stt@spock.camb.inmet.com) wrote: : For a limited object, it is also generally reasonable : to call an explicit initialization procedure with any number : of parameters. Again, the primary goal of Initialize is to : provide *default* initialization. Explicit parameterized : initialization can be performed in other ways (via functions : or procedures). There is one subtle danger with using procedures as "constructors". Imagine the following: type BankAccount is tagged private; procedure Open (Account : in out BankAccount; Name : in String); Here Open opens a bank account in a particular name. Now if you derive a new type CurrentAccount from BankAccount to produce a BankAccount that allows overdrafts, you might have this: type CurrentAccount is new BankAccount with private; procedure Open (Account : in out CurrentAccount; Name : in String; Overdraft : in Money); Here's where the danger is: the first procedure (Open for BankAccount) is a primitive of BankAccount and will be inherited by CurrentAccount. It's therefore possible to initialise a CurrentAccount as if it were a BankAccount, which might leave the overdraft limit uninitialised: C : CurrentAccount; ... Open (C, "Fred Bloggs"); -- oops! Okay, you can provide a default overdraft limit of 0.00, so this is not an entirely convincing example; the point is that there is a danger here which is non-obvious, since you might forget that there is a second Open for CurrentAccounts which is provided due to inheritance. C++ sidesteps this problem by making constructors non-inheritable. The only way to accomplish this in 9X is to make sure that Open isn't a primitive (e.g. by shoving it in a child package) which is fairly painful. -- ------------------------------------------------------------------------------- John English | Thoughts for the day: Dept. of Computing | - People who live in windowed environments University of Brighton | shouldn't cast pointers E-mail: je@brighton.ac.uk | - In C++ only your friends can access your Fax: 0273 642405 | private parts ------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-04 13:44 ` John English @ 1994-11-04 22:16 ` Norman H. Cohen 1994-11-05 15:01 ` Cyrille Comar ` (2 more replies) 0 siblings, 3 replies; 13+ messages in thread From: Norman H. Cohen @ 1994-11-04 22:16 UTC (permalink / raw) In article <1994Nov4.134412.10010@unix.brighton.ac.uk>, je@unix.brighton.ac.uk (John English) writes: |> There is one subtle danger with using procedures as "constructors". |> Imagine the following: |> type BankAccount is tagged private; |> procedure Open (Account : in out BankAccount; |> Name : in String); |> |> Here Open opens a bank account in a particular name. Now if you |> derive a new type CurrentAccount from BankAccount to produce a |> BankAccount that allows overdrafts, you might have this: |> type CurrentAccount is new BankAccount with private; |> procedure Open (Account : in out CurrentAccount; |> Name : in String; |> Overdraft : in Money); |> |> Here's where the danger is: the first procedure (Open for BankAccount) |> is a primitive of BankAccount and will be inherited by CurrentAccount. |> It's therefore possible to initialise a CurrentAccount as if it were |> a BankAccount, which might leave the overdraft limit uninitialised: |> C : CurrentAccount; |> ... |> Open (C, "Fred Bloggs"); -- oops! |> |> Okay, you can provide a default overdraft limit of 0.00, so this is |> not an entirely convincing example; the point is that there is a |> danger here which is non-obvious, since you might forget that there |> is a second Open for CurrentAccounts which is provided due to inheritance. There is a good reason the example is not convincing: It violates the principle that the inheritance hierarchy should reflect an IS-A relationship. CurrentAccount should be derived from BankAccount only if a "current account" is a special kind of "bank account". If it is, then it should have all the properties of bank accounts--including the ability to open it by specifying just a name. After all, when you add CurrentAccount to the class rooted at BankAccount, you are asserting that all the primitive operations of BankAccount can be applied to BankAccount'Class objects with any tag. You can look at this as a problem with the operation Open or a problem with the hierarchy. If you view it as a problem with the hierarchy, a solution is to introduce a new abstract class, say Universal_Account, that declares the operations that truly are meaningful for ALL bank accounts, and derive BankAccount and CurrentAccount from Universal_Account. Open is not one of these operations. Two distinct versions of Open would be declared for the two derived types. |> C++ sidesteps this problem by making constructors non-inheritable. |> The only way to accomplish this in 9X is to make sure that Open isn't |> a primitive (e.g. by shoving it in a child package) which is fairly |> painful. Note that the danger of an inadvertent error could be avoided in Ada 9X by making the constructor a function returning a BankAccount value: function New_Bank_Account (Name: String) return BankAccount; Then the version inherited by CurrentAccount would be abstract. Calls on the inherited version would be illegal, thus alerting the programmer to the fact that something is amiss. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-04 22:16 ` Norman H. Cohen @ 1994-11-05 15:01 ` Cyrille Comar 1994-11-07 10:32 ` John English 1994-11-07 9:08 ` John English 1994-11-10 15:23 ` John English 2 siblings, 1 reply; 13+ messages in thread From: Cyrille Comar @ 1994-11-05 15:01 UTC (permalink / raw) ncohen@watson.ibm.com (Norman H. Cohen) writes: : In article <1994Nov4.134412.10010@unix.brighton.ac.uk>, : je@unix.brighton.ac.uk (John English) writes: : : |> There is one subtle danger with using procedures as "constructors". : |> Imagine the following: : |> type BankAccount is tagged private; : |> procedure Open (Account : in out BankAccount; : |> Name : in String); : |> : |> Here Open opens a bank account in a particular name. Now if you : |> derive a new type CurrentAccount from BankAccount to produce a : |> BankAccount that allows overdrafts, you might have this: : |> type CurrentAccount is new BankAccount with private; : |> procedure Open (Account : in out CurrentAccount; : |> Name : in String; : |> Overdraft : in Money); : |> : |> Here's where the danger is: the first procedure (Open for BankAccount) : |> is a primitive of BankAccount and will be inherited by CurrentAccount. : |> It's therefore possible to initialise a CurrentAccount as if it were : |> a BankAccount, which might leave the overdraft limit uninitialised: : |> C : CurrentAccount; : |> ... : |> Open (C, "Fred Bloggs"); -- oops! : |> : |> Okay, you can provide a default overdraft limit of 0.00, so this is : |> ... : |> C++ sidesteps this problem by making constructors non-inheritable. : |> The only way to accomplish this in 9X is to make sure that Open isn't : |> a primitive (e.g. by shoving it in a child package) which is fairly : |> painful. you have an exact equivalent in Ada. If you don't want to make Open a primitive operation of BankAccount define it as a classwide operation: procedure Open (Account : in out BankAccount'Class; Name : in String); It will not be inherited! I do not say that it is the perfect solution for your problem, my point is that it is exactly equivalent to defining Open as a non-virtual member function in C++. -- ------------------------------------------------------------------------ Cyrille Comar, E-mail: comar@cs.nyu.edu Gnat Project US phone: (212) 998-3489 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-05 15:01 ` Cyrille Comar @ 1994-11-07 10:32 ` John English 0 siblings, 0 replies; 13+ messages in thread From: John English @ 1994-11-07 10:32 UTC (permalink / raw) Cyrille Comar (comar@cs.nyu.edu) wrote: : procedure Open (Account : in out BankAccount'Class; : Name : in String); : It will not be inherited! I do not say that it is the perfect solution for your : problem, my point is that it is exactly equivalent to defining Open as a : non-virtual member function in C++. But surely Open can still be applied to a CurrentAccount (which is in BankAccount'Class) so this doesn't actually help much. -- ------------------------------------------------------------------------------- John English | Thoughts for the day: Dept. of Computing | - People who live in windowed environments University of Brighton | shouldn't cast pointers E-mail: je@brighton.ac.uk | - In C++ only your friends can access your Fax: 0273 642405 | private parts ------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-04 22:16 ` Norman H. Cohen 1994-11-05 15:01 ` Cyrille Comar @ 1994-11-07 9:08 ` John English 1994-11-10 15:23 ` John English 2 siblings, 0 replies; 13+ messages in thread From: John English @ 1994-11-07 9:08 UTC (permalink / raw) Norman H. Cohen (ncohen@watson.ibm.com) wrote: : In article <1994Nov4.134412.10010@unix.brighton.ac.uk>, : je@unix.brighton.ac.uk (John English) writes: : |> C++ sidesteps this problem by making constructors non-inheritable. : |> The only way to accomplish this in 9X is to make sure that Open isn't : |> a primitive (e.g. by shoving it in a child package) which is fairly : |> painful. : Note that the danger of an inadvertent error could be avoided in Ada 9X : by making the constructor a function returning a BankAccount value: : function New_Bank_Account (Name: String) return BankAccount; Yes, I should have declared it "type BankAccount is tagged limited private" since I assume that copying a bank account shouldn't be allowed -- my mistake. If this is so, you can't use a function for initialisation. -- ------------------------------------------------------------------------------- John English | Thoughts for the day: Dept. of Computing | - People who live in windowed environments University of Brighton | shouldn't cast pointers E-mail: je@brighton.ac.uk | - In C++ only your friends can access your Fax: 0273 642405 | private parts ------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-04 22:16 ` Norman H. Cohen 1994-11-05 15:01 ` Cyrille Comar 1994-11-07 9:08 ` John English @ 1994-11-10 15:23 ` John English 1994-11-11 10:44 ` Robb Nebbe 1994-11-14 21:19 ` Norman H. Cohen 2 siblings, 2 replies; 13+ messages in thread From: John English @ 1994-11-10 15:23 UTC (permalink / raw) Norman H. Cohen (ncohen@watson.ibm.com) wrote: : There is a good reason the example is not convincing: It violates the : principle that the inheritance hierarchy should reflect an IS-A : relationship. CurrentAccount should be derived from BankAccount only if : a "current account" is a special kind of "bank account". If it is, then : it should have all the properties of bank accounts--including the ability : to open it by specifying just a name. After all, when you add : CurrentAccount to the class rooted at BankAccount, you are asserting that : all the primitive operations of BankAccount can be applied to : BankAccount'Class objects with any tag. I've had to think about this one for a while -- I didn't find it convincing but couldn't figure out why. The idea is that yes, a current account is a kind of bank account. BankAccount is an "idealised" bank account -- no transaction will be refused, no charges will be made, no interest is payable. Classes like CurrentAccount can then extend this into something more realistic and so can SavingsAccount etc. All these account types will have the common properties inherited from BankAccount (deposit, withdraw, check balance, print statement...) However, extending a class involves adding extra members to it and these must be initialised somehow. If it isn't possible to do this with a simple default in the type declaration, you have to call an initialisation routine. The question is really whether Open should be a primitive. Each different derivation of BankAccount will have been extended in a different way and so will require different initialisation, using different parameters. It's quite hard to prevent Open being a primitive (it needs declaring elewhere, so you have to provide an elsewhere for it) but it's incorrect to consider it as an operation which can be applied to any BankAccount'Class since it is responsible for class-specific initialisation. : You can look at this as a problem with the operation Open or a problem : with the hierarchy. If you view it as a problem with the hierarchy, a : solution is to introduce a new abstract class, say Universal_Account, : that declares the operations that truly are meaningful for ALL bank : accounts, and derive BankAccount and CurrentAccount from : Universal_Account. Open is not one of these operations. Two distinct : versions of Open would be declared for the two derived types. Changing the hierarchy only postpones the problem: Open will end up as a primitive of CurrentAccount, and any derivation from CurrentAccount will therefore inherit it. Open is indeed not one of those universally sensible operations, so it needs to be provided as a non-primitive, and it needs to be provided anew for every derivation. And each new version will have to call its parent's version of Open to do the parent-part specific initialisation. This is where it gets messy and error-prone. And of course, there is no way in 9X to force Open to be called and you have to take extra care (e.g. an Initialised flag) to prevent it being called more than once. C++ constructors are a neat solution to these problems -- they are always called exactly once per object, they are not inherited, they automatically call the base class constructor, and they can have parameters. Ada 9X requires a lot more effort to avoid subtle errors IMHO. -- ------------------------------------------------------------------------------- John English | Thoughts for the day: Dept. of Computing | - People who live in windowed environments University of Brighton | shouldn't cast pointers E-mail: je@brighton.ac.uk | - In C++ only your friends can access your Fax: 0273 642405 | private parts ------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-10 15:23 ` John English @ 1994-11-11 10:44 ` Robb Nebbe 1994-11-14 21:19 ` Norman H. Cohen 1 sibling, 0 replies; 13+ messages in thread From: Robb Nebbe @ 1994-11-11 10:44 UTC (permalink / raw) In article <1994Nov10.152352.27015@unix.brighton.ac.uk>, je@unix.brighton.ac.uk (John English) writes: |> Norman H. Cohen (ncohen@watson.ibm.com) wrote: |> : There is a good reason the example is not convincing: It violates the |> : principle that the inheritance hierarchy should reflect an IS-A |> : relationship. CurrentAccount should be derived from BankAccount only if |> : a "current account" is a special kind of "bank account". If it is, then |> : it should have all the properties of bank accounts--including the ability |> : to open it by specifying just a name. After all, when you add |> : CurrentAccount to the class rooted at BankAccount, you are asserting that |> : all the primitive operations of BankAccount can be applied to |> : BankAccount'Class objects with any tag. |> |> I've had to think about this one for a while -- I didn't find it convincing |> but couldn't figure out why. The idea is that yes, a current account is a |> kind of bank account. BankAccount is an "idealised" bank account -- no |> transaction will be refused, no charges will be made, no interest is payable. |> Classes like CurrentAccount can then extend this into something more |> realistic and so can SavingsAccount etc. All these account types will |> have the common properties inherited from BankAccount (deposit, withdraw, |> check balance, print statement...) If it is an "idealized" bank account, i.e. it describes what all bank accounts have in common then it should be abstract and operations like open should also be abstract. A similar example is mamals. A mamal is an idealization of the commonality between a certain number of animals but there is no one animal that is "just" a mamal; they are always something more. You need the right tool for the job. Just because you can come up with a solution in C++ using constructors does not mean it is a good solution. In fact I would program the solution in C++ much the same way I would in Ada. Pounding a nail in with a screwdriver is not a good solution; just because C++ happens to include a really big screwdriver thus making it feasible to pound a nail in with it doesn't change the fact that it would still be easier to use the hammer (which C++ also provides). |> And of course, there is no way in 9X to force Open to be called and you |> have to take extra care (e.g. an Initialised flag) to prevent it being |> called more than once. C++ constructors are a neat solution to these |> problems -- they are always called exactly once per object, they are |> not inherited, they automatically call the base class constructor, and |> they can have parameters. Ada 9X requires a lot more effort to avoid |> subtle errors IMHO. Actually all Ada 9X requires is a better design. The actual account type probably shouldn't even be visible. What should be visible is a type Account_Number which is not limited private that refers to a real account. This more closely models the real world and resolves any of the problems I have thought of. Maybe you see some that I haven't? - Robb Nebbe ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-10 15:23 ` John English 1994-11-11 10:44 ` Robb Nebbe @ 1994-11-14 21:19 ` Norman H. Cohen 1994-11-14 18:35 ` Robert I. Eachus 1994-11-16 21:45 ` Matt Kennel 1 sibling, 2 replies; 13+ messages in thread From: Norman H. Cohen @ 1994-11-14 21:19 UTC (permalink / raw) In article <1994Nov10.152352.27015@unix.brighton.ac.uk>, je@unix.brighton.ac.uk (John English) writes: |> Norman H. Cohen (ncohen@watson.ibm.com) wrote: ... |> : You can look at this as a problem with the operation Open or a problem |> : with the hierarchy. If you view it as a problem with the hierarchy, a |> : solution is to introduce a new abstract class, say Universal_Account, |> : that declares the operations that truly are meaningful for ALL bank |> : accounts, and derive BankAccount and CurrentAccount from |> : Universal_Account. Open is not one of these operations. Two distinct |> : versions of Open would be declared for the two derived types. |> |> Changing the hierarchy only postpones the problem: Open will end up as |> a primitive of CurrentAccount, and any derivation from CurrentAccount |> will therefore inherit it. Open is indeed not one of those universally |> sensible operations, so it needs to be provided as a non-primitive, and |> it needs to be provided anew for every derivation. And each new version |> will have to call its parent's version of Open to do the parent-part |> specific initialisation. This is where it gets messy and error-prone. This is a more general problem that arises with OOP in any language: Often the need arises to define a new class B that has MOST, BUT NOT ALL of the features of some other class A. The problem is that the author of A, not having been omnisicient, failed to recognize it as a special case of a more general problem for which other special cases would arise in the future. If you don't have the authority to modify the definition of class A, you end up duplicating a lot of code. If you do have such authority, you can FACTOR the common features of A and B into a new class C, redefine A as a specialization of C, and define B as another specialization of C. In your example, CurrentAccount is A and the unanticipated derivation from CurrentAccount that you hypothesize is B. (Had the derivation been anticipated, an abstract class playing the role of C--having all the features of CurrentAccount meaningful even for specializations of current accounts, but not features such as Open--would have been written in the first place, and a concrete class would have been defined by deriving from this abstract class and adding an appropriate Open operation. |> And of course, there is no way in 9X to force Open to be called and you |> have to take extra care (e.g. an Initialised flag) to prevent it being |> called more than once. C++ constructors are a neat solution to these |> problems -- they are always called exactly once per object, they are |> not inherited, they automatically call the base class constructor, and |> they can have parameters. Ada 9X requires a lot more effort to avoid |> subtle errors IMHO. I'm confused now about whether you're envisioning Open as a function or a procedure. (It's been a while and I don't have the full example at hand.) As others have pointed out, a function with a classwide result subtype, function Open_Current_Account (...) return Account'Class; will not be inherited. However, calls on functions are not intrinsically associated with objects. Your remark about exactly one call on open per object sounds like an initialization PROCEDURE. This can be provided by controlled types. Like C++ constructors, the Initialize procedure of a controlled type is called once per object, and can be parameterized using discriminants. By default, a C++ constructor is not inherited, but a default constructor that implicitly calls the base-class constructor is implicitly created, which amounts to pretty much the same thing. In Ada 9X the parent's Initialize procedure is not implicitly invoked by the derived type's Initialize procedure, but it is straightforward to make an explicit call. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-14 21:19 ` Norman H. Cohen @ 1994-11-14 18:35 ` Robert I. Eachus 1994-11-16 21:45 ` Matt Kennel 1 sibling, 0 replies; 13+ messages in thread From: Robert I. Eachus @ 1994-11-14 18:35 UTC (permalink / raw) Norm said something which is true, but accidently misleading: > As others have pointed out, a function with a classwide result subtype, > function Open_Current_Account (...) return Account'Class; > will not be inherited. However, calls on functions are not intrinsically > associated with objects. Your remark about exactly one call on open per > object sounds like an initialization PROCEDURE. This can be provided by > controlled types... The more natural way to write this: function Open_Current_Account (...) return Account; ...will be inherited as an abstract operation which must be explicitly overridden. However, this is also almost (but not quite in Ada 9X) useless for most types of objects, including accounts, where objects should not be assignable. Better would be: type Account is abstract tagged limited private; type Account_Kind is String(1..8); -- allow extension. type Account_Ref is access Account'CLASS; ... function Open_Account (Kind: Account_Kind;...) return Account_Ref; -- and probably in a child package: type Checking_Account is new Account with private; ... Now the operation Open_Account will be derived if you derive a type from Account_Ref for some reason, but it will need a to explicitly dispatch for each type of account: ... if Kind = "CHECKING" then return Open_Checking_Account(...); If you are clever and lazy ;-), you will define other operations on accounts--which may or may not be an abstract type, as procedures or functions with an operand of type Account. If, these are intended as operations on all Account types, the templates (abstract or otherwise) should be defined in the same package as Account, so they are inherited, potentially dispatching, operations. Now in the parent package add, for instance: function Balance(AR: Account_Ref) return Money; pragma Inline(Balance); with the full definition: function Balance(AR: Account_Ref) return Money is begin return Balance(AR.all); end Balance; -- This function raises Constraint_Error not Program_Error if the -- pointer is null... And with a good optimizing compiler all the magic dispatching glue will disappear during inlining. Where does this leave us with respect to the original "problem"? The code naturally works the way it is supposed to. Account type is either a parameter to the open function or is implicit in the name, you can chose the one that is appropriate. Non-constructor functions and procedures are derived and dispatching unless overridden, and the distinction between objects and their names only matters where it matters, possibly only when creating an account. -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-11-14 21:19 ` Norman H. Cohen 1994-11-14 18:35 ` Robert I. Eachus @ 1994-11-16 21:45 ` Matt Kennel 1 sibling, 0 replies; 13+ messages in thread From: Matt Kennel @ 1994-11-16 21:45 UTC (permalink / raw) Norman H. Cohen (ncohen@watson.ibm.com) wrote: : This is a more general problem that arises with OOP in any language: : Often the need arises to define a new class B that has MOST, BUT NOT ALL : of the features of some other class A. The problem is that the author of : A, not having been omnisicient, failed to recognize it as a special case : of a more general problem for which other special cases would arise in : the future. If you don't have the authority to modify the definition of : class A, you end up duplicating a lot of code. If you do have such : authority, you can FACTOR the common features of A and B into a new class : C, redefine A as a specialization of C, and define B as another : specialization of C. If you separate subtyping from implementation inheritance there is no problem doing what you want. In B, you inherit just some of the implementation of A. This is not a problem because by doing so you do not promise to uphold B as a subtype of A. You then write an abstract class "C" that fits over A and B (with no implementation) that abstracts the commonality. A and B are both subtypes of C, but neither is a subtype of each other. : -- : Norman H. Cohen ncohen@watson.ibm.com -- -Matt Kennel mbk@inls1.ucsd.edu -Institute for Nonlinear Science, University of California, San Diego -*** AD: Archive for nonlinear dynamics papers & programs: FTP to -*** lyapunov.ucsd.edu, username "anonymous". ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Initialization Params for Controlled Types 1994-10-27 9:44 Initialization Params for Controlled Types Angel Alvarez 1994-10-27 14:27 ` Tucker Taft @ 1994-10-27 23:06 ` Robert Dewar 1 sibling, 0 replies; 13+ messages in thread From: Robert Dewar @ 1994-10-27 23:06 UTC (permalink / raw) to pass parameters to initialization routines, use discriminants, works nicely, that's how we do storage pools in GNAT. ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~1994-11-16 21:45 UTC | newest] Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 1994-10-27 9:44 Initialization Params for Controlled Types Angel Alvarez 1994-10-27 14:27 ` Tucker Taft 1994-11-04 13:44 ` John English 1994-11-04 22:16 ` Norman H. Cohen 1994-11-05 15:01 ` Cyrille Comar 1994-11-07 10:32 ` John English 1994-11-07 9:08 ` John English 1994-11-10 15:23 ` John English 1994-11-11 10:44 ` Robb Nebbe 1994-11-14 21:19 ` Norman H. Cohen 1994-11-14 18:35 ` Robert I. Eachus 1994-11-16 21:45 ` Matt Kennel 1994-10-27 23:06 ` Robert Dewar
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox