dynamic_cast

Back to topics list To post a new topic, please log in or register
avatar
12983
WHRoeder 2014.04.26 16:04
 
At the bottom of Typecasting - MQL4 Documentation it says
You can use the explicit casting to convert the base class pointers to the pointers of a derived class. But you must be fully confident in the admissibility of such a transformation, because otherwise a critical runtime error will occur and the mql4 program will be stopped.
Given these classes
class base{ ... };
class derv : public base{ ...};
class other : public base{ ... };
This code crashes
base* obj = new other;
derv* d   = (derv*)obj; // Crash
Because obj is an other but other is not a derived from derv
C++ code can insure this by using a dynamic_cast
<type> *p_subclass = dynamic_cast<<type> *>( p_obj );
Mql4/5 doesn't provide such support.
But this capability can be synthesized with a simple base class and a macro
class Polymorphic{
 public:
   virtual bool         isA(string c)           const{   return(FALSE); };
   virtual Polymorphic* dynamic_cast(string c)  const{   return(NULL);  };
// #define POLYMORPHIC(stringCLASS, typenameBASE)                              \
//    virtual bool isA(string c)                   const{                      \
//       return( c == stringCLASS || typenameBASE::IsA(c) ); };                \
//    virtual Polymorphic* dynamic_cast(string c)  const{                      \
//       return( isA(stringCLASS) ? GetPointer(this) : NULL ); }
};
I've split the macro here for readability. It must be on one line in mql4/5
Given these definitions
class base : public Polymorphic{
 public: POLYMORPHIC("base",Polymorphic);
   void iAM(int i){ Print("iam a base "+i); }
};
class derv : public base{
 public:  POLYMORPHIC("derv",base);
   void iAM(int i){ Print("iam a derv:base "+i); }
};
class other : public base{
 public: POLYMORPHIC("other",base);
   void iAM(int i){ Print("iam a other:base "+i); }
};
I've put the macro next to the opening brace because the class name (as a string) and the base class must exactly match. (Must be a string since Metaquotes didn't provide the # operator in the preprocessor. Why not?)
This script produces
void OnStart(){
   base* obj = new other;
   derv*  d = obj.dynamic_cast("derv"); if(d) d.iAM(__LINE__); } // 
   if(obj.isA("other")){ other* o=(other*)obj; o.iAM(__LINE__);  // iam a other:base nn
d = NULL since a derv is base but is not an other. The up cast to other succeeds since obj is an other. QED.

See the attached file for more examples. (Russian post динамическое приведение - MQL4 форум)

Attached files:
  dynamiccast_2.txt  (1.64 KB)
avatar
Moderator
3206
angevoyageur 2014.04.27 19:51 #
 

Thank you for sharing.

Do you have a practical real world example where this can be useful ?

avatar
12983
WHRoeder 2014.04.27 21:18 #
 

The usual case for an up cast is when you are processing a collection of objects (array of pointers to the base type) but need to call a method that is not virtual in the base class, or want to call it for those objects that support it.

There are only three options:

  1. Refactor the paradigm (expensive.)
  2. Add a second virtual function to the base class (may not be possible) that just calls the first and override that method in a derived class. This complicates the base class, for something only a derived class uses:
    class Text{
      virtual Print();
      virtual PrintFancy(){ Print(); } // ? What formatting does text have?
    }
    class Html{
      virtual Print();        // Drops formatting
      virtual PrintFancy();   // Keeps formatting
    }
    Printer(Text& t){
       t.PrintFancy(); // ? what does it mean for text to print Fancy?
    
  3. Try to up cast the pointer and if it succeeds call the method. Base class (Text) and any classes derived from Base (WrappedText) are unaffected. Only the class with the new method (and subclasses from there are aware of the change.)
    class Text{
      virtual Print();
    }
    class WrappedText : public Text{
      virtual Print();        // Word wrapping
    };                        // But no fancy printing here
    class Html        : public Text{
      virtual Print();        // Drops formatting
      virtual PrintFancy();   // Keeps formatting
    }
    Printer(Text& t){
      Html h = Dynamic_Cast<h&> t;
      if(h) h.PrintFancy();
      else  t.Print();

In C++ you up cast it in a try/catch block or use a dynamic_cast. dynamic_cast - Wikipedia, the free encyclopedia

Mql4/5 doesn't have either.

avatar
Moderator
3206
angevoyageur 2014.04.28 09:02 #
 
WHRoeder:

The usual case for an up cast is when you are processing a collection of objects (array of pointers to the base type) but need to call a method that is not virtual in the base class, or want to call it for those objects that support it.

There are only two options:

  1. Add a second virtual function to the base class (my not be possible) that just calls the first and override that method in a derived class. This complicates the base class, for something only a derived class uses:
  2. Try to up cast the pointer and if it succeeds call the method. Base class (Text) and any classes derived from Base (WrappedText) are unaffected. Only the class with the new method (and subclasses from there are aware of the change.)

In C++ you up cast it in a try/catch block or use a dynamic_cast. dynamic_cast - Wikipedia, the free encyclopedia

Mql4/5 doesn't have either.

Interesting, thank you.

avatar
12983
WHRoeder 2015.10.03 15:13 #
 
Attached is my update version. I use a template method for type deduction instead of the string argument I used before.
Also I make the macro calls protected in the actual objects as they aren't supposed to be called.
class Polymorphic{
 public:
   template<typename D>
   bool     dynamic_cast(D*& dp)       const{ ...
}
class Object : public Polymorphic_NC{     // Noncopyable
 protected: POLYMORPHIC(Polymorphic_NC);
 :
}
class Derived : public Object{            // Noncopyable
 protected: POLYMORPHIC(Object);
 public:    void DeriveOnly(){ ...}
 :
}

Object*   obj = ...;
Derived*  derv;
if(object.dynamic_cast(derv) ) derv.DeriveOnly();
Also note that I have not provided const versions because the compiler (880 and earlier) can't cast constants
derive* d = (const derive*)b;
'const' - unexpected token      testscr.mq4     67      16
Attached files:
  polymorphic.mqh  (5.41 KB)
avatar
162
mql5 2016.02.09 16:31 #
 
Operator dynamic_cast will be added

avatar
1109
Ovo 2016.02.09 19:23 #
 
mql5:
Operator dynamic_cast will be added

What will be the advantage in comparison to the current casting?

 This will be probably implemented

B* my_b = dynamic_cast<B*>(my_a);

 and this is how it works currently:

B* my_b = my_a;

 Did I miss anything, or is it just a plan to complicate things?

avatar
12983
WHRoeder 2016.02.09 20:20 #
 

Currently, if my_a is not a B the assignment results in the termination of the script.

Last line of Typecasting - MQL4 Documentation

But you must be fully confident in the admissibility of such a transformation, because otherwise a critical runtime error will occur and the mql4 program will be stopped.

avatar
1109
Ovo 2016.02.09 20:44 #
 
WHRoeder:

Currently, if my_a is not a B the assignment results in the termination of the script.

Last line of Typecasting - MQL4 Documentation


Well, is there any difference then? I assume the dynamic_cast would terminate the script as well.
avatar
12983
WHRoeder 2016.02.09 22:48 #
 
In C++ dynamic_cast returns NULL if the cast can't be done; same as my code. There's no need for it unless it doesn't terminate the script.
Back to topics list  

To add comments, please log in or register