Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #5194 > unrolled thread

advice -- temp var for constructors?

Started byMiles Bader <miles@gnu.org>
First post2011-05-20 13:35 +0900
Last post2011-05-20 14:28 +0000
Articles 9 — 4 participants

Back to article view | Back to comp.lang.c++


Contents

  advice -- temp var for constructors? Miles Bader <miles@gnu.org> - 2011-05-20 13:35 +0900
    Re: advice -- temp var for constructors? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-05-20 07:16 +0200
    Re: advice -- temp var for constructors? "Balog Pal" <pasa@lib.hu> - 2011-05-20 10:38 +0200
      Re: advice -- temp var for constructors? Miles Bader <miles@gnu.org> - 2011-05-20 17:50 +0900
        Re: advice -- temp var for constructors? "Balog Pal" <pasa@lib.hu> - 2011-05-20 11:12 +0200
    Re: advice -- temp var for constructors? Marc <marc.glisse@gmail.com> - 2011-05-20 10:23 +0000
      Re: advice -- temp var for constructors? "Balog Pal" <pasa@lib.hu> - 2011-05-20 13:02 +0200
        Re: advice -- temp var for constructors? Marc <marc.glisse@gmail.com> - 2011-05-20 13:55 +0000
          Re: advice -- temp var for constructors? Marc <marc.glisse@gmail.com> - 2011-05-20 14:28 +0000

#5194 — advice -- temp var for constructors?

FromMiles Bader <miles@gnu.org>
Date2011-05-20 13:35 +0900
Subjectadvice -- temp var for constructors?
Message-ID<buo1uzuxa3i.fsf@dhlpc061.dev.necel.com>
Inside a class constructor, I often wish I could use temporary
intermediate while calculating the initialization values -- both for
clarity and efficiency -- but there doesn't seem to be any reallly good
way to do this.

E.g., consider a class A with fields x, y, z.  I want to initialize x,
y, and z from a constructor argument "arg".  If I was writing normal
code, I could do something like:

   int temp1 = some_long_calculation (arg);
   int temp2 = some_other_long_calculation (arg);
   x = blah_blah (temp1, temp2);
   y = oink_oink (temp1, temp2);
   z = zergh_zergh (temp1, temp2);

but inside constructor initialization forms, one can't create "temp1"
and "temp2".

Soooo, possible workarounds:

(1) Don't use initialization forms at all (at least for data members
    that use a temp variable), but instead just write code like the
    above in the constructor body.

    [This often works, but I think would generally be considered kind of
    ugly, and in some cases is less efficient.]

(2) Add private class data members for each temporary variable, and only
    actually use them during the initialization.

    [This is also ugly, wastes space in each object, and in some cases
    using data members as temporaries seems to result in less efficient
    code than real temporary variables (maybe because alias analysis is
    more difficult).]

(3) Just bite the bullet, and don't use temporary variables at all --
    instead duplicate the expressions and hope the compiler can detect
    the duplication and eliminate the redundancy itself.

    [Less readable, often less efficient, less maintainable because of
    the duplication.]

None of these solutions seems very satisfying.  Does anybody have advice
about other, better, methods...?

Thanks,

-Miles

-- 
Arrest, v. Formally to detain one accused of unusualness.

[toc] | [next] | [standalone]


