How can I use either < or > (or other comparative operator) in an expression depending on a function input?

 

I have two longish blocks of code that are identical except in various comparative statements '>' is switched with '<', '>=' with '<=' etc. I wanted to put these in a function and use one operator or another depending on a function input. In C++ I could create a comparator function for each comparison needed, and then pass the right function as an argument  (wrapped in a suitably defined function) as below. But my indicator doesn't compile when I do this. What alternatives are there?

#include <iostream>
using namespace std;

bool comp1(int a, int b) {
    return a > b;
}

bool comp2(int a, int b) {
    return a < b;
}

void myFunc(int a, int b, bool (*myComp)(int, int)) {
    bool res = myComp(a, b);
    cout << "value : " << res << endl;
}
int main()
{
    myFunc(1, 2, comp1);
    myFunc(1, 2, comp2);
    return 0;
}
 
whitebloodcell:

I have two longish blocks of code that are identical except in various comparative statements '>' is switched with '<', '>=' with '<=' etc. I wanted to put these in a function and use one operator or another depending on a function input. In C++ I could create a comparator function for each comparison needed, and then pass the right function as an argument  (wrapped in a suitably defined function) as below. But my indicator doesn't compile when I do this. What alternatives are there?

You can use polymorphism. Instead of a pointer to function instances of a polymorphic class might be used:

#property strict

class Comparable {
public:
  virtual bool compare(int a, int b) const {
    Print("Comparable::compare(int a = ", a, ", b = ", b, "): should NOT be called normally!!!");
    return false;
  }
};

class Less: public Comparable {
public:
  bool compare(int a, int b) const {
    Print("Less::compare(int a = ", a, ", b = ", b, ")");
    return a < b;
  }
};

class Greater: public Comparable {
public:
  bool compare(int a, int b) const {
    Print("Greater::compare(int a = ", a, ", b = ", b, ")");
    return a > b;
  }
};

void myFunc(int a, int b, const Comparable &comparable) {
  bool res = comparable.compare(a, b);

  Print("myFunc(a = ", a, ", b = ", b, "), Value: ", res);
}

void OnStart() {
  Less less;
  Greater greater;

  myFunc(1, 2, less);
  myFunc(1, 2, greater);
}

When the script is executed the following messages appear in the log:

19:21:52 Script 1 EURUSD,H1: loaded successfully
19:21:52 1 EURUSD,H1: initialized
19:21:52 1 EURUSD,H1: Less::compare(int a = 1, b = 2)
19:21:52 1 EURUSD,H1: myFunc(a = 1, b = 2), Value: true
19:21:52 1 EURUSD,H1: Greater::compare(int a = 1, b = 2)
19:21:52 1 EURUSD,H1: myFunc(a = 1, b = 2), Value: false
19:21:52 1 EURUSD,H1: uninit reason 0
19:21:52 Script 1 EURUSD,H1: removed

Thanks to added Print() calls the script internal workings are self-explanatory with the log messages.

The method compare() can be overloaded accepting pairs of parameters of another types. In this case it is possible to compare variables of those types too. Even class type objects might be compared if the corresponding overloads of the method compare() are defined.

 

Great, thanks! Is there a way to make the virtual function a template. or do I need to make overloads for each type? I tried the below but it didn't compile

  template<typename T>
  virtual bool compare(T a, T b) const {
    Print("Comparable::compare(int a = ", a, ", b = ", b, "): should NOT be called normally!!!");
    return false;
}
 
whitebloodcell:

Great, thanks! Is there a way to make the virtual function a template. or do I need to make overloads for each type? I tried the below but it didn't compile

See documentation :

Function templates should not be declared with export, virtual and #import keywords.

 
whitebloodcell:

Great, thanks! Is there a way to make the virtual function a template. or do I need to make overloads for each type? I tried the below but it didn't compile

Method templates cannot be virtual even in C++.

Probably, you need class templates in order to get what you want but MQL4 compiler currently says: "template classes are not supported yet".

However, take in account that MQL4 currently has no support for neither function template specialization nor function template overloading by a function, so you cannot define special implementations of the methods for distinct types if you would wish to.

