• Join us

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

To add comments, please log in or register
lippmaje
1087
lippmaje  
Amir Yacoby:

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

I have been thinking about this while I read Alain's link. Somewhere there they mentioned this, too, it's called it a builder method (or pattern) right?

Let's say I go and combine that with the user error scheme above, then I'd get something like this:

#include <ErrorDescription.mqh>

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

class CClass
  {
protected:
   int m_interval;
   CClass(int interval); // non-public constructor
   
public:
   static CClass *New(int interval); // static builder method
  };

void CClass::CClass(int interval) // constructor
  {
   m_interval=interval;
   // construct some things here, interval is valid
  }

static CClass *New(int interval) // builder
  {
   CClass *ptr=NULL;

   if(interval<=0)
     {
      SetUserError(ERR_INVALID_FUNCTION_PARAMVALUE);
      LOGERR("interval must be >0"); // -> interval must be >0, error: 69587 Invalid function parameter value
     }
   else
     {
      ptr=new CClass(interval);
     }
   return ptr;
  }

CClass *clptr;

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

This combines three proposals:

  1. Alain's to not use an Init function aka 2nd constructor which could result in 'zombie' instances if not handled correctly.
  2. Nicholi's to not 'abuse' the error system by querying _LastError/GetLastError() instead of using a proper method.
  3. Amir's to use a builder method, which is also mentioned in the StackOverflow thread as good practice.

Comments?

Amir Yacoby
1421
Amir Yacoby  
lippmaje:

I have been thinking about this while I read Alain's link. Somewhere there they mentioned this, too, it's called it a builder method (or pattern) right?

Let's say I go and combine that with the user error scheme above, then I'd get something like this:

This combines three proposals:

  1. Alain's to not use an Init function aka 2nd constructor which could result in 'zombie' instances if not handled correctly.
  2. Nicholi's to not 'abuse' the error system by querying _LastError/GetLastError() instead of using a proper method.
  3. Amir's to use a builder method, which is also mentioned in the StackOverflow thread as good practice.

Comments?

It's called Named constructors 


*-it is based on kind of factory method, what you use is the thin version for only the factory, which is satisfying for most cases
nicholi shen
2402
nicholi shen  
lippmaje:

I have been thinking about this while I read Alain's link. Somewhere there they mentioned this, too, it's called it a builder method (or pattern) right?

Let's say I go and combine that with the user error scheme above, then I'd get something like this:

This combines three proposals:

  1. Alain's to not use an Init function aka 2nd constructor which could result in 'zombie' instances if not handled correctly.
  2. Nicholi's to not 'abuse' the error system by querying _LastError/GetLastError() instead of using a proper method.
  3. Amir's to use a builder method, which is also mentioned in the StackOverflow thread as good practice.

Comments?

This is a python design pattern applied to MQL and it doesn't make sense. In general you only want to use dynamic objects when you need to add them to collections or want to completely destroy them for some reason (like Expert reinitialization). When you use the Error system you are only setting a hidden global flag. You might as well just have a global int variable which you check. If the goal of the objects construction is to return a NULL pointer in the event of an error then just set the error flag and delete the object -- instead of the rube-goldberg like use of other languages' design patterns. 

#define LOGERR(a) Print(a)

class CClass {
 protected:
   int m_interval;
 public:
   CClass(int interval);
};

void CClass::CClass(int interval) {
   m_interval = interval;
   if (interval <= 0) {
      LOGERR("interval must be >0");
      if (CheckPointer(&this) == POINTER_DYNAMIC) 
         delete &this;
   }   
}


CClass *clptr;

int OnInit() {
   clptr = new CClass(-1);
   if(!CheckPointer(clptr)) {
      LOGERR("failed to create CClass"); // -> failed to create CClass, error: 69587 Invalid function parameter value
      return INIT_FAILED;
   }
   return INIT_SUCCEEDED;
}
lippmaje
1087
lippmaje  

@nicholi shen thank you, this looks very interesting, I'll study/explore the solution later.


So today I came across this code sequence in OnInit of some EA:

CMoneyFixedMargin *m_money;

int OnInit() {
   ...
      if(m_money!=NULL)
         delete m_money;
      m_money=new CMoneyFixedMargin;
      if(m_money!=NULL)
        {
         if(!m_money.Init(GetPointer(m_symbol),Period(),m_symbol.Point()*digits_adjust))
            return(INIT_FAILED);
         m_money.Percent(Risk);
        }
      else
        {
         Print(__FUNCTION__,", ERROR: Object CMoneyFixedMargin is NULL");
         return(INIT_FAILED);
        }
   ...
}

I mention it here since it seems to fit my topic.

It looks like the class instance requires an additional initialization, 1. constructor, 2. init call. This could hardly be considered a good practice, right?

I haven't delved much into the MQL classes so far. Is this standard behavior?

12
To add comments, please log in or register