#5195

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-05-20 07:16 +0200
Message-ID<ir4t4h$aah$1@dont-email.me>
In reply to#5194
* Miles Bader, on 20.05.2011 06:35:
> Inside a class constructor, I often wish I could use temporary
> intermediate while calculating the initialization values -- both for
> clarity and efficiency -- but there doesn't seem to be any reallly good
> way to do this.
>
> E.g., consider a class A with fields x, y, z.  I want to initialize x,
> y, and z from a constructor argument "arg".  If I was writing normal
> code, I could do something like:
>
>     int temp1 = some_long_calculation (arg);
>     int temp2 = some_other_long_calculation (arg);
>     x = blah_blah (temp1, temp2);
>     y = oink_oink (temp1, temp2);
>     z = zergh_zergh (temp1, temp2);
>
> but inside constructor initialization forms, one can't create "temp1"
> and "temp2".
>
> Soooo, possible workarounds:
>
> (1) Don't use initialization forms at all (at least for data members
>      that use a temp variable), but instead just write code like the
>      above in the constructor body.
>
>      [This often works, but I think would generally be considered kind of
>      ugly, and in some cases is less efficient.]
>
> (2) Add private class data members for each temporary variable, and only
>      actually use them during the initialization.
>
>      [This is also ugly, wastes space in each object, and in some cases
>      using data members as temporaries seems to result in less efficient
>      code than real temporary variables (maybe because alias analysis is
>      more difficult).]
>
> (3) Just bite the bullet, and don't use temporary variables at all --
>      instead duplicate the expressions and hope the compiler can detect
>      the duplication and eliminate the redundancy itself.
>
>      [Less readable, often less efficient, less maintainable because of
>      the duplication.]
>
> None of these solutions seems very satisfying.  Does anybody have advice
> about other, better, methods...?

E.g. like (off the cuff, not touched by compilers' dirty hands)


<code>
class Foo
{
private:
     struct State
     {
         int x, y, z;

         State( int t1, int t2 )
             : x( blah_blah( t1, t2 ) )
             , y( oink_oink( t1, t2 ) )
             , z( zergh_zergh( t1, t2 ) )
         {}
     } state;

public:
     // Whatever, then

     Foo( int arg )
         : state( some_calc_1( arg ), some_long_calc_2( arg ) )
     {}
};
</code>


Cheers & hth.,

- Alf "let me see that for you - lmstfy.com"

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#5196

From"Balog Pal" <pasa@lib.hu>
Date2011-05-20 10:38 +0200
Message-ID<ir59b3$2lh6$1@news.ett.com.ua>
In reply to#5194
"Miles Bader" <miles@gnu.org>
> Inside a class constructor, I often wish I could use temporary
> intermediate while calculating the initialization values -- both for
> clarity and efficiency -- but there doesn't seem to be any reallly good
> way to do this.

Hm? Call a function that returns what you want, calculated any complex way 
you like... You can make it a private static in the class. You can even make 
it non-static, but then be extremely careful to use only members that are 
already constucted, and spam comments in the class to avoid reordering.

[toc] | [prev] | [next] | [standalone]


#5197

FromMiles Bader <miles@gnu.org>
Date2011-05-20 17:50 +0900
Message-ID<buo1uztwyab.fsf@dhlpc061.dev.necel.com>
In reply to#5196
"Balog Pal" <pasa@lib.hu> writes:
>> Inside a class constructor, I often wish I could use temporary
>> intermediate while calculating the initialization values -- both for
>> clarity and efficiency -- but there doesn't seem to be any reallly good
>> way to do this.
>
> Hm? Call a function that returns what you want, calculated any complex
> way you like... You can make it a private static in the class. You can
> even make it non-static, but then be extremely careful to use only
> members that are already constucted, and spam comments in the class to
> avoid reordering.

The problem with that is that multiple data members are initialized from
the _same_ set of temporaries (maybe I should have stated this more
explicitly) -- if it's only a single data member, I do indeed just use a
function, and that of course works fine.

-Miles

-- 
Future, n. That period of time in which our affairs prosper, our friends
are true and our happiness is assured.

[toc] | [prev] | [next] | [standalone]


#5198

From"Balog Pal" <pasa@lib.hu>
Date2011-05-20 11:12 +0200
Message-ID<ir5bag$2men$1@news.ett.com.ua>
In reply to#5197
"Miles Bader" <miles@gnu.org> az alábbiakat írta a következő hírüzenetben: 
buo1uztwyab.fsf@dhlpc061.dev.necel.com...
> "Balog Pal" <pasa@lib.hu> writes:
>>> Inside a class constructor, I often wish I could use temporary
>>> intermediate while calculating the initialization values -- both for
>>> clarity and efficiency -- but there doesn't seem to be any reallly good
>>> way to do this.
>>
>> Hm? Call a function that returns what you want, calculated any complex
>> way you like... You can make it a private static in the class. You can
>> even make it non-static, but then be extremely careful to use only
>> members that are already constucted, and spam comments in the class to
>> avoid reordering.
>
> The problem with that is that multiple data members are initialized from
> the _same_ set of temporaries (maybe I should have stated this more
> explicitly) -- if it's only a single data member, I do indeed just use a
> function, and that of course works fine.

In such situations I use the body of ctor, and just () in the list. It shall 
not be a problem, we have the ctor body for some reason do we? ;-)

