Is it possible to generate an ERR_INVALID_PARAMETER error on class instantiation?

 

In MQL 4, I want to generate this error if my class fails to initialize. Like so:

void CClass::CClass(int interval) // constructor
 {
    if(interval<=0)
     {
        _LastError=ERR_INVALID_PARAMETER; // constant cannot be modified compiler error
        ExpertRemove();
        return;
     }
    m_interval=interval;
 }

But this doesn't compile.

Ideally I'd like to let the caller know that this instance generation failed. Like so:

CClass *clptr;

int OnInit()
 {
    clptr=new CClass(Inp_Interval);
    if(clptr==NULL) // this does not work!
     {
        Print("failed to create CClass, error: ",GetLastError());
        return INIT_FAILED;
     }
    ...
 }

I know I could use SetUserError() but I'd like to use one of the others, and I'd like to let this instantiation fail. How could I achieve that?

 
lippmaje:

In MQL 4, I want to generate this error if my class fails to initialize. Like so:

But this doesn't compile.

Ideally I'd like to let the caller know that this instance generation failed. Like so:

I know I could use SetUserError() but I'd like to use one of the others, and I'd like to let this instantiation fail. How could I achieve that?

You don't have much choice, either you set a status flag in your class, something like

bool m_initialized=(interval>0);

and then check it in the caller.

Or you use a custom error, what is the problem with this one ? What do you mean by "use one of the others" ?

 

My idea was if I could set _LastError to a system error code I'd get neat error descriptions with

#include <ErrorDescription.mqh>

#define LOGERR(a) Print(a," error: ",ErrorDescription(GetLastError()))

...
LOGERR("failed to create CClass");
 

Use SetUserError()

But the ErrorDescription() needs to be extended in order to be able to handle your error

 
lippmaje:

My idea was if I could set _LastError to a system error code I'd get neat error descriptions with

ERR_INVALID_PARAMETER is not a system error in mql4.

   void CClass::CClass(int interval) // constructor
     {
      if(interval<=0)
        {
         SetUserError(ERR_INVALID_FUNCTION_PARAMVALUE);
         m_interval=WRONG_VALUE;
         return;
        }
      m_interval=interval;
     }
#define LOGERR(a,e) Print(a," error: ",ErrorDescription(e-ERR_USER_ERROR_FIRST))
CClass *clptr;

int OnInit()
  {
   clptr=new CClass(Inp_Interval);
   if(_LastError==ERR_USER_ERROR_FIRST+ERR_INVALID_FUNCTION_PARAMVALUE)
     {
      LOGERR("failed to create CClass",GetLastError());
      delete clptr;      
      return(INIT_FAILED);
     }
}
 
Alain Verleyen:

ERR_INVALID_PARAMETER is not a system error in mql4.

Right, it's MQL 5, I was looking into the wrong reference manual.

The code looks good to go, thank you and @mladen.


Here's the latest version in case someone's interested:

#include <ErrorDescription.mqh>

#define LOGERR(a) Print(a,", error: ",_LastError," ",ErrorDescription(_LastError<ERR_USER_ERROR_FIRST ? _LastError : _LastError-ERR_USER_ERROR_FIRST))

...
void CClass::CClass(int interval) // constructor
  {
   ResetLastError();
   if(interval<=0)
     {
      SetUserError(ERR_INVALID_FUNCTION_PARAMVALUE);
      m_interval=WRONG_VALUE;
      return;
     }
   m_interval=interval;
  }

CClass *clptr;

int OnInit()
  {
   clptr=new CClass(Inp_Interval);
   if(_LastError)
     {
      LOGERR("failed to create CClass"); // -> failed to create CClass, error: 69587 Invalid function parameter value
      delete clptr;      
      return INIT_FAILED;
     }
   return INIT_SUCCEEDED;
  }

ErrorDescription.mqh: (for MQL4, for MQL5 see here https://www.mql5.com/de/code/79)

#import "stdlib.ex4"

string ErrorDescription(int error_code);

#import
ErrorDescription
ErrorDescription
  • www.mql5.com
LoongClock MovingAverages Die MovingAverages Library enthält Funktionen für die Berechnung verschiedener Typen von Gleitenden Durchschnitten. TimerClosingPeriod Der Indikator gibt die Zeit bis zum Schluss der Periode der aktuellen Zeiteinheit aus. Ist diese kleiner als H1 wird...
 

It's better practice to implement an init method on your class instead of trying to use MQL's Error system. It will also allow you the opportunity to create static objects in the global scope and still be able to initialize them later. 


class Test {
 public:
   bool init(int arg) {
      if (!(0 < arg && arg < 10)) {
         Print("Error: failed to init Test with arg=", arg);
         return false;
      }
      return true;
   }
};

Test *g_test_ptr;
Test g_test_obj;

int OnInit() {
   g_test_ptr = new Test();
   return (g_test_obj.init(3) && g_test_ptr.init(20)) ? INIT_SUCCEEDED : INIT_FAILED;
}
 
nicholi shen:

It's better practice to implement an init method on your class instead of trying to use MQL's Error system. It will also allow you the opportunity to create static objects in the global scope and still be able to initialize them later. 


Good. I have been thinking about that. It's just a few classes at the moment but I'd want to settle with a consistent scheme if it gets more.

Setting error codes and leaving the callstack through repeated returns instead of throwing exceptions is sort of a hassle, but yeah ok, we have to live with it I guess.

 

Using an Init() method can hardly be called a better practice. It's very similar to using a custom error, we are remaining with uninitialized or wrong state object.

Of course MQL is not C++ is you have to be creative.

How to handle failure in constructor in C++?
How to handle failure in constructor in C++?
  • 2011.02.14
  • ThomsonThomson 8,0561515 gold badges6666 silver badges110110 bronze badges
  • stackoverflow.com
I want to open a file in a class constructor. It is possible that the opening could fail, then the object construction could not be completed. How to handle this failure? Throw exception out? If this is possible, how to handle it in a non-throw constructor?
 
Alain Verleyen:

Using an Init() method can hardly be called a better practice. It's very similar to using a custom error, we are remaining with uninitialized or wrong state object.

Of course MQL is not C++ is you have to be creative.

MQL is nowhere near the sophistication of other languages when it comes to exception handling (there is none). I'd strongly argue that a boolean initialization method is the best way to initialize an object in MQL, and especially since it is the way that almost all MQL library code is written. No need to deviate from MQL design patterns and attempt to use the Error system which only serves to provide logging. Perhaps you could set a user error from an initializer method, but one thing is for certain -- you never want to use the constructor for initialization when anything else relies on a successful instantiation of the object. That's a big MQL no-no.

 
lippmaje:

In MQL 4, I want to generate this error if my class fails to initialize. Like so:

But this doesn't compile.

Ideally I'd like to let the caller know that this instance generation failed. Like so:

I know I could use SetUserError() but I'd like to use one of the others, and I'd like to let this instantiation fail. How could I achieve that?

Use static constructors (another benefit is to give meaningful names to constructors, like the example)

class CAnimal
  {
private:
   string            m_name;
   int               m_age;
   color             m_color;
   color             m_eye_color;
protected:
   static CAnimal   *m_instance;
   CAnimal(const string name,const int age,const color clr,const color eye_color)
           : m_name(name),m_age(age),m_color(clr),m_eye_color(eye_color){}
public:
                    ~CAnimal()
     {
      if(CheckPointer(m_instance)==POINTER_DYNAMIC && &this!=m_instance)
         delete m_instance;
     }
   string         Name() {return m_name;}
   int            Age()  {return m_age;}
   color          Color(){return m_color;}
   color          EyeColor(){return m_eye_color;}
   virtual void      MakeSound()=NULL;
  };
CAnimal *CAnimal::m_instance=NULL;

class CDog : public CAnimal
  {
private:
   CDog(const string name,const int age,const color clr,const color eye_color)
          : CAnimal(name,age,clr,eye_color) {}
                    ~CDog() {}

public:
   static CDog    *CreateAdultDog(const string name,const int age,const color clr,const color eye_color)
     {
      //--- check
      if(age<2 || clr==clrRed)
        {
         Print("Adult dogs should be above 2 years old");
         return NULL;
        }
      if(clr==clrRed)
        {
         Print("Can't create ",name," Dogs can't be red");
         return NULL;
        }
      m_instance=new CDog(name,age,clr,eye_color);
      return m_instance;
     }
   static CDog    *CreateYoungDog(const string name,const int age,const color clr,const color eye_color)
     {
      //--- check
      if(age>=2)
        {
         Print("Can't create ",name," Yound dogs should be under 2 years old");
         return NULL;
        }
      if(clr==clrRed)
        {
         Print("Can't create ",name," Dogs can't be red");
         return NULL;
        }
      m_instance=new CDog(name,age,clr,eye_color);
      return m_instance;
     }
   virtual void   MakeSound()
     {
      Print("Woof! I am ",Name()," And I am ",Age()<2 ? "a young" : "an adult"," dog");
     }

  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   CAnimal *pluto=CDog::CreateAdultDog("Pluto",3,clrBrown,clrGreen);
   CAnimal *elton=CDog::CreateYoungDog("Elton",3,clrBlack,clrBrown);
   CAnimal *reddog=CDog::CreateYoungDog("Sugar",1,clrRed,clrGray);
   if(CheckPointer(pluto))
     {
      pluto.MakeSound();
      delete pluto;
     }
   if(CheckPointer(elton))
     {
      elton.MakeSound();
      delete elton;
     }
   if(CheckPointer(reddog))
     {
      reddog.MakeSound();
      delete reddog;
     }
  }
Reason: