jamshid@ut-emx.uucp (Jamshid Afshar) (05/17/91)
In article <DSOUZA.91May3150307@gwen.cad.mcc.com> dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: >All data members which are objects have to be initialized before >the body of the constructor. There is this basic problem with >computing arguments for constructors in the initialization list: > > You cannot compute any temporaries (e.g. common subexpressions) > which are needed for initialization of such data members. <A stripped-down version of his example follows.> struct A { A(int m, int n); }; struct B { A a1; B(int j) : a1( F1(j), F1(j)+1 ) {} // F1(j) is very time-cosuming }; >// Here is what I would like to do, in a completely fictitous >// and probably highly ambiguous syntax: > >B::B(int j) >: > { int f1 = F1(j); > a1(f1, f1+1); > } >{ > .. actual constructor body >} > >Using globals, "memoizing" functions, etc. is not a solution to this. > >Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com When I need to do general compuation during class member initialization I use static member functions, but they can't help in your example. I encountered a similar problem in which I had a Base class constructor which took two parameters and a Derived class always passed it duplicate items (ie. Base(foo(j), foo(j))) where foo(j) was time-consuming. What I did was add another constructor to Base which only took one parameter. This was a good move as it turned out I needed to do this kind of construction just as often as the original way. Another solution if you do not want to mess with the Base class is to create an intermediate class. One solution to your example is to define class A above to be: struct A { A(int m, int n); A(int m); // construct an A whose n is one more than m }; Yes, I know this is obvious and it's not always so simple, but I've always found a solution like this to not only work, but actually end up reflecting the problem better. I like the current constructor format: Class(parms) : Base(params), member1(params), member2(params) { ...body... } because it makes it clear that you are constructing objects as opposed to executing statements in the member initialization list. When you are inside curly-braces you shouldn't have to worry whether an object has actually been constructed (which is one reason I believe goto's were restricted in C++). I think introducing two blocks for constructors would lead to errors using objects before they are constructed. Actually, this is already a problem because at least my compiler (Borland C++ 2.0) does not catch the use of an object before it's constructor call in the member initalization list. I don't believe the ARM requires it to be flagged as an error, but it sure would be a nice warning (also remember members are initalized in declaration order, not their order in the member initalization list). Speaking of special warnings for constructors, the use of virtual functions in a constructor would also be nice. Example taken from BC++ bug list at sun.soe.clarkson.edu in the file ~ftp/pub/Turbo-C++/bug-report. class String { char* s; unsigned Len; public: String(const char* p); // char* --> String //... }; String::String(const char* p) : Len(strlen(p)), s(new char[Len+1]) { /////// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ strcpy(s, p); /////// Missed feature: TC++ should warn here } /////// Ex: `Len used before it is initialized' Jamshid Afshar jamshid@emx.utexas.edu
davidm@uunet.UU.NET (David S. Masterson) (05/18/91)
>>>>> On 3 May 91 20:03:07 GMT, dsouza@gwen.cad.mcc.com (Desmond Dsouza) said: Desmond> In article <71952@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) Desmond> writes: Jim> Again, if you want to factor out common code from constructors, I Jim> recommend you follow Bjarne's suggestion: write a private init() routine Jim> which is called from within each constructor. Desmond> Sorry, but I dont think this works in general. Desmond> All data members which are objects have to be initialized before Desmond> the body of the constructor. There is this basic problem with Desmond> computing arguments for constructors in the initialization list: Desmond> You cannot compute any temporaries (e.g. common subexpressions) which Desmond> are needed for initialization of such data members. Desmond> class A { public: A(int m, int n); }; Desmond> class B { Desmond> public: Desmond> B (int j); Desmond> private: Desmond> A a1; // initialized with F1(j), F1(j)+1 for Desmond> //some horribly complex F1 Desmond> }; Desmond> B::B(int j) Desmond> // how can I share the computation of F1(j) here ??? Desmond> : a1 (F1(j), F1(j)+1) Desmond> { Desmond> // this part does not help: I cannot modify a1 at this point. Desmond> } WARNING: Novice code coming... Would a possible solution to this be: B::B(int j) : a1() { int x = F1(j), y = x + 1; A temp(x,y); a1 = temp; // why can't you modify a1 using copy assignment? } -- ==================================================================== David Masterson Consilium, Inc. (415) 691-6311 640 Clyde Ct. uunet!cimshop!davidm Mtn. View, CA 94043 ==================================================================== "If someone thinks they know what I said, then I didn't say it!"
dsouza@gwen.cad.mcc.com (Desmond Dsouza) (05/21/91)
cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: Desmond> You cannot compute any temporaries (e.g. common subexpressions) which Desmond> are needed for initialization of such data members. Desmond> class A { public: A(int m, int n); }; Desmond> class B { Desmond> public: Desmond> B (int j); Desmond> private: Desmond> A a1; // initialized with F1(j), F1(j)+1 for Desmond> //some horribly complex F1 Desmond> }; Desmond> B::B(int j) Desmond> // how can I share the computation of F1(j) here ??? Desmond> : a1 (F1(j), F1(j)+1) Desmond> { Desmond> // this part does not help: I cannot modify a1 at this point. Desmond> } WARNING: Novice code coming... Would a possible solution to this be: B::B(int j) : a1() { int x = F1(j), y = x + 1; A temp(x,y); a1 = temp; // why can't you modify a1 using copy assignment? } -- You make assumptions about default constructor (0 arguments) and the assignment operator which are not valid in general. Someone else suggested deriving a class from A and using that instead. That is a pretty expensive way of computing a temporary integer! -- Desmond. -- ------------------------------------------------------------------------------- Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza
jimad@microsoft.UUCP (Jim ADCOCK) (05/21/91)
In article <DSOUZA.91May3150307@gwen.cad.mcc.com> dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: > You cannot compute any temporaries (e.g. common subexpressions) > which are needed for initialization of such data members. Consider: ---- int F1(int j) { /* something really really big and horrible here */ return j;} class A { public: A(int m, int n); }; class B { public: B (int j); private: int f1; A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 }; B::B(int j) // how can I share the computation of F1(j) here ??? !!! Try the following: : f1(F1(j)), a1(f1, f1+1) { // this part does not help: I cannot modify a1 at this point. }
dsouza@gwen.cad.mcc.com (Desmond Dsouza) (05/23/91)
In article <72461@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > You cannot compute any temporaries (e.g. common subexpressions) > which are needed for initialization of such data members. Consider: ---- int F1(int j) { /* something really really big and horrible here */ return j;} class A { public: A(int m, int n); }; class B { public: B (int j); private: int f1; A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 }; B::B(int j) // how can I share the computation of F1(j) here ??? !!! Try the following: : f1(F1(j)), a1(f1, f1+1) { // this part does not help: I cannot modify a1 at this point. } No good. You cannot afford to add a data member per object instance to simply store an intermediate constructor variable. (There is also the subtle point about order of initialization of data members -- it follows the order in the class definition, so you had it right). -- Desmond. -- ------------------------------------------------------------------------------- Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza
euaeny@eua.ericsson.se (Erik Nyquist) (05/24/91)
dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: >In article <72461@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > > You cannot compute any temporaries (e.g. common subexpressions) > > which are needed for initialization of such data members. > Consider: > ---- > int F1(int j) { /* something really really big and horrible here */ return j;} > class A { > public: A(int m, int n); > }; > class B { > public: > B (int j); > private: > int f1; > A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 > }; > B::B(int j) > // how can I share the computation of F1(j) here ??? !!! Try the following: > : f1(F1(j)), a1(f1, f1+1) > { > // this part does not help: I cannot modify a1 at this point. > } >No good. You cannot afford to add a data member per object instance to >simply store an intermediate constructor variable. >(There is also the subtle point about order of initialization of data > members -- it follows the order in the class definition, so you had it right). Why no extend the language and make it possible to declare temporaries in the initialization list? ;-) (yes, I know! We should try to standardize the language, not change it.) class A { public: A(int m, int n); }; class B { public: B (int j); private: A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 }; B::B(int j) // how can I share the computation of F1(j) here ??? !!! Is this what you need? : int f1 = F1(j), a1(f1, f1+1) { // this part does not help: I cannot modify a1 at this point. } I don't say that we should change the language, but how can we solve this problem without language extensions? We could also ask ourselves if we really should try to solve this problem. That surely depends on how common it is! Erik Nyquist Ellemtel Utecklings AB We are no longer the knights that say Ni! Box 1505 We are the knights that say: S-125 25 Alvsjo, Sweden Iky,iky,iky,iky,patang,zoop-boing, zowie. -- Erik Nyquist Ellemtel Utecklings AB We are no longer the knights that say Ni! Box 1505 We are the knights that say: S-125 25 Alvsjo, Sweden Iky,iky,iky,iky,patang,zoop-boing, zowie.
marc@mit.edu (Marc Horowitz) (05/28/91)
In article <1991May23.170530.23037@eua.ericsson.se> euaeny@eua.ericsson.se (Erik Nyquist) writes: dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: >In article <72461@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > > You cannot compute any temporaries (e.g. common subexpressions) > > which are needed for initialization of such data members. I don't say that we should change the language, but how can we solve this problem without language extensions? We could also ask ourselves if we really should try to solve this problem. That surely depends on how common it is! I posted to comp.lang.c++ last night with almost exactly the same problem. A local friend mentioned that this issue had been recently discussed, so I'm responding to that original post. I I'll start by saying that this problem should definitely be looked at. It apparently happens at least somewhat often, since it happened to me last night. I had an even more difficult situation. My classes look like this: class A { public: A(int a, int b, int c, int d); // ... }; class B : public A { public: B(char *s) :A(/* ??? */) { /* too late */ } // ... }; void f(const char *s, int *a, int *b, int *c, int *d); f takes a string, and parses the integers out of it. It's somewhat expensive. Now, how should I go about doing this? Every solution I've been able to come up with is a morally abhorrent kludge involving some large number of static variables. I really would like to be able to have a block where I could define a few local temporaries, call f, and pass those temps into the base constructor. I could add another constructor to A, but A shouldn't need to depend on f, since f is really a part of B. Also note that subclassing won't help, since the arguments to A aren't functions of a common temporary, as f(x) and f(x)+1 are in the original example. I agree with Desmond: this seems like a definite flaw in the language. Someone mentioned that adding another block where temporaries could be created could result in programmers accidentally thinking the object was constructed while writing code in this block. I think this problem is solvable. If the block is in a scope similar to that of a static member function (e.g., ``this'' is not defined), then any use of a non-static member is an error. I think this is the way it should be, and is not overly restrictive. As long as someone can come up with a non-ambiguous syntax, it doesn't seem that code generation should be too difficult, either. Marc -- Marc Horowitz <marc@mit.edu> 617-253-7788
pena@brainware.fi (Olli-Matti Penttinen) (05/30/91)
In article <MARC.91May27140938@steve-dallas.mit.edu> marc@mit.edu (Marc Horowitz) writes: [ lotsa stuff zapped ] I'll start by saying that this problem should definitely be looked at. It apparently happens at least somewhat often, since it happened to me last night. I had an even more difficult situation. My classes look like this: class A { public: A(int a, int b, int c, int d); // ... }; class B : public A { public: B(char *s) :A(/* ??? */) { /* too late */ } // ... }; void f(const char *s, int *a, int *b, int *c, int *d); f takes a string, and parses the integers out of it. It's somewhat expensive. Now, how should I go about doing this? Every solution I've been able to come up with is a morally abhorrent kludge involving some large number of static variables. I really would like to be able to have a block where I could define a few local temporaries, call f, and pass those temps into the base constructor. I could add another constructor to A, but A shouldn't need to depend on f, since f is really a part of B. Also note that subclassing won't help, since the arguments to A aren't functions of a common temporary, as f(x) and f(x)+1 are in the original example. Do you ever use f for anything but to initialize or change the value of some A. If not, then f would really belong in A. In anycase, why not add A::A(const char *); that uses f to compute the 4 ints. Besides, Bjarne's proposal to use a protected init-method works well, too, iff A has a default constructor. Admittedly, things might get a little dirty if an A cannot have a meaningful state constructed out of nothing. In that case, one could (accidentally) use the value of an A before the object is fully constructed, so the problem would still be there. All in all, I don't see a reason to extend the language just because a new feature would occasionally save a few keystrokes. [ more text deleted ] Marc -- Marc Horowitz <marc@mit.edu> 617-253-7788 ==pena -- Olli-Matti Penttinen <pena@brainware.fi> | "When in doubt, use brute force." Brainware Oy | --Ken Thompson P.O.Box 330 +---------------------------------- 02151 ESPOO, Finland Tel. +358 0 4354 2565 Fax. +358 0 461 617
niklas@appli.se (Niklas Hallqvist) (06/02/91)
pena@brainware.fi (Olli-Matti Penttinen) writes: :In article <MARC.91May27140938@steve-dallas.mit.edu> marc@mit.edu (Marc Horowitz) writes: : [ lotsa stuff zapped ] : I'll start by saying that this problem should definitely be looked at. : It apparently happens at least somewhat often, since it happened to me : last night. I had an even more difficult situation. My classes look : like this: : class A { : public: : A(int a, int b, int c, int d); : // ... : }; : class B : public A { : public: : B(char *s) : :A(/* ??? */) : { /* too late */ } : // ... : }; : void f(const char *s, int *a, int *b, int *c, int *d); : f takes a string, and parses the integers out of it. It's somewhat : expensive. : Now, how should I go about doing this? Every solution I've been able : to come up with is a morally abhorrent kludge involving some large : number of static variables. I really would like to be able to have a : block where I could define a few local temporaries, call f, and pass : those temps into the base constructor. I could add another : constructor to A, but A shouldn't need to depend on f, since f is : really a part of B. Also note that subclassing won't help, since the : arguments to A aren't functions of a common temporary, as f(x) and : f(x)+1 are in the original example. :Do you ever use f for anything but to initialize or change the value :of some A. If not, then f would really belong in A. In anycase, why :not add A::A(const char *); that uses f to compute the 4 ints. :Besides, Bjarne's proposal to use a protected init-method works well, :too, iff A has a default constructor. Admittedly, things might get a :little dirty if an A cannot have a meaningful state constructed out of :nothing. In that case, one could (accidentally) use the value of an A :before the object is fully constructed, so the problem would still be :there. :All in all, I don't see a reason to extend the language just because :a new feature would occasionally save a few keystrokes. : [ more text deleted ] You forgot to read the requirements of the problem. The class A and the function f could not be altered (probably due to a binary only license of some library). The way to do this is of course by using MI, private inheritance and the well-known dependence of base class declaration order ;-) class A { public: A(int a, int b, int c, int d); // ... }; void f(const char* s, int* a, int* b, int* c, int* d); class B_helper { protected: int a, b, c, d; B_helper(const char* s) { f(s, &a, &b &c, &d); } }; class B : private B_helper, public A { public: B(const char* s) : B_helper(s), A(a, b, c, d) { /* usual code */ } // ... }; This code is untested by me, but I did send this suggestion to Marc a few days ago, and as he has not (yet) replied negatively, I suppose it works. If it does, this should be general enough to solve any problem of this kind, isn't it so? Marc! I got your reply, have you tested this code yet? Niklas -- Niklas Hallqvist Phone: +46-(0)31-40 75 00 Applitron Datasystem Fax: +46-(0)31-83 39 50 Molndalsvagen 95 Email: niklas@appli.se S-412 63 GOTEBORG, Sweden mcsun!sunic!chalmers!appli!niklas