Another best thing is to put all those related members in a separate 
struct/class that will be a meber or base -- if their init is tied likely 
they are bound in some way. Then you can use a single ctor call.

The rest is too hacky, certainly it is possible to have a smart pointer that 
holds the temp and gets reset when done -- but it is still a footprint both 
in processing and in class size. 

[toc] | [prev] | [next] | [standalone]


#5203

FromMarc <marc.glisse@gmail.com>
Date2011-05-20 10:23 +0000
Message-ID<ir5ffu$la8$1@news-rocq.inria.fr>
In reply to#5194
Miles Bader  wrote:

> Inside a class constructor, I often wish I could use temporary
> intermediate while calculating the initialization values -- both for
> clarity and efficiency -- but there doesn't seem to be any reallly good
> way to do this.
> 
> E.g., consider a class A with fields x, y, z.  I want to initialize x,
> y, and z from a constructor argument "arg".  If I was writing normal
> code, I could do something like:
> 
>    int temp1 = some_long_calculation (arg);
>    int temp2 = some_other_long_calculation (arg);
>    x = blah_blah (temp1, temp2);
>    y = oink_oink (temp1, temp2);
>    z = zergh_zergh (temp1, temp2);
> 
> but inside constructor initialization forms, one can't create "temp1"
> and "temp2".

A(T1 x,T2 y,T3 z,T4 temp1=f(x,y,z),T5 temp2=g(x,y,z)) :
  x(h(temp1,temp2)),y(i(temp1,temp2)),z(j(temp1,temp2)) {}

Delegating constructors allow different solutions.

[toc] | [prev] | [next] | [standalone]


#5205

From"Balog Pal" <pasa@lib.hu>
Date2011-05-20 13:02 +0200
Message-ID<ir5hp4$2ovl$1@news.ett.com.ua>
In reply to#5203
"Marc" <marc.glisse@gmail.com>
>> Inside a class constructor, I often wish I could use temporary
>> intermediate while calculating the initialization values -- both for
>> clarity and efficiency -- but there doesn't seem to be any reallly good
>> way to do this.
>>
>> E.g., consider a class A with fields x, y, z.  I want to initialize x,
>> y, and z from a constructor argument "arg".  If I was writing normal
>> code, I could do something like:
>>
>>
>> but inside constructor initialization forms, one can't create "temp1"
>> and "temp2".
>
> A(T1 x,T2 y,T3 z,T4 temp1=f(x,y,z),T5 temp2=g(x,y,z)) :
>  x(h(temp1,temp2)),y(i(temp1,temp2)),z(j(temp1,temp2)) {}
>
int foo(int);
struct A
{
  A(int par, int tmp = foo(par)) {}
};
void m() { A a(1);}
- ---
"ComeauTest.c", line 4: error: a parameter is not allowed
    A(int par, int tmp = foo(par)) {}
                             ^

1 error detected in the compilation of "ComeauTest.c".
- ----

Even if worked, you could be bitten if someone passes in a value.

