problem with passing struct arrays

 

Hi everybody. I have a concern that I cannot find an answer to in the documentation. probably from incompetence and stupidity. Here, I have systematically filled arrays in a struct, so that it would theoretically be easier to deal with the data in the future. The only problem is that I can't pass them. I have a function that systematically collects structured data into arrays. Everything works, but it would be necessary to forward these data to the next function, etc. void function2(). Since I have very little experience with struct, I can't do it in any way. do I really have to define each arrays separately in a function or can I only pass the struct itself to the array inside which there are other arrays? How to make it easier to work with such data? I really apologize for my incompetence and I hope this is not too stupid a question. I'm really struggling with this. Here is the struct that I want to pass to another function:

void function1(){
 struct ZX_level_data
    {
    double highest;
    double lowest;
    int    members;
    string strenght;
    string scope;
    };
    
    ZX_level_data ZX_level_struct_arr[] = {};
    ArrayResize(ZX_level_struct_arr, ZX_founded_groups_number);

    for (int i = 0; i < ZX_founded_groups_number; ++i)
        {
         ZX_level_data ZX_level;
         ZX_level.highest  = ZX_neighbors_group_highest_arr[i];
         ZX_level.lowest   = ZX_neighbors_group_lowest_arr[i];
         ZX_level.members  = ZX_neighbors_group_members_arr[i];
         ZX_level.strenght = ZX_group_strength_arr[i];
         ZX_level.scope    = ZX_group_scope_attribute_arr[i]; 
         ZX_level_struct_arr[i] = ZX_level;
         }
}

I have seen that constructors are also used for struct as for classes. unfortunately, due to my limited OOP experience, I can't figure out how and whether to do it in order to make life easier in the future and make the code more systematic. I really hope that someone competent will answer and bother to help a little. I really didn't find similar examples here before, maybe in the future it will be a good lesson for others on how to behave accurately with struck arrays...

I hope you don't laugh and I wish you a nice day!
Documentation on MQL5: Array Functions / ArrayFill
Documentation on MQL5: Array Functions / ArrayFill
  • www.mql5.com
ArrayFill - Array Functions - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
titaanlabidas:

Hi everybody. I have a concern that I cannot find an answer to in the documentation. probably from incompetence and stupidity. Here, I have systematically filled arrays in a struct, so that it would theoretically be easier to deal with the data in the future. The only problem is that I can't pass them. I have a function that systematically collects structured data into arrays. Everything works, but it would be necessary to forward these data to the next function, etc. void function2(). Since I have very little experience with struct, I can't do it in any way. do I really have to define each arrays separately in a function or can I only pass the struct itself to the array inside which there are other arrays? How to make it easier to work with such data? I really apologize for my incompetence and I hope this is not too stupid a question. I'm really struggling with this. Here is the struct that I want to pass to another function:

I have seen that constructors are also used for struct as for classes. unfortunately, due to my limited OOP experience, I can't figure out how and whether to do it in order to make life easier in the future and make the code more systematic. I really hope that someone competent will answer and bother to help a little. I really didn't find similar examples here before, maybe in the future it will be a good lesson for others on how to behave accurately with struck arrays...


Semantically you are not using  struct arrays, but array of struct. The difference is, your definition would have arrays inside the struct, but you are using an array of struct type.

To solve your problem: Define the struct on global space, outside the function. Then declare the array also outside the function.

Now it is a global array, living beyond the scope of the function. This way you can access the array anywhere in your code.

 struct ZX_level_data
    {
    double highest;
    double lowest;
    int    members;
    string strenght;
    string scope;
    };
    
    ZX_level_data ZX_level_struct_arr[];

void function1(){
    ArrayResize(ZX_level_struct_arr, ZX_founded_groups_number);     for (int i = 0; i < ZX_founded_groups_number; ++i)         {          ZX_level_data ZX_level;          ZX_level.highest  = ZX_neighbors_group_highest_arr[i];          ZX_level.lowest   = ZX_neighbors_group_lowest_arr[i];          ZX_level.members  = ZX_neighbors_group_members_arr[i];          ZX_level.strenght = ZX_group_strength_arr[i];          ZX_level.scope    = ZX_group_scope_attribute_arr[i];          ZX_level_struct_arr[i] = ZX_level;          } }
 
Dominik Egert #:

Semantically you are not using  struct arrays, but array of struct. The difference is, your definition would have arrays inside the struct, but you are using an array of struct type.

To solve your problem: Define the struct on global space, outside the function. Then declare the array also outside the function.

Now it is a global array, living beyond the scope of the function. This way you can access the array anywhere in your code.

  

