www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Use class template as a type

reply dm <none email.com> writes:
Hi.
Is it possible to write in D something like this?

```
abstract class MyClass(T)
{
   public:
     property const(T) value(){return _value;}
     property void value(T val){_value = val;}
...
   private:
    T _value;
...
}
...
class MyClassFloat: MyClass!float
...

class MyClassInt: MyClass!int
...

void main()
{
   MyClass[] someArray;
   someArray ~= new MyClassFloat();
...

   someArray ~= new MyClassInt();
...

   foreach(myClass; someArray)
    if(typeid(myClass) == typeid(MyClassInt))
     myClass.value = 999;
    else
     myClass.value = 123.45f;
...

}
```
When I trying to compile code like above I got
Error: class MyClass(T) is used as a type.
Nov 28 2016
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
In your case I'd just swap out ``MyClass[] someArray;`` to ``Object[] 
someArray;``.
But only because there are no members added without the extra typing in 
MyClass.

Remember types in meta-programming in D are not erased, they exist in 
the assembly and are unique. Unlike Java who did the implementation 
rather wrong.
Nov 28 2016
parent reply dm <none email.com> writes:
On Monday, 28 November 2016 at 11:30:23 UTC, rikki cattermole 
wrote:
 In your case I'd just swap out ``MyClass[] someArray;`` to 
 ``Object[] someArray;``.
 But only because there are no members added without the extra 
 typing in MyClass.

 Remember types in meta-programming in D are not erased, they 
 exist in the assembly and are unique. Unlike Java who did the 
 implementation rather wrong.
I'm tried to use Object[], but got error Error: no property 'value' for type 'object.Object' I guess I must cast() to MyClassInt or MyClassFloat, but how can I do it?
Nov 28 2016
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 29/11/2016 2:56 AM, dm wrote:
 On Monday, 28 November 2016 at 11:30:23 UTC, rikki cattermole wrote:
 In your case I'd just swap out ``MyClass[] someArray;`` to ``Object[]
 someArray;``.
 But only because there are no members added without the extra typing
 in MyClass.

 Remember types in meta-programming in D are not erased, they exist in
 the assembly and are unique. Unlike Java who did the implementation
 rather wrong.
I'm tried to use Object[], but got error Error: no property 'value' for type 'object.Object' I guess I must cast() to MyClassInt or MyClassFloat, but how can I do it?
We have a handy dandy syntax for this: if (MyClassInt subclass = cast(MyClassInt)value) { writeln(subclass.value); } If it doesn't cast to said type (it will be null) that branch won't execute.
Nov 28 2016
next sibling parent dm <none email.com> writes:
 We have a handy dandy syntax for this:

 if (MyClassInt subclass = cast(MyClassInt)value) {
 	writeln(subclass.value);
 }

 If it doesn't cast to said type (it will be null) that branch 
 won't execute.
Hell yeah! It's works! Thank you!
Nov 28 2016
prev sibling parent reply Namespace <rswhite4 gmail.com> writes:
 We have a handy dandy syntax for this:

 if (MyClassInt subclass = cast(MyClassInt)value) {
 	writeln(subclass.value);
 }

 If it doesn't cast to said type (it will be null) that branch 
 won't execute.
Just out of interest: it looks like a dynamic_cast in C++ which is considered as slow operation. Is that D cast also a dynamic cast and also slow? I've never used it, so I'm a bit curious.
Nov 28 2016
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 29/11/2016 3:35 AM, Namespace wrote:
 We have a handy dandy syntax for this:

 if (MyClassInt subclass = cast(MyClassInt)value) {
     writeln(subclass.value);
 }

 If it doesn't cast to said type (it will be null) that branch won't
 execute.
Just out of interest: it looks like a dynamic_cast in C++ which is considered as slow operation. Is that D cast also a dynamic cast and also slow? I've never used it, so I'm a bit curious.
I wouldn't worry about it. You're already using classes and they are dog slow in general.
Nov 28 2016
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 28 November 2016 at 14:35:36 UTC, Namespace wrote:
 We have a handy dandy syntax for this:

 if (MyClassInt subclass = cast(MyClassInt)value) {
 	writeln(subclass.value);
 }

 If it doesn't cast to said type (it will be null) that branch 
 won't execute.