But the idea is good, and can be pushed to wrking stuff: make the temp param 
a smart pointer, init it early then use. Like (just pseudocode)

struct T3
{
 T1 m1;
 T2 m2;
 T3( T1 p1, T2 p2) {...}
};

A(T1 p1, T2 p2, auto_ptr<T3> t = 0) :
  m1( t.reset(new T3(p1, p2), t->m1), m2(t->m2) {}

I still prefer just using the ctor body of A.





[toc] | [prev] | [next] | [standalone]


#5209

FromMarc <marc.glisse@gmail.com>
Date2011-05-20 13:55 +0000
Message-ID<ir5rso$ria$1@news-rocq.inria.fr>
In reply to#5205
"Balog Pal"  wrote:

>> A(T1 x,T2 y,T3 z,T4 temp1=f(x,y,z),T5 temp2=g(x,y,z)) :
>>  x(h(temp1,temp2)),y(i(temp1,temp2)),z(j(temp1,temp2)) {}
>>
> int foo(int);
> struct A
> {
>   A(int par, int tmp = foo(par)) {}
> };
> void m() { A a(1);}
> - ---
> "ComeauTest.c", line 4: error: a parameter is not allowed
>     A(int par, int tmp = foo(par)) {}
>                              ^

Indeed, I hadn't tried it, and I am a bit surprised that this is
forbidden. Well, too bad. But I wonder why they thought it a problem
to allow using a mandatory argument in the default value of optional
arguments.

> Even if worked, you could be bitten if someone passes in a value.

Bah, just introduce a dummy argument in between:
struct A {
  struct Never_used_directly{};
  A(int par,Never_used_directly=Never_used_directly(),int tmp=...){}
};

(you can make the dummy even harder to accidentally produce, but
that's the idea)

> But the idea is good, and can be pushed to wrking stuff: make the temp param 
> a smart pointer, init it early then use. Like (just pseudocode)
> 
> struct T3
> {
>  T1 m1;
>  T2 m2;
>  T3( T1 p1, T2 p2) {...}
> };
> 
> A(T1 p1, T2 p2, auto_ptr<T3> t = 0) :
>   m1( t.reset(new T3(p1, p2), t->m1), m2(t->m2) {}

Clever, but I'd rather wait for delegating constructors instead.

[toc] | [prev] | [next] | [standalone]


#5210

FromMarc <marc.glisse@gmail.com>
Date2011-05-20 14:28 +0000
Message-ID<ir5tpr$st6$1@news-rocq.inria.fr>
In reply to#5209
Marc  wrote:

> "Balog Pal"  wrote:
> 
>>> A(T1 x,T2 y,T3 z,T4 temp1=f(x,y,z),T5 temp2=g(x,y,z)) :
>>>  x(h(temp1,temp2)),y(i(temp1,temp2)),z(j(temp1,temp2)) {}
>>>
>> int foo(int);
>> struct A
>> {
>>   A(int par, int tmp = foo(par)) {}
>> };
>> void m() { A a(1);}
>> - ---
>> "ComeauTest.c", line 4: error: a parameter is not allowed
>>     A(int par, int tmp = foo(par)) {}
>>                              ^
> 
> Indeed, I hadn't tried it, and I am a bit surprised that this is
> forbidden. Well, too bad. But I wonder why they thought it a problem
> to allow using a mandatory argument in the default value of optional
> arguments.

Apparently it is my mental representation of default arguments that
didn't quite fit. I always pictured it as forwarding:
int f(int,int=g());
was to me something like:
int f(int,int);
int f(int x){return f(x,g());}

whereas the standard presents it without any intermediate layer:
f(1); is "rewritten" to f(1,g()); but with resolution of g performed
as if in the forwarding model...
(and thus the unspecified order of evaluation of function arguments
prevents from using one parameter in another one)

It is not super convincing, but at least I see where it comes from
now...

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web