Thank you very much for your reply and I really appreciate it. I found your comments and tutorials on the forum when I first researched about struct. they were written at a very high level and there is no doubt that you know this subject very well. then I realized that I don't really understand this topic and I took my time and started from the very beginning to understand the possibilities of struct. I made the struct structure as simple as possible, tried all the possibilities and wrote down my own. I thought I'd share it with you too, because maybe it will save a lot of time for someone in the future who wants to start using structures in code and is in the same trouble as I was. I myself was in trouble because all the study material I could find in a hurry was either too high-end or rather very basic.

Ok let´s start with the simplest example:

struct structdata
   {
   double doubles;
   int    integers;
   string strings;
   };

function1(); 
function2(); 

void function1(){
   structdata dataset1;
   dataset1.doubles    = 11.11;
   dataset1.integers   = 11;
   dataset1.strings    = "firstfunction";
   Print(dataset1.doubles, dataset1.integers, dataset1.strings);
}

void function2(){
   structdata dataset2;
   dataset2.doubles    = 33.33;
   dataset2.integers   = 22;
   dataset2.strings    = "secondfunction";
   Print(dataset2.doubles, dataset2.integers, dataset2.strings);
}
// we can use separate function to get all information of previous functions
void printData(const structdata &data)
{
    Print("Printing data using a separate function:");
    Print(data.doubles, data.integers, data.strings);
}

if we declare a struct in global space and and we use it separately in adjacent functions. then it is indeed the easiest way is to use struct and it works naturally even if we don't write out the pass struct parameters separately. if we want to access the data with the third function, it is easily possible by passing the struct parameters to the third function.

example2:

struct structdata
   {
   double doubles;
   int    integers;
   string strings;
   };

   function1();   

void function1(){
   structdata dataset1;
   dataset1.doubles    = 11.11;
   dataset1.integers   = 22;
   dataset1.strings     = "firstfunction";
    
   function2(dataset1); // we call function 2 and pass the struct data
}

void function2(structdata &data){
   
   structdata dataset2;
   dataset2.doubles    = 33.33;
   dataset2.integers  = 44;
   dataset2.strings   = "secondfunctiondata";    
   // we print out first and second function data.
   Print(data.doubles, data.integers, data.strings);
   Print(dataset2.doubles, dataset2.integers, dataset2.strings);
   
   printData(data, dataset2);
}

void printData(const structdata &data1, const structdata &data2)
{
    // Print out data from both datasets
    Print("Printing data using a separate function:");
    Print(data1.doubles, data1.integers, data1.strings);
    Print(data2.doubles, data2.integers, data2.strings);
}

we declare a struct in global space again. in this time we call function2 inside function1 and we can easily pass function1 data to function2 by passing struct parameters. if the data needs to be accessed using a separate function then we need to pass two references, data1 and data2, which allow access from both datasets.

example3:

void function1(){
        
   // we declare struct inside the function
   struct structdata
   {
   double doubles;
   int    integers;
   string strings;
   };
   
   structdata dataset1;
   dataset1.doubles    = 11.11;
   dataset1.integers   = 22;
   dataset1.strings    = "firstfunctiondata";
   
   structdata dataset2;
   dataset2.doubles    = 33.33;
   dataset2.integers   = 44;
   dataset2.strings    = "secondfunctiondata";    
      
   Print(dataset1.doubles, dataset1.integers, dataset1.strings);
   Print(dataset2.doubles, dataset2.integers, dataset2.strings);
} 

this is the same situation where I accidentally started myself and it's even perhaps useful if you don't want to include too many global variables and the action could only contain function central renders. in this case, everything actually works inside the function until we don't want to pass out the struct data. struct calls behaves differently than normal variables and I don't know how to pass out information to the next function and is it even possible. maybe there are some clever ways, so maybe it is somehow possible to send information to the next function in this way. Someone who knows the topic very well could comment. you can also correct or add something. 

this here is just my first basic experiment about struct and i really hope that it will be helpful to someone out there who wants starting to use structures to make the code better readable and more systematic.

 
titaanlabidas #:

Ok let´s start with the simplest example:

Example 1 is not the simplest form, however, it is exactly at the level of learning about struct and usage of, that I am at. So your thread is very much appreciated. I will be following this thread for more responses from you and Dominik.

 

Here an example on how to create a serial number object.

