www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Classes in D and C++

reply Andy Little <andy servocomm.freeserve.co.uk> writes:
Hi all,

I have been evaluating D over the last day in comparison to C++.

The template metaprogramming stuff is great and I think its a real improvement
over C++.

Sadly though theshowstopper for me is user defined types ( classes). I was
hoping that I could port my physical quantities library to D, but the killer is
the difference between classes in D and C++. 

In C++ it is possible to create classes that act pretty much like inbuilt
types. I used this to good effect in my quan library.

Unfortunately in D although you can do:

class X{
	this( int n_in){...}
}

Its not possible it seems to do e.g this:

X  x(3);

rather you have to do:

X x = new X(3);



Following is a cut down example of useage of my C++ Quan library, which I hope
shows why this requirement on class types is (though possible) highly
impractical for what I wanted to do. The physical quanties here are all UDT's
(e.g classes) rather than double typedefs FWIW and are compile time checked to
conform to dimensional analysis rules.



I sincerely hope that this post is seen in the right spirit. I am not
attempting to be a troll, but just trying to explain where I am at in relation
to D, and just kind of disappointed right now that D doesnt allow the
flexibility with classes that I have in C++. e.g create classes with
constructors on the stack, arbitrary temporaries etc, which may be syntactic
sugar to an extent but is IMO very useful and satisfying to code.


Of course I may have this all wrong ...?

regards
Andy Little

// C++ code. compute chord and angle of wind turbine blade sections
// using my quan library

chord_omega RotorDialog::getChordOmega(quan::length::m const& r)const
{ 
    using quan::length;
    using quan::velocity;
    using quan::mass_flow;
    using quan::force;
    using quan::pow;
    using quan::constant;
    using quan::angle;

    length::m const tip_r = this->m_outer_dia / 2.0; 
    if (r > tip_r){
        throw std::out_of_range("getChordOmega Input radius is out of range");
    }

    velocity::m_per_s const Vin_y  = this->m_vw;
    length::m const  dr = get_section_spacing();
    velocity::m_per_s const  Vout_y
    = Vin_y
    * ( (r < tip_r)
        ? 1.0 
          -  ((2.0 / 3.0) * (1.0 - this->m_ellipticality)
            + (this->m_ellipticality * sqrt(1.0 - pow<2>(r / tip_r) )))
        : 1.0 - (2.0/3.0)*(1.0 - this->m_ellipticality) );
    velocity::m_per_s const  Vb_y = (Vin_y + Vout_y) / 2.0;
    mass_flow::kg_per_s const  air_mass_flow
    = Vb_y * 2 * constant::pi * r * m_rho_air * dr;
    force::N const Fb_y = air_mass_flow * (Vin_y - Vout_y) ;
    double const  cl = this->m_cl;
    double const  drag_coeff  = this->m_cd / cl;

    velocity::m_per_s const epsilon = 0.0001 * abs(Vin_y);
    velocity::m_per_s const Vin_x = Vin_y * this->m_tsr * (r / tip_r);
    velocity::m_per_s Vout_x(0);
    velocity::m_per_s oldVout_x(0);
    for (int i = 0 ;i < 1000 ; ++i){
        velocity::m_per_s const Vb_x = Vout_x / 2.0 + Vin_x;
        angle::rad const beta = atan2(Vb_y,Vb_x);
        double const cos_beta = quan::cos(beta);
        double const sin_beta = quan::sin(beta); 
        force::N const Lift 
        = Fb_y /(cos_beta + drag_coeff * sin_beta);
        force::N const Fb_x 
        = Lift * (sin_beta - drag_coeff * cos_beta);
        oldVout_x = Vout_x;
        Vout_x = Fb_x / air_mass_flow;
        if (compare(Vout_x,oldVout_x,epsilon) == 0 ){
            length::m const chord 
            = Lift 
            / ( (quan::pow<2>(Vb_x) + quan::pow<2>(Vb_y) ) 
                * 0.5 * this->m_rho_air
                * cl * dr * this->m_numblades );
            return chord_omega(chord,beta );
        }
    }//fail
    return chord_omega(length::mm(0.0),angle::rad(0.0));
}
Mar 04 2007
next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Andy Little wrote:
 Hi all,
 
 I have been evaluating D over the last day in comparison to C++.
 
 The template metaprogramming stuff is great and I think its
 a real improvement over C++.
 
 Sadly though theshowstopper for me is user defined types
 ( classes). I was hoping that I could port my physical quantities
 library to D, but the killer is the difference between classes in
 D and C++.
 
 In C++ it is possible to create classes that act pretty much like
 inbuilt types. I used this to good effect in my quan library.
 
 Unfortunately in D although you can do:
 
 class X{
 	this( int n_in){...}
 }
 
 Its not possible it seems to do e.g this:
 
 X  x(3);
 
 rather you have to do:
 
 X x = new X(3);

