Writing generic code in MQL for Objects [solved] - page 3

 
Dominik Christian Egert #:
interesting.

So you would as well state, this is awkward behavior?

What kind of interfaces are you talking about? Marshalling? Because standard interfaces are available. What we don't have are lambdas. But that could be at least partially mimicked with templates and virtual functions, if templating were to work fully.

But, interfaces are available. Not template overloading and not virtual template member functions... but interfaces... :-)
meaningless.

IInterface is not related to inheritance, it is a different concept.

Interface is a contract that a class adheres to, which should not be part of the inheritance tree. Meaning it doesn't matter if the language allows multy inheritance or not, but the class in addition to inhheriting its father(or fathers) also apllies those contracts.

What MQL5 did is tying up these two concepts and as result caused a situation where the interface was emptied from its meaning.

The whole generic library is useless without supllying IComparable<T> as interface for instance if you want to use a class as key in CHashMap, you have to be able  extend the IComparable in that class to override the hash method.
 
Amir Yacoby #:
meaningless.

IInterface is not related to inheritance, it is a different concept.

Interface is a contract that a class adheres to, which should not be part of the inheritance tree. Meaning it doesn't matter if the language allows multy inheritance or not, but the class in addition to inhheriting its father(or fathers) also apllies those contracts.

What MQL5 did is tying up these two concepts and as result caused a situation where the interface was emptied from its meaning.

The whole generic library is useless without supllying IComparable<T> as interface for instance if you want to use a class as key in CHashMap, you have to be able  extend the IComparable in that class to override the hash method.
Understood, but that's C++ concepts. MQL is different, a separate language.

But I do get your point.

EDIT:

MQL has all the essence removed from the interface in this regard. And I personally find the concept more irritating, especially, because it's a single inheritance model. You cannot combine multiple base classes into one derived version.

MQL calls it interface, but from a C++ standpoint it's just an abstract class without constructor.
 
Dominik Christian Egert #:

MQL calls it interface, but from a C++ standpoint it's just an abstract class without constructor.
Right so no interface in MQL and yet they copy the .net classes as generic library but make them pretty useless because of that.  
 
Alain Verleyen #:

I don't have time currently (for some days), I will check when possible (don't hesitate to ask again in case I forget).

I continued to work on a solution, and here it is:

struct my_rates_time : public MqlRates
{
    // Default constructor
    my_rates_time()                                         {};

    // Copy constructor
    my_rates_time(const MqlRates& obj)                      { this = obj; };

    // Operators
    const bool  operator> (const my_rates_time& obj) const  { return(time > obj.time); };
};

static int serial = 1;
class test_obj
{
    public:
    int value;
    
    test_obj() :
        value (serial++)
    { };
    
    test_obj(const test_obj& p_in) :
        value (p_in.value)
    { Print("Copy C"); };

    const bool operator> (const test_obj& p_in) const { return(value > p_in.value); };
};


class test_derived : public test_obj
{
    public:
    test_derived()
    { };
    
    test_derived(const test_derived& p_in) :
        test_obj(p_in)
    { Print("Copy D"); };

    test_derived(const test_obj& p_in) :
        test_obj(p_in)
    { Print("Copy E"); };

    const bool operator> (const test_derived& p_in) const { return(value > p_in.value); };
};



template <typename USER_DATA>
struct _test
{
    USER_DATA __dummy;


    template <typename UNIFIED>
    const bool __sub(const UNIFIED* p_in)
    { return((p_in != NULL) ? (__dummy > p_in) : true); };
    
    template <typename UNIFIED>
    const bool __sub(const UNIFIED& p_in) const
    { return(__dummy > p_in); };



    template <typename TARGET_TYPE, typename SOURCE_TYPE>
    void __func(TARGET_TYPE*& p_out, SOURCE_TYPE* p_in)
    { __sub(dynamic_cast<const TARGET_TYPE*>(p_in)); };
    
    template <typename TARGET_TYPE, typename SOURCE_TYPE>
    void __func(TARGET_TYPE*& p_out, SOURCE_TYPE& p_in)
    { __sub(dynamic_cast<const TARGET_TYPE*>(&p_in)); };
    
    void __func(USER_DATA& p_out, const USER_DATA& p_in)
    { __sub(p_in); };
    
    void __func(USER_DATA& p_out, USER_DATA& p_in)
    { __sub(p_in); };
    
    template <typename TARGET_TYPE, typename SOURCE_TYPE>
    void __func(TARGET_TYPE& p_out, SOURCE_TYPE& p_in)
    { __sub(TARGET_TYPE(p_in)); };
    
    template <typename TARGET_TYPE, typename SOURCE_TYPE>
    void __func(TARGET_TYPE& p_out, const SOURCE_TYPE* p_in)
    { __sub(dynamic_cast<const TARGET_TYPE*>(p_in)); };
    
    template <typename TARGET_TYPE, typename SOURCE_TYPE>
    void __func(TARGET_TYPE& p_out, const SOURCE_TYPE p_in) const
    { __sub(p_in); };


    template <typename CALL_TYPE>
    void __call(const CALL_TYPE* p_in)
    { __func(__dummy, p_in); };

    template <typename CALL_TYPE>
    void __call(const CALL_TYPE& p_in)
    { __func(__dummy, p_in); };

    template <typename CALL_TYPE>
    void __call(const CALL_TYPE p_in) const 
    { __func(__dummy, p_in); };

};




void OnStart()
{
    test_obj        target_obj;
    test_obj*       target_ptr  = new test_obj();
    test_derived    target_d_obj;
    test_derived*   target_d_ptr    = new test_derived();
    my_rates_time   target_struct;
    int             target_int;
    
        test_obj                    _obj();
        const test_obj              c_obj();
        test_obj*                   _ptr        = GetPointer(_obj);
        const test_obj*             c_ptr       = new test_obj();
        test_derived                _d_obj();
        const test_derived          c_d_obj();
        test_derived*               _d_ptr        = GetPointer(_d_obj);
        const test_derived*         c_d_ptr       = new test_derived();
        MqlRates                    _rates;
        const MqlRates              c_rates;
        my_rates_time               _struct;
        const my_rates_time         c_struct;
        int                         _int        = 5;
        const int                   c_int       = 8;




    _test<test_obj> test_obj_tpl;
    
    test_obj_tpl.__func(target_obj, _obj);
    test_obj_tpl.__func(target_obj, c_obj);
    test_obj_tpl.__func(target_obj, _ptr); 
    test_obj_tpl.__func(target_obj, c_ptr);

    test_obj_tpl.__func(target_obj, _d_obj);        // Calls copy constructor
    test_obj_tpl.__func(target_obj, c_d_obj);       // Calls copy constructor
    test_obj_tpl.__func(target_obj, _d_ptr); 
    test_obj_tpl.__func(target_obj, c_d_ptr);

    test_obj_tpl.__call(_obj);
    test_obj_tpl.__call(c_obj);
    test_obj_tpl.__call(_ptr); 
    test_obj_tpl.__call(c_ptr);

    test_obj_tpl.__call(_d_obj);                    // Calls copy constructor
    test_obj_tpl.__call(c_d_obj);                   // Calls copy constructor
    test_obj_tpl.__call(_d_ptr); 
    test_obj_tpl.__call(c_d_ptr);




    _test<test_obj*> test_ptr_tpl;
    test_ptr_tpl.__dummy = target_ptr;

    test_ptr_tpl.__func(target_ptr, _obj);
    test_ptr_tpl.__func(target_ptr, c_obj);
    test_ptr_tpl.__func(target_ptr, _ptr);
    test_ptr_tpl.__func(target_ptr, c_ptr);

    test_ptr_tpl.__func(target_ptr, _d_obj);
    test_ptr_tpl.__func(target_ptr, c_d_obj);
    test_ptr_tpl.__func(target_ptr, _d_ptr);
    test_ptr_tpl.__func(target_ptr, c_d_ptr);

    test_ptr_tpl.__call(_obj);
    test_ptr_tpl.__call(c_obj);
    test_ptr_tpl.__call(_ptr);
    test_ptr_tpl.__call(c_ptr);

    test_ptr_tpl.__call(_d_obj);
    test_ptr_tpl.__call(c_d_obj);
    test_ptr_tpl.__call(_d_ptr);
    test_ptr_tpl.__call(c_d_ptr);




    _test<test_derived> test_d_obj_tpl;

    test_d_obj_tpl.__func(target_d_obj, _d_obj);
    test_d_obj_tpl.__func(target_d_obj, c_d_obj);
    test_d_obj_tpl.__func(target_d_obj, _d_ptr);
    test_d_obj_tpl.__func(target_d_obj, c_d_ptr);

    test_d_obj_tpl.__func(target_d_obj, _obj);          // Calls copy constructor
    test_d_obj_tpl.__func(target_d_obj, c_obj);         // Calls copy constructor
    test_d_obj_tpl.__func(target_d_obj, _ptr);
    test_d_obj_tpl.__func(target_d_obj, c_ptr);

    test_d_obj_tpl.__call(_d_obj);
    test_d_obj_tpl.__call(c_d_obj);
    test_d_obj_tpl.__call(_d_ptr);
    test_d_obj_tpl.__call(c_d_ptr);

    test_d_obj_tpl.__call(_obj);                        // Calls copy constructor
    test_d_obj_tpl.__call(c_obj);                       // Calls copy constructor
    test_d_obj_tpl.__call(_ptr);
    test_d_obj_tpl.__call(c_ptr);



    _test<test_derived*> test_d_ptr_tpl;
    test_d_ptr_tpl.__dummy = target_d_ptr;

    test_d_ptr_tpl.__func(target_d_ptr, _d_obj);
    test_d_ptr_tpl.__func(target_d_ptr, c_d_obj);
    test_d_ptr_tpl.__func(target_d_ptr, _d_ptr);
    test_d_ptr_tpl.__func(target_d_ptr, c_d_ptr);

    test_d_ptr_tpl.__func(target_d_ptr, _obj);
    test_d_ptr_tpl.__func(target_d_ptr, c_obj);
    test_d_ptr_tpl.__func(target_d_ptr, _ptr);
    test_d_ptr_tpl.__func(target_d_ptr, c_ptr);

    test_d_ptr_tpl.__call(_d_obj);
    test_d_ptr_tpl.__call(c_d_obj);
    test_d_ptr_tpl.__call(_d_ptr);
    test_d_ptr_tpl.__call(c_d_ptr);

    test_d_ptr_tpl.__call(_obj);
    test_d_ptr_tpl.__call(c_obj);
    test_d_ptr_tpl.__call(_ptr);
    test_d_ptr_tpl.__call(c_ptr);



    _test<my_rates_time> test_struct_tpl;

    test_struct_tpl.__func(target_struct, _rates);      // Calls copy constructor
    test_struct_tpl.__func(target_struct, c_rates);     // Calls copy constructor
    test_struct_tpl.__func(target_struct, _struct);
    test_struct_tpl.__func(target_struct, c_struct);

    test_struct_tpl.__call(_rates);                     // Calls copy constructor
    test_struct_tpl.__call(c_rates);                    // Calls copy constructor
    test_struct_tpl.__call(_struct);
    test_struct_tpl.__call(c_struct);


    _test<int> test_int_tpl;

    test_int_tpl.__func(target_int, 7);
    test_int_tpl.__func(target_int, _int);
    test_int_tpl.__func(target_int, c_int);

    test_int_tpl.__call(7);
    test_int_tpl.__call(_int);
    test_int_tpl.__call(c_int);

}