Moreover, function templates in couple with the type scheme are designed defectively: you cannot use function templates as for class type objects as for immediate values of fundamental types (see commented lines below), although variables of that types are still usable.

Implementation is imperfect and uses an enum-driven switch-based dispatch:

#property strict

template <typename T>
bool less(const T &a, const T &b) {
  Print("less(a, b)");
  return a < b; 
}

template <typename T>
bool greater(const T &a, const T &b) {
  Print("greater(a, b)");
  return a > b;
}

enum Operation {
  less,
  greater
};

template <typename T>
bool myFuncExec(const T &a, const T &b, Operation operation) {
  switch (operation) {
  case less:
    return ::less(a, b);
  case greater:
    return ::greater(a, b);
  default:
    Print("Internal error");
  }

  return false;
}

template <typename T>
void myFunc(const T &a, const T &b, Operation operation) {
  bool res = myFuncExec(a, b, operation);

  Print("myFunc(a, b, operation = ", operation, "), Value: ", res);
}

class Value {
private:
  int i;

public:
  Value(int i): i(i) {
    Print("Value::Value(int i = ", i, ")");
  }

  bool operator <(const Value &value) const {
    Print("Value::operator <()");
    return i < value.i;
  }

  bool operator >(const Value &value) const {
    Print("Value::operator >()");
    return i > value.i;
  }
};

void OnStart() {
  int i1 = 1;
  int i2 = 2;

  myFunc(i1, i2, less);
  myFunc(i1, i2, greater);

  // myFunc(1, 2, less); // Error here: '1' - parameter passed as reference, variable expected
  // myFunc(1, 2, greater); // Error here: '1' - parameter passed as reference, variable expected

  Value value1(1);
  Value value2(2);

  myFunc(value1, value2, less);
  myFunc(value1, value2, greater);
}

The log has the following messages:

04:16:39 Script 1 EURUSD,H1: loaded successfully
04:16:39 1 EURUSD,H1: initialized
04:16:39 1 EURUSD,H1: less(a, b)
04:16:39 1 EURUSD,H1: myFunc(a, b, operation = 0), Value: true
04:16:39 1 EURUSD,H1: greater(a, b)
04:16:39 1 EURUSD,H1: myFunc(a, b, operation = 1), Value: false
04:16:39 1 EURUSD,H1: Value::Value(int i = 1)
04:16:39 1 EURUSD,H1: Value::Value(int i = 2)
04:16:39 1 EURUSD,H1: less(a, b)
04:16:39 1 EURUSD,H1: Value::operator <()
04:16:39 1 EURUSD,H1: myFunc(a, b, operation = 0), Value: true
04:16:39 1 EURUSD,H1: greater(a, b)
04:16:39 1 EURUSD,H1: Value::operator >()
04:16:39 1 EURUSD,H1: myFunc(a, b, operation = 1), Value: false
04:16:39 1 EURUSD,H1: uninit reason 0
04:16:39 Script 1 EURUSD,H1: removed

Have a nice day...

 
simpleton:

Method templates cannot be virtual even in C++.

...

Moreover, function templates in couple with the type scheme are designed defectively: you cannot use function templates as for class type objects as for immediate values of fundamental types (see commented lines below), although variables of that types are still usable.

...

Sorry but I miss your point here, what is designed defectively ? The commented lines (the error) have nothing to do with your template, you are passing a constant where the parameter is defined as a reference.

I probably misunderstood what you mean.

 
angevoyageur:

Sorry but I miss your point here, what is designed defectively ? The commented lines (the error) have nothing to do with your template, you are passing a constant where the parameter is defined as a reference.

I probably misunderstood what you mean.

The parameters are defined not just as references but as constant references. In C++ temporaries/rvalues are perfectly bound to constant references:

#include <iostream>

template <typename T>
void myFunc(T const &a, T const &b) {
        std::cout << "a = " << a << ", b = " << b << std::endl;
}

int main() {
        myFunc(1, 2);
        return 0;
}

The code is compiled perfectly and produces the next output:

a = 1, b = 2
That is my point here.
 
simpleton:

The parameters are defined not just as references but as constant references. In C++ temporaries/rvalues are perfectly bound to constant references:

The code is compiled perfectly and produces the next output:

That is my point here.
I see, thank you.
Reason: