From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,308a261188818cce X-Google-Attributes: gid103376,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news2.google.com!postnews.google.com!i13g2000prf.googlegroups.com!not-for-mail From: Adam Beneschan Newsgroups: comp.lang.ada Subject: Re: Pointers explained? Date: Mon, 30 Jul 2007 12:36:35 -0700 Organization: http://groups.google.com Message-ID: <1185824195.711745.136860@i13g2000prf.googlegroups.com> References: <1185817996.143086.317990@g12g2000prg.googlegroups.com> <1185818189.689914.159900@x40g2000prg.googlegroups.com> NNTP-Posting-Host: 66.126.103.122 Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" X-Trace: posting.google.com 1185824196 20917 127.0.0.1 (30 Jul 2007 19:36:36 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: Mon, 30 Jul 2007 19:36:36 +0000 (UTC) In-Reply-To: <1185818189.689914.159900@x40g2000prg.googlegroups.com> User-Agent: G2/1.0 X-HTTP-UserAgent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.12) Gecko/20050922 Fedora/1.7.12-1.3.1,gzip(gfe),gzip(gfe) Complaints-To: groups-abuse@google.com Injection-Info: i13g2000prf.googlegroups.com; posting-host=66.126.103.122; posting-account=ps2QrAMAAAA6_jCuRt2JEIpn5Otqf_w0 Xref: g2news2.google.com comp.lang.ada:1275 Date: 2007-07-30T12:36:35-07:00 List-Id: On Jul 30, 10:56 am, shaunpatter...@gmail.com wrote: > Hi > > I'm still relatively new to Ada -- coming from a strong C++ > background. Some of this may seem stupid - and feel free to point it > out to me. I don't have a strong C++ background, so some of my response may seem stupid. > I am using Ada 95 - GNAT Pro 3.16a1 > > I have a message factory I've been screwing around with (converting > it > from Ada to C++). > > I basically have an abstract message type: > > type Message is abstract tagged null record; > type Message_Class is access all Message'Class; > > then all other messages are derived from this. > > Now from my factory create method, I'm returning a Message_Class. > This message_class > is stored in another record to be handled again later: > > type CallbackEvent is > record > msg : Message_Class; > ... > end record; > > Now my basic problem -- or not really a problem -- is that to create > the Message_Class I have to use > "new" and allocate and deallocate memory. > > Now - my solution was to re-write my message factory to return a type > Message'Class. However, I went > to change my callback structure to: > > type CallbackEvent is > record > msg : Message'Class > end record; > > and I get the message "unconstrained subtype in component declaration" > > So, I'm assuming the compiler just doesn't know how to big this > message is. That's pretty much correct. Technically, "Message'Class" is an "indefinite subtype"; other examples of indefinite subtypes would be "string", which is an unconstrained array that you haven't given the bounds for. You can't have a variable or component declaration that looks like X : String; The reason is basically the same, though: the compiler wouldn't know how much to allocate. Of course the compiler can't know how big a Message'Class is, because the object could be *any* type that you derived from Message, including a type in a package that you haven't even written yet. > that's why I believe i should use a pointer (the Message_Class type) I think you're right. Others will probably point out how the fact that Message_Class is a pointer ought to be hidden from users of your Message package. But at some point, a pointer will likely be needed. > However, > I cannot find a way of converting between a Message'Class to a > Message_Class; > > It appears that the only way to get a Message_Class is by some where > in the > code using a "new" > > Is this assumption correct? Not quite. The other thing you can do is use 'Access on a variable whose type in Message'Class, if the variable is declared "aliased": Z : aliased Message_1; now Z'Access gives you a "pointer" to Z, which can used in a context where a Message_Class is expected. (Sort of like the & operator in C+ +.) But this is unlikely to help you much. It won't work to declare Z as a local variable in your factory function---Ada won't let you do that, and it shouldn't, because the memory used for Z will be on the stack, and the stack will disappear when your factory function exits and will be overwritten by the next subroutine you call. C++ will of course let you do that, with the likely result that any machine that runs the program will end up screwing itself up and probably become part of a botnet that will fill MY inbox with messages about fake Rolexes and Canadian drugs and web sites that tell me how I can enlarge my ... um ... bowling score. So I'd definitely prefer that you use Ada. Anyway, there may be some cases where returning an access to a global variable may be appropriate, but those are probably not the norm. So some sort of dynamic allocation (via "new") will likely be necessary, at some point. A couple things I might point out: First, if you have a factory function [say Create_Message] that returns Message'Class (i.e. *not* a pointer), you can declare variables of type Message'Class as long as you use the function (or some other function call, maybe) as an initializer: Var1 : Message'Class := Create_Message (...); When you do this, Create_Message can return any type derived from Message. At that point, the program will determine how much space is needed for Var1 and allocate it. Var1's type can't be changed after that, though; if Create_Message returns a Message_3, Var1's type will be fixed at Message_3, and you can't, say, assign Var1 := Y; unless Y also happens to have type Message_3. (If it's the wrong type, a Constraint_Error exception will be raised.) This applies to record components also; by default, the function will be called to set up the component when any variable of that record type is created. The variable will be allocated dynamically, but you won't need a "new" to allocate it and you won't have to worry about deallocation---that will all be taken care of automatically. Second, if there's some place in the program where you *know* what type that factory function will return, you can take advantage of that, e.g.: type Record_With_Message_4 is record Msg : Message_4; end record; R : Record_With_Message_4; ... R.Msg := Create_Message (...); Now the code will call Create_Message but then check to make sure the type returned is actually Message_4. If it isn't, a Constraint_Error exception will be raised. If it is, the message will be set up, and Msg will probably not be dynamically allocated. Anyway, those are a couple cases where you can use your factory function without "new". Because of that, I'd recommend writing an overloaded function: function Create_Message (...) return Message'Class; function Create_Message (...) return Message_Class; where the body of the second function would be function Create_Message (...) return Message_Class is begin return new Message'Class (Create_Message (...)); end; or something to that effect. The compiler will be able to tell from context which Create_Message, the one that returns the Message'Class or the one that returns the pointer, is being called, unless the context is ambiguous. Anyway, I'm sure others have some other ideas they can give you. In places where "new" will be necessary somewhere, you may want to look into controlled types to get the deallocation to be done automatically. But this message is already real long, so I'll let others enlighten you with the details of that. Hope at least some of this is helpful, -- Adam