Compiles and works as expected.... - Its demo-code and its functionality is not the point here, its about the type and type casting as well as the possibility to formulate "universal" code for having one function name called with any given datatype, which this code has achieved.

Still, this is not the way the keyword "const" should work. - I am sure, this is an implementation error.

Maybe someone could give some insigt on how this could be explained, or any other point of view, because, maybe my interpretation is wrong.



EDIT:

The first eight calls of the copy-constructor are not required, but I cannot find a way to avoid them, anyone have an idea?

 
Dominik Christian Egert:

I have been working on unified functionality for types to be passed to a member function.

but for some reason I am not able to make it compile, has anyone an idea how to change the function signatures/template parameters so that this compiles.



I dont know why this does not work with "const int" parameter types, as it does work with const >struct< and const >class< and const >class<* <- pointer

The code you provided has a couple of issues. Firstly, the ZeroMemory function used in the constructor is not a standard C++ function. Assuming you intended to zero-initialize the value member variable, you can use value initialization instead. Secondly, the comparison operators used in the func member functions are not well-formed since the types being compared ( tmp and value ) must support the comparison operator.

#include <cstring>  // Include for memset

template <typename T>
struct __test
{
    T value;
    
    __test() : value() { }  // Value initialization to zero-initialize 'value'
    
    template <typename U>
    bool func(U* p_in[])
    {
        T tmp = (*p_in)[0];
        return (tmp > value);
    }

    template <typename U>
    bool func(const U p_in[])
    {
        T tmp = p_in[0];
        return (tmp > value);
    }

    template <typename U>
    bool func(const U& p_in)
    {
        T tmp = p_in;
        return (tmp > value);
    }

    template <typename U>
    bool func(U* p_in)
    {
        T tmp = *p_in;
        return (tmp > value);
    }
};

Here is the correct way, 

  • The constructor now uses value initialization ( : value()) to zero-initialize the value member variable.
  • The ZeroMemory function call has been removed.
  • The comparison operators have been updated assuming the types T and U support the necessary comparison operators.
  • I removed the unnecessary const qualifier in the return type of the func member functions.

Please note that this corrected code assumes that the usage and calling context of the __test struct and its member functions are appropriate for your requirements.

I hope this helps.

 
Nardus Van Staden #:

The code you provided has a couple of issues. Firstly, the ZeroMemory function used in the constructor is not a standard C++ function. Assuming you intended to zero-initialize the value member variable, you can use value initialization instead. Secondly, the comparison operators used in the func member functions are not well-formed since the types being compared ( tmp and value ) must support the comparison operator.

Here is the correct way, 

  • The constructor now uses value initialization ( : value()) to zero-initialize the value member variable.
  • The ZeroMemory function call has been removed.
  • The comparison operators have been updated assuming the types T and U support the necessary comparison operators.
  • I removed the unnecessary const qualifier in the return type of the func member functions.

Please note that this corrected code assumes that the usage and calling context of the __test struct and its member functions are appropriate for your requirements.

I hope this helps.

Well, thank you for your input.

I see what you are addressing, but that wasn't the main concern, actually.

Please take a look at the last post. You will notice, there is some issue when trying to apply your recommendations.

The goal was to have one function name and be able to call it with any of the available data types.

I will take into account what you have pointed out and try to apply that to my last posted code.

If there are corrections possible, I will update the code and repost it.

If I see this correct, the assignments you are doing will create copies of the objects. One goal is to have as little overhead as possible.
(T tmp=)

What the code is supposed to show is the faulty implementation of template expansion by the compiler. All other code, besides the function __call() is to proof correct working and type separation.

I dont think an int can be initialized with an empty value initializer. So that might not work. I will have to take a look to be sure.

EDIT: the const qualifier for return values is a habit of mine. In fact, if exporting functions from a library, this qualifier is relevant. The compiler distinguishes between functions with and functions without.

Since I come from c/c++ it's a relic that sticks with me. Using const wherever possible.

But you are right, it is not needed in this case.
 
Nardus Van Staden #:

The code you provided has a couple of issues. Firstly, the ZeroMemory function used in the constructor is not a standard C++ function. Assuming you intended to zero-initialize the value member variable, you can use value initialization instead. Secondly, the comparison operators used in the func member functions are not well-formed since the types being compared ( tmp and value ) must support the comparison operator.

Here is the correct way, 

  • The constructor now uses value initialization ( : value()) to zero-initialize the value member variable.
  • The ZeroMemory function call has been removed.
  • The comparison operators have been updated assuming the types T and U support the necessary comparison operators.
  • I removed the unnecessary const qualifier in the return type of the func member functions.

Please note that this corrected code assumes that the usage and calling context of the __test struct and its member functions are appropriate for your requirements.

I hope this helps.

I am sorry, I just noticed, your code is not MQL code.

Sorry for the confusion, but your code is incompatible to the topic of this thread.

EDIT:
Since you seem to reference c++, I need to correct you about const specifier for return value, in c++ it is very necessary to add if possible.
 
Dominik Christian Egert #:
I am sorry, I just noticed, your code is not MQL code.

Sorry for the confusion, but your code is incompatible to the topic of this thread.

EDIT:
Since you seem to reference c++, I need to correct you about const specifier for return value, in c++ it is very necessary to add if possible.

I think there is a misconception of add const "if possible". Possible to add it technically doesn't mean it should be added. I would say the recommendation should be "add it wherever possible and useful".

Herb Sutter and Andrei Alexandrescu wrote it very well in one of their book :

CPP Coding Standards - 101 Rules Guidelines and Best Practices 200" 

"Use const proactively

const is your friend: Immutable values are easier to understand, track, and reason about, so prefer constants over variables wherever it is sensible and make const your default choice when you define a value: It's safe, it's checked at compile time (see Item 14), and it's integrated with C++'s type system."

That doesn't mean add it everywhere.

But it's a bit off-topic anyway.

 
Alain Verleyen #:

I think there is a misconception of add const "if possible". Possible to add it technically doesn't mean it should be added. I would say the recommendation should be "add it wherever possible and useful".

Herb Sutter and Andrei Alexandrescu wrote it very well in one of their book :

CPP Coding Standards - 101 Rules Guidelines and Best Practices 200" 

That doesn't mean add it everywhere.

But it's a bit off-topic anyway.

Yes, that's better and actually what I meant.

Possible and useful.

I guess, I implied that, especially on return values of primitive type. 

EDIT:

Not sure if it's so far off topic, const is the main source of the problem I am trying to frame.
 
Dominik Christian Egert #:

Not sure if it's so far off topic, const is the main source of the problem I am trying to frame.

That's why I wrote "a bit", as the topic is not about how/when to use const, but rather that when you use it, it should be interpreted correctly by the compiler.

Hopefully we will come to some conclusions about it. I started to study your code...