Just out of interest: it looks like a dynamic_cast in C++ which is considered as slow operation. Is that D cast also a dynamic cast and also slow? I've never used it, so I'm a bit curious.
The cast from a class type to a sub class in itself does absolutely nothing. It has only an effect when you call a virtual method. This is slow because of the indirection that happens when the right offset has to be found in the VTBL.
Nov 28 2016
parent reply ag0aep6g <anonymous example.com> writes:
On 11/29/2016 02:21 AM, Basile B. wrote:
 The cast from a class type to a sub class in itself does absolutely
 nothing.
That can't be right. A bad downcast gives you null, so it has to check the dynamic type information. Compare with upcasts which are statically known to be correct, so they don't need to check anything at runtime.
Nov 29 2016
parent reply Bauss <jj_1337 live.dk> writes:
On Tuesday, 29 November 2016 at 09:58:16 UTC, ag0aep6g wrote:
 On 11/29/2016 02:21 AM, Basile B. wrote:
 The cast from a class type to a sub class in itself does 
 absolutely
 nothing.
That can't be right. A bad downcast gives you null, so it has to check the dynamic type information. Compare with upcasts which are statically known to be correct, so they don't need to check anything at runtime.
Usually casts to base classes can be determined if they're valid at compile-time. Take this for an example: class Foo { } class Bar : Foo { } void main() { auto bar = new Bar; auto foo = cast(Foo)bar; // The compiler should know that bar is of type Bar, which is a subclass of Foo and thus the cast theoretically is redundant. } Even in a situation like this, the compiler should be able to see if the cast could ever be invalid during compile-time determined by calls to fun. void fun(Cast)(Bar bar) { return cast(Cast)bar; // If Cast is Foo then the compiler should know the cast is redundant. ] I don't know if the D compiler actually takes such situation into account, but I'd assume it does some kind of optimization in regards of that.
Nov 30 2016
parent reply ag0aep6g <anonymous example.com> writes:
On 11/30/2016 10:42 AM, Bauss wrote:
 Usually casts to base classes can be determined if they're valid at
 compile-time.
Yeah, that's what I said. A cast to a base class is an "upcast". Upcasts don't need run-time checks. The other direction (cast to more derived class) is a downcast. Downcasts need run-time checks.
Nov 30 2016
parent Basile B. <b2.temp gmx.com> writes:
On Wednesday, 30 November 2016 at 14:53:21 UTC, ag0aep6g wrote:
 On 11/30/2016 10:42 AM, Bauss wrote:
 Usually casts to base classes can be determined if they're 
 valid at
 compile-time.
Yeah, that's what I said. A cast to a base class is an "upcast". Upcasts don't need run-time checks. The other direction (cast to more derived class) is a downcast. Downcasts need run-time checks.
Actually I've always called an "upcast" a "downcast" ! This incredible misconception explains why you had to correct me after my yesterday's answer.
Nov 30 2016
prev sibling next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Monday, 28 November 2016 at 11:26:41 UTC, dm wrote:
 Hi.
 Is it possible to write in D something like this?

 ```
 abstract class MyClass(T)
 {
   public:
     property const(T) value(){return _value;}
     property void value(T val){_value = val;}
 ...
   private:
    T _value;
 ...
 }
 ...
 class MyClassFloat: MyClass!float
 ...

 class MyClassInt: MyClass!int
 ...

 void main()
 {
   MyClass[] someArray;
---^
   someArray ~= new MyClassFloat();
 ...

   someArray ~= new MyClassInt();
 ...

   foreach(myClass; someArray)
    if(typeid(myClass) == typeid(MyClassInt))
     myClass.value = 999;
    else
     myClass.value = 123.45f;
 ...

 }
 ```
 When I trying to compile code like above I got
 Error: class MyClass(T) is used as a type.