The idea is to have a counter that increments with each instantiation.

        struct s_serial_nr
        {
            public:
            // Local storage

                const ulong serial_nr;
                ulong       copy_of_sn;


            // Constructor

                s_serial_nr() :
                    serial_nr(serial_counter++),
                    copy_of_sn(NULL)
                {};


            // Copy constructor

                s_serial_nr(const s_serial_nr& p_in)
                { operator=(p_in); }


            // Assignment operator

                s_serial_nr operator=(const s_serial_nr& p_in)              { copy_of_sn = p_in.serial_nr; return(this); };


            // Comparison operators

                const bool operator==(const s_serial_nr& p_in) const        { return(serial_nr == p_in.serial_nr); };
                const bool operator!=(const s_serial_nr& p_in) const        { return(serial_nr != p_in.serial_nr); };
                const bool operator>=(const s_serial_nr& p_in) const        { return(serial_nr >= p_in.serial_nr); };
                const bool operator<=(const s_serial_nr& p_in) const        { return(serial_nr <= p_in.serial_nr); };
                const bool operator>(const s_serial_nr& p_in)  const        { return(serial_nr >  p_in.serial_nr); };
                const bool operator<(const s_serial_nr& p_in)  const        { return(serial_nr <  p_in.serial_nr); };
        };

        static ulong serial_counter = 1;

This example uses a global variable for the counter. You could also include this counter variable into the struct as a static member. - Thats your challenge now. Show an example on how to integrate the counter as a static member variable.



_____

Next I will show the difference between a "simple struct" and a "complex struct" and the implications it brings along. - Yes, there are actually two types of struct definitions, although they are implicit included into the language, but I think it is imperative to know about that, due to code design for later use of the structures.

 
Dominik Egert #:

Here an example on how to create a serial number object.

The idea is to have a counter that increments with each instantiation.

This example uses a global variable for the counter. You could also include this counter variable into the struct as a static member. - Thats your challenge now. Show an example on how to integrate the counter as a static member variable.

_____

Next I will show the difference between a "simple struct" and a "complex struct" and the implications it brings along. - Yes, there are actually two types of struct definitions, although they are implicit included into the language, but I think it is imperative to know about that, due to code design for later use of the structures.

I really appreciate that professionals like you bother to help lower league players. At this point, in the form of this code, it is actually a code several steps more complicated than what I have been dealing with at the moment. I have less than a year of programming experience and unfortunately no more experience in OOP than a few lectures in C++. I understood that there is no way around OOP because without it, programming is a real mess when the codes get messy, and it also opens up new possibilities. Obviously, this task is too much for me to begin with, but at the same time, it is also very good that you give me a headache. I believe that in a few months I will be able to speak more than at the moment, so I would suggest something like this. sorry if this is a very stupid and non-working solution.

I would place "static ulong serial_counter" into "struct s_serial_nr" after "public:" statement. then I would declare in the global space something like that : "ulong s_serial_nr::serial_counter = 1;"

 
titaanlabidas #:
I really appreciate that professionals like you bother to help lower league players. At this point, in the form of this code, it is actually a code several steps more complicated than what I have been dealing with at the moment. I have less than a year of programming experience and unfortunately no more experience in OOP than a few lectures in C++. I understood that there is no way around OOP because without it, programming is a real mess when the codes get messy, and it also opens up new possibilities. Obviously, this task is too much for me to begin with, but at the same time, it is also very good that you give me a headache. I believe that in a few months I will be able to speak more than at the moment, so I would suggest something like this. sorry if this is a very stupid and non-working solution.

I would place "static ulong serial_counter" into "struct s_serial_nr" after "public:" statement. then I would declare in the global space something like that : "ulong s_serial_nr::serial_counter = 1;"

Thats right, exactly how to do it.

I will be busy the next days, but as I find time, I will post an example for simple and "complex" structures.

 
Dominik Egert #:

Thats right, exactly how to do it.

I will be busy the next days, but as I find time, I will post an example for simple and "complex" structures.


 well, it went well because it is clearly written in the documentation: 

Static Members

The members of a class can be declared using the storage class modifier static. These data members are shared by all instances of this class and are stored in one place. Non-static data members are created for each class object variable.

The inability to declare static members of a class would have led to the need to declare these data on the the global level of the program. It would break the relationship between the data and their class, and is not consistent with the basic paradigm of the OOP - joining data and methods for handling them in a class. The static member allows class data that are not specific to a particular instance to exist in the class scope.

Since a static class member does not depend on the particular instance, the reference to it is as follows:

class_name::variable

well, I guess I don't want to know that if it was simple, then what the complex looks like :D

Actually, I'm waiting with great interest.

happy holidays !

Documentation on MQL5: Language Basics / Variables / Static Variables
Documentation on MQL5: Language Basics / Variables / Static Variables
  • www.mql5.com
Static Variables - Variables - Language Basics - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
titaanlabidas #:
 well, it went well because it is clearly written in the documentation: 

Static Members