Why don't you use "struct" instead? A struct can have functions, operator overloads, just no constructor/destructor/virtuals, but for simple types these shouldn't be needed. For custom types that behave as value types, you should be using a "struct" instead of a class. Instead of a constructor, create a "static opCall". opCall is the overload for "(..)" so you can instantiate your type similar to C++: struct SomeType { int member = 0;// default initializer here static SomeType opCall( int whatever ) { SomeType st; st.member = whatever;//custom initialize return st; } //... } SomeType st = 2;//construction No need for constructors ;) L.
Mar 04 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Lionello Lunesu wrote:
 Andy Little wrote:
 Hi all,

 I have been evaluating D over the last day in comparison to C++.

 The template metaprogramming stuff is great and I think its

 Sadly though theshowstopper for me is user defined types

> library to D, but the killer is the difference between classes in > D and C++.
 In C++ it is possible to create classes that act pretty much like

 Unfortunately in D although you can do:

 class X{
     this( int n_in){...}
 }

 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Why don't you use "struct" instead? A struct can have functions, operator overloads, just no constructor/destructor/virtuals, but for simple types these shouldn't be needed. For custom types that behave as value types, you should be using a "struct" instead of a class. Instead of a constructor, create a "static opCall". opCall is the overload for "(..)" so you can instantiate your type similar to C++: struct SomeType { int member = 0;// default initializer here static SomeType opCall( int whatever ) { SomeType st; st.member = whatever;//custom initialize return st; } //... } SomeType st = 2;//construction No need for constructors ;) L.

I don't think that quite works as you wrote. I think it needs to be SomeType st = SomeType(2); or auto st = SomeType(2); if you want to avoid repeating your repeat-avoiding self repeatedly. --bb
Mar 04 2007
next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Bill Baxter wrote:
 Lionello Lunesu wrote:
 Andy Little wrote:
 Hi all,

 I have been evaluating D over the last day in comparison to C++.

 The template metaprogramming stuff is great and I think its

 Sadly though theshowstopper for me is user defined types

> library to D, but the killer is the difference between classes in > D and C++.
 In C++ it is possible to create classes that act pretty much like

 Unfortunately in D although you can do:

 class X{
     this( int n_in){...}
 }

 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Why don't you use "struct" instead? A struct can have functions, operator overloads, just no constructor/destructor/virtuals, but for simple types these shouldn't be needed. For custom types that behave as value types, you should be using a "struct" instead of a class. Instead of a constructor, create a "static opCall". opCall is the overload for "(..)" so you can instantiate your type similar to C++: struct SomeType { int member = 0;// default initializer here static SomeType opCall( int whatever ) { SomeType st; st.member = whatever;//custom initialize return st; } //... } SomeType st = 2;//construction No need for constructors ;) L.

I don't think that quite works as you wrote. I think it needs to be SomeType st = SomeType(2); or auto st = SomeType(2); if you want to avoid repeating your repeat-avoiding self repeatedly. --bb

Actually, I was surprised myself, but it did work. And come to think of it, I remember Walter mentioning that he made it work, but I can't seem to find any reference to this in the changelog... L.
Mar 05 2007
parent Sean Kelly <sean f4.ca> writes:
Lionello Lunesu wrote:
 
 Actually, I was surprised myself, but it did work. And come to think of 
 it, I remember Walter mentioning that he made it work, but I can't seem 
 to find any reference to this in the changelog...

I must have missed the comment. Glad to hear it! Sean
Mar 05 2007
prev sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Bill Baxter wrote:
 Lionello Lunesu wrote:
 Instead of a constructor, create a "static opCall". opCall is the 
 overload for "(..)" so you can instantiate your type similar to C++:

 struct SomeType {
   int member = 0;// default initializer here
   static SomeType opCall( int whatever ) {
     SomeType st;
     st.member = whatever;//custom initialize
     return st;
   }
   //...
 }

 SomeType st = 2;//construction

 No need for constructors ;)

I don't think that quite works as you wrote. I think it needs to be SomeType st = SomeType(2); or auto st = SomeType(2); if you want to avoid repeating your repeat-avoiding self repeatedly.

Actually, with a slight modification that compiles just fine: --- struct SomeType { int member = 0;// default initializer here static SomeType opCall( int whatever ) { SomeType st; st.member = whatever;//custom initialize return st; } //... } void main() { SomeType st = 2;//construction } --- Static opCall isn't compile-time executable since it uses a struct :(, so the declaration must be in a function. (This is actually a pretty good argument to allow compile-time execution to work with structs, I think) This has been allowed for a while now, see http://www.digitalmars.com/d/changelog2.html#new0177 : --- # Casting a value v to a struct S is now rewritten as S(v). # Initializing a struct S from a value v is now rewritten as S(v). ---
Mar 05 2007
parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Frits van Bommel wrote:
 This has been allowed for a while now, see 
 http://www.digitalmars.com/d/changelog2.html#new0177 :
 ---
 # Casting a value v to a struct S is now rewritten as S(v).
 # Initializing a struct S from a value v is now rewritten as S(v).
 ---

Ah you found it! I didn't know it was pre-1.0 :) L.
Mar 05 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Lionello Lunesu wrote:
 Frits van Bommel wrote:
 This has been allowed for a while now, see 
 http://www.digitalmars.com/d/changelog2.html#new0177 :
 ---
 # Casting a value v to a struct S is now rewritten as S(v).
 # Initializing a struct S from a value v is now rewritten as S(v).
 ---

Ah you found it! I didn't know it was pre-1.0 :) L.

Ah, ok. I do remember those vague lines in the changelog now. --bb
Mar 05 2007
prev sibling next sibling parent reply Andy Little <andy servocomm.freeserve.co.uk> writes:
Lionello Lunesu Wrote:

 SomeType st = 2;//construction
 
 No need for constructors ;)

OK, To explain why I don't want to allow this syntax I need to explain som of how the quan library works. Its main purpose is to catch errors in dimensional analysis. So for example : assume some types representing Force, Mass and Acceleration. Now I say Mass m; Acceleration; Force f = m * a; // OK this is dimensionally correct according to Physics. However If I say: Force f = m / a; // Error. This is dimensionally not correct and the library is designed to not allow this to compile. In fact the dimension of all physical quantities can be represented as combinations of powers of the base dimensions e.g( in The SI system): length, mass, time, temperature, current, substance, intensity. Force for example is mass^1, length ^1, time ^-2 etc. (Just to jog the memory of Physics lessons) (And behind the scenes this is how the library works, by computing the resulting dimension in a calculation using metaprogramming) Also there are so called dimensionless quantities and these are represented in the library by inbuilt types. e.g int, double etc are seen by the library as dimensionless types, and certain calculations Do result in dimensionless types. E.g: Length L1, L2; double ratio = L1/ L2; // OK the result is dimensionless so is a numeric type. Therefore I don't allow the syntax: Length L= 1; As it is dimensionally incorrect to convert a numeric type to a physical quantity I do however allow the syntax Length L(1); It is specifically allowed to allow value initialisation of a physical_quantity from a numeric. (At some point you need to load the initial value and experience of quan library shows this works and is a compact way to express it) In C++ these 2 forms of initialisation can be differentiated by making the constructor explicit, which is basically how I deal with it in C++. I can get the explicit syntax in D I think by using a static OpCall, but I cant avoid the implicit construction :-( Finally The physical quantity class is actually more complicated and can be constructed with dimensionally equivalent quantities but with other units and so on. I am not sure how I would tackle that problem in D, but at the moment I am stuck on this one. regards Andy Little
Mar 05 2007
parent reply BCS <BCS pathlink.com> writes:
Andy Little wrote:
 Lionello Lunesu Wrote:
 
 
SomeType st = 2;//construction

No need for constructors ;)

OK, To explain why I don't want to allow this syntax I need to explain som of how the quan library works. Its main purpose is to catch errors in dimensional analysis.

Take a look at this lib. It does most of what I see your lib doing and might give you some ides on how to make yours work in D. http://www.csc.kth.se/~ol/physical.d
Mar 05 2007
parent Andy Little <andy servocomm.freeserve.co.uk> writes:
BCS Wrote:

 Andy Little wrote:
 Lionello Lunesu Wrote:
 
 
SomeType st = 2;//construction

No need for constructors ;)

OK, To explain why I don't want to allow this syntax I need to explain som of how the quan library works. Its main purpose is to catch errors in dimensional analysis.

Take a look at this lib. It does most of what I see your lib doing and might give you some ides on how to make yours work in D. http://www.csc.kth.se/~ol/physical.d

Hey, that looks interesting, certainly a lot leaner than my Quan library. The example made me laugh as it seems to be a D version of one of the examples in Quan. It will be interesting to study as I know the domain and so it will certainly help me to understand a bit more about D. Are there any plans to flesh it out? ( though it looks pretty functional already). Seems like it could be a nice example for D. regards Andy Little
Mar 05 2007
prev sibling parent reply Andy Little <andy servocomm.freeserve.co.uk> writes:
Lionello Lunesu Wrote:

 Instead of a constructor, create a "static opCall". opCall is the 
 overload for "(..)" so you can instantiate your type similar to C++:
 
 struct SomeType {
    int member = 0;// default initializer here
    static SomeType opCall( int whatever ) {
      SomeType st;
      st.member = whatever;//custom initialize
      return st;
    }
    //...
 }
 
 SomeType st = 2;//construction
 
 No need for constructors ;)

Aha. If I chnge struct to class I am getting something close to what I want: import std.stdio; class X{ static X opCall( double n ) { X xx; xx.x = n; return xx; } private: double x; } int main(char[][] args) { X x = X(3); //ok //############### X x = 3; // lError ine 21 //############# X y = x; // OK writefln("%s",x.x); return 0; } gives: class1.d(21): Error: cannot implicitly convert expression (3) of type int to cla ss1.X Error: cannot cast int to class1.X (Which is what I want :-) ) That sorts that case out. I'm back on the road for the moment :-) regards Andy Little
Mar 05 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Andy Little" <andy servocomm.freeserve.co.uk> wrote in message 
news:esgps5$2116$1 digitalmars.com...
 Aha. If I chnge struct to class I am getting something close to what I 
 want:
 import std.stdio;
 class X{

      static X opCall( double n )
      {
         X xx;

^^^ Don't forget to 'new' the instance of X here!
         xx.x = n;
         return xx;
      }

 private:
   double x;
 }

 

Mar 05 2007
prev sibling parent reply Uno <unodgs tlen.pl> writes:
 Its not possible it seems to do e.g this:
 
 X  x(3);
 
 rather you have to do:
 
 X x = new X(3);

Yep, I don't like that syntax too. Everywhere news.. And although D has many great features such small things prevent me to switch to D.
Mar 05 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Yep, I don't like that syntax too. Everywhere news.. And although D has many great features such small things prevent me to switch to D.

You can do: auto x = X(3); and x will be put on the stack.
Mar 05 2007
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Walter Bright wrote:
 Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Yep, I don't like that syntax too. Everywhere news.. And although D has many great features such small things prevent me to switch to D.

You can do: auto x = X(3); and x will be put on the stack.

Surely you mean scope x = X(3); Or did scope get rolled back into the auto keyword again while I wasn't looking? >_< -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 05 2007
next sibling parent reply Max Samukha <samukha voliacable.com> writes:
On Mon, 05 Mar 2007 20:15:04 +1100, Daniel Keep
<daniel.keep.lists gmail.com> wrote:

Walter Bright wrote:
 Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Yep, I don't like that syntax too. Everywhere news.. And although D has many great features such small things prevent me to switch to D.

You can do: auto x = X(3); and x will be put on the stack.

Surely you mean scope x = X(3); Or did scope get rolled back into the auto keyword again while I wasn't looking? >_<

I think he means structs: struct X{ static X opCall( int n_in){ X x; return x; } } void main() { auto x = X(1); //allocates x of type X on stack and assigns the result of X.opCall(1) to it. X x1 = 1; // does the same thing to x1 x1 = cast(X)2; //calls X.opCall(2) and assigns the result to x1; } Scope classes are allocated on stack but still require 'new'. class X { this(int n_in) { } ~this() { } } void test() { ` scope auto x = new X(1); // x allocated on stack } //~this called on x on scope exit void main() { test(); }
Mar 05 2007
parent Max Samukha <samukha voliacable.com> writes:
On Mon, 05 Mar 2007 12:10:53 +0200, Max Samukha
<samukha voliacable.com> wrote:

On Mon, 05 Mar 2007 20:15:04 +1100, Daniel Keep
<daniel.keep.lists gmail.com> wrote:

Walter Bright wrote:
 Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Yep, I don't like that syntax too. Everywhere news.. And although D has many great features such small things prevent me to switch to D.

You can do: auto x = X(3); and x will be put on the stack.

Surely you mean scope x = X(3); Or did scope get rolled back into the auto keyword again while I wasn't looking? >_<

I think he means structs: struct X{ static X opCall( int n_in){ X x; return x; } } void main() { auto x = X(1); //allocates x of type X on stack and assigns the result of X.opCall(1) to it. X x1 = 1; // does the same thing to x1 x1 = cast(X)2; //calls X.opCall(2) and assigns the result to x1; } Scope classes are allocated on stack but still require 'new'. class X { this(int n_in) { } ~this() { } } void test() { ` scope auto x = new X(1); // x allocated on stack } //~this called on x on scope exit void main() { test(); }

Correction: surely not classes are allocated but objects.
Mar 05 2007
prev sibling parent reply Kyle Furlong <kylefurlong gmail.com> writes:
 
 Walter Bright wrote:
 Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

has many great features such small things prevent me to switch to D.

auto x = X(3); and x will be put on the stack.

Surely you mean scope x = X(3); Or did scope get rolled back into the auto keyword again while I wasn't looking? >_<

http://www.digitalmars.com/d/memory.html#stackclass
Mar 05 2007
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Kyle Furlong wrote:
 Walter Bright wrote:
 Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

has many great features such small things prevent me to switch to D.

auto x = X(3); and x will be put on the stack.

Surely you mean scope x = X(3); Or did scope get rolled back into the auto keyword again while I wasn't looking? >_<

http://www.digitalmars.com/d/memory.html#stackclass

Yes, yes; I'm a dimwit. Just ignore me :P With all this talk about structs and classes and static opCall, I forgot which one we were talking about :3 -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 05 2007
prev sibling next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Walter Bright wrote:
 Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Yep, I don't like that syntax too. Everywhere news.. And although D has many great features such small things prevent me to switch to D.

You can do: auto x = X(3); and x will be put on the stack.

Uh, I think this one should go on the eater-eggs list!? I've read the docs on "Allocating Class Instances On The Stack" but I had no idea you could instantiate classes without 'new'! I love it! L.
Mar 05 2007
parent Lionello Lunesu <lio lunesu.remove.com> writes:
Lionello Lunesu wrote:
 Walter Bright wrote:
 Uno wrote:
 Its not possible it seems to do e.g this:

 X  x(3);

 rather you have to do:

 X x = new X(3);

Yep, I don't like that syntax too. Everywhere news.. And although D has many great features such small things prevent me to switch to D.

You can do: auto x = X(3); and x will be put on the stack.

Uh, I think this one should go on the eater-eggs list!? I've read the docs on "Allocating Class Instances On The Stack" but I had no idea you could instantiate classes without 'new'! I love it!

Uhm I think you'll still need a struct + static opCall for this to work. L.
Mar 05 2007
prev sibling parent reply Uno <unodgs tlen.pl> writes:
 You can do:
 
 	auto x = X(3);
 
 and x will be put on the stack.

I didn't know that shortcut.. thanks! Now it looks much better and it's acceptable for me, but c++ form is still shorter and more elegant (of course it's disputable).
Mar 05 2007
parent Sean Kelly <sean f4.ca> writes:
Uno wrote:
 You can do:

 	auto x = X(3);

 and x will be put on the stack.

I didn't know that shortcut.. thanks! Now it looks much better and it's acceptable for me, but c++ form is still shorter and more elegant (of course it's disputable).

The problem with the C++ form is that it's semantically identical to a function prototype statement. I do think it would be great if there were a common syntax to construct all types in D, but I'm not sure if "X x(a)" is it. "X x = X(a)" is probably better. But I'd like this to work for concrete types as well. Sean
Mar 05 2007