Thats because MyClass is a template class. Templates are note types, instansiations of templates can be types. e.g. Myclass!float[] arr; // note this is not MyClass!(float[]); will work. As Rikki suggested using Object[] instead will allow use to store classes of different types.
Nov 28 2016
parent dm <none email.com> writes:
 Thats because MyClass is a template class. Templates are note 
 types, instansiations of  templates can be types.
 e.g.

     Myclass!float[] arr; // note this is not MyClass!(float[]);

 will work. As Rikki suggested using Object[] instead will allow 
 use to store classes of different types.
Maybe I must use some stub class or interface and override all methods... But I so like D templates, as a result it's a small and easy to understand code. Or actually it's maybe a XY problem. I'm trying to implement OpenGL material manager and for OpenGL uniforms I tried to write: ``` abstract class Uniform(T) property ... property ... T _val;... void method()... ... class FloatUniform: Uniform!float ... override void method()... And in material class class Material ... Texture[] textures; Uniform[] uniforms; ... ``` Maybe i'm totally wrong and better just use glUniformXXX... in my main app instead of ``` auto uniform = new SomeTypeUniform... ... uniform.value = someValue; ``` ?
Nov 28 2016
prev sibling parent reply Jerry <Kickupx gmail.com> writes:
On Monday, 28 November 2016 at 11:26:41 UTC, dm wrote:
 ```
 abstract class MyClass(T)
 {
   public:
     property const(T) value(){return _value;}
     property void value(T val){_value = val;}
 ...
   private:
    T _value;
 ...
 }
To avoid having to use the Object class directly you can make an base class of the class template. Like: ``` abstract class MyClass {} abstract class MyClassImpl(T) { public: property const(T) value(){return _value;} property void value(T val){_value = val;} ... private: T _value; ... } MyClassInt and float inherits from MyClassImpl ``` And use it like: ``` void main() { MyClass[] objs; objs ~= new MyClassFloat(); objs ~= new MyClassInt(); } ```
Nov 29 2016
next sibling parent Jerry <Kickupx gmail.com> writes:
On Tuesday, 29 November 2016 at 15:56:23 UTC, Jerry wrote:
 abstract class MyClass {}
 abstract class MyClassImpl(T)
Oops, forgot MyClassImpl should extend from MyClass. abstract class MyClassImpl(T) : MyClass { ... }
Nov 29 2016
prev sibling next sibling parent Bauss <jj_1337 live.dk> writes:
On Tuesday, 29 November 2016 at 15:56:23 UTC, Jerry wrote:
 On Monday, 28 November 2016 at 11:26:41 UTC, dm wrote:
 ```
 abstract class MyClass(T)
 {
   public:
     property const(T) value(){return _value;}
     property void value(T val){_value = val;}
 ...
   private:
    T _value;
 ...
 }
To avoid having to use the Object class directly you can make an base class of the class template. Like: ``` abstract class MyClass {} abstract class MyClassImpl(T) { public: property const(T) value(){return _value;} property void value(T val){_value = val;} ... private: T _value; ... } MyClassInt and float inherits from MyClassImpl ``` And use it like: ``` void main() { MyClass[] objs; objs ~= new MyClassFloat(); objs ~= new MyClassInt(); } ```
I would rather go with an interface than a base class.
Nov 30 2016
prev sibling parent dm <none email.com> writes:
On Tuesday, 29 November 2016 at 15:56:23 UTC, Jerry wrote:
 To avoid having to use the Object class directly you can make 
 an base class of the class template.
 Like:

 ```
 abstract class MyClass {}
 abstract class MyClassImpl(T)
 {
 public:
      property const(T) value(){return _value;}
      property void value(T val){_value = val;}
  ...
    private:
     T _value;
  ...
 }

 MyClassInt and float inherits from MyClassImpl
 ```

 And use it like:

 ```
 void main() {
    MyClass[] objs;
    objs ~= new MyClassFloat();
    objs ~= new MyClassInt();
 }
 ```
Yes, but anyway you need to downcast if(MyClassBlahBlah subclass = cast(MyClassBlahBlah)obj)... So it's not much sense to have base class or interface MyClass.
Nov 30 2016