The members of a class can be declared using the storage class modifier static. These data members are shared by all instances of this class and are stored in one place. Non-static data members are created for each class object variable.

The inability to declare static members of a class would have led to the need to declare these data on the the global level of the program. It would break the relationship between the data and their class, and is not consistent with the basic paradigm of the OOP - joining data and methods for handling them in a class. The static member allows class data that are not specific to a particular instance to exist in the class scope.

Since a static class member does not depend on the particular instance, the reference to it is as follows:

class_name::variable

well, I guess I don't want to know that if it was simple, then what the complex looks like :D

Actually, I'm waiting with great interest.

happy holidays !

Here is an example to show the limitations of structures.


struct simple_struct
{
    // Local data
    public:
        int     data1;
        int     data2;
        string  data3;
};


struct constructor_struct : public simple_struct
{
    // Local data
    public:
        int     c_data1;
        int     c_data2;
        string  c_data3;


    // Constructor
    
        constructor_struct() :
            c_data1   (NULL),
            c_data2   (NULL),
            c_data3   (NULL)
        { };
        
};


struct derived_struct : protected constructor_struct
{
    // Additional local storage
    public:
        double  d_price1;


    // Constructor
    
        derived_struct() :
            constructor_struct(),
            d_price1  (NULL)
        { 
            d_price1 += DBL_MIN;
        };
};



void OnStart()
{

    simple_struct s_test[];
    ArrayResize(s_test, 10);
    ZeroMemory(s_test);

    constructor_struct c_test[];
    ArrayResize(c_test, 10);
    ZeroMemory(c_test);

    derived_struct d_test[];
    ArrayResize(d_test, 10);
    ZeroMemory(d_test);
        
}

This will give a compiler error:

'ZeroMemory' - not allowed for objects with protected members or inheritance

interestingly, it does work for public inheritance, I thought I remember, it was not possible a few builds ago, but I might be wrong.

 
Dominik Egert #:

Here is an example to show the limitations of structures.


This will give a compiler error:

'ZeroMemory' - not allowed for objects with protected members or inheritance

interestingly, it does work for public inheritance, I thought I remember, it was not possible a few builds ago, but I might be wrong.

thanks for a very interesting example. you are right, it will compile if you change the constructor_struct public. I can't look that deep into the "problem". if constructor_struct is needed, it would be protected, then the closest way I could compile it. I have not tested how it works, but maybe it should work in that way:
struct simple_struct
{
    // Local data
public:
    int data1;
    int data2;
    string data3;
};

// initialize simple_struct members
void InitSimpleStruct(simple_struct &s)
{
    s.data1 = NULL;
    s.data2 = NULL;
    s.data3 = NULL;
}

struct constructor_struct : public simple_struct
{
    // Local data
public:
    int c_data1;
    int c_data2;
    string c_data3;

    // Constructor
    constructor_struct()
    {
        InitSimpleStruct(this);
        c_data1 = NULL;
        c_data2 = NULL;
        c_data3 = NULL;
    }
};

struct derived_struct : protected constructor_struct
{
    // Additional local storage
public:
    double d_price1;

    // Constructor
    derived_struct()
    {
        InitDerivedStruct();
        d_price1 = DBL_MIN;
    }

    // Function to initialize derived_struct members
    void InitDerivedStruct()
    {
        InitSimpleStruct(this);
    }
};


void OnStart()
{
    simple_struct s_test[10];
    constructor_struct c_test[10];
    derived_struct d_test[10];

    for (int i = 0; i < 10; ++i)
    {
        InitSimpleStruct(s_test[i]);
        InitSimpleStruct(c_test[i]);
        c_test[i].c_data1 = NULL;
        c_test[i].c_data2 = NULL;
        c_test[i].c_data3 = NULL;

        d_test[i].InitDerivedStruct();
        d_test[i].d_price1 = DBL_MIN;
    }
}
 
titaanlabidas #:
thanks for a very interesting example. you are right, it will compile if you change the constructor_struct public. I can't look that deep into the "problem". if constructor_struct is needed, it would be protected, then the closest way I could compile it. I have not tested how it works, but maybe it should work in that way:
Actually the only problem are some functions, you cannot use, if you have protected or private inheritance models in use.

Namely, ZeroMemory, ArrayInitialize, StructToCharArray and maybe some other I don't remember right now.

I actually suggest, if you run into such a problem, either redesign the struct or add the functionality required as a member function.

Don't try to program around the problem like you tried in your last post. It will get messy, if you do so.

As a paradigm to use OOP, I suggest, design your >Data< as Objects, and design your >program logic< outside in form of procedural functions.

You will have a much easier life and better code.
Reason: