Русский Español Português
preview
From Basic to Intermediate: Struct (VII)

From Basic to Intermediate: Struct (VII)

MetaTrader 5Examples |
306 0
CODE X
CODE X

Introduction

In the previous article, From Basic to Intermediate: Struct (VI), we examined how to begin creating a universal implementation of a simple data structure. Despite seeming unusual, this type of modeling is used much more often than most people think. This is because large databases use a very similar principle to organize and search data.

I understand that this topic, covered in our articles, may seem excessive to many. However, it is worth remembering that our goal is to explain things in such a way that in the future there will be no need to clarify certain details that, in my view, are trivial. But since most of you use these articles to learn from the experience and knowledge of other programmers, everything I can convey to you at the beginning will greatly assist you in the future, dear readers.

It is not enough to simply see working code; one must understand why it works and, if necessary, know how to adapt an implementation made by another programmer to suit our needs. To do this, it is necessary to know these concepts and understand how they can be applied.

As in the previous article, we began implementing something quite interesting to better understand how structures can be used within other structures, and perhaps you were intrigued by the outcome of the last code. This happened because we used a structure created for one purpose and ended up being able to apply it to another. In this case, the data originally had a double type, but in the end we were able to use data of any type.

However, what we showed is not what we wanted to achieve. For that, we need to advance a little further. But all this effort will pay off and be rewarded in the future. I promise. So, let us continue from where we left off in the previous article.


Structures of structures, part two: the return

In the previous article, we ended with the code shown below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T>
05. struct st_Data
06. {
07.     //+----------------+
08.     private:
09.     //+----------------+
10.         struct st_Reg
11.         {
12.             T       h_value;
13.             uint    k_value;
14.         }Values[];
15.     //+----------------+
16.         string ConvertToString(T arg)
17.         {
18.             if ((typename(T) == "double") || (typename(T) == "float")) return DoubleToString(arg, 2);
19.             if (typename(T) == "string") return arg;
20. 
21.             return IntegerToString(arg);
22.         }
23.     //+----------------+
24.     public:
25.     //+----------------+
26.         bool Set(const uint &arg1[], const T &arg2[])
27.         {
28.             if (arg1.Size() != arg2.Size())
29.                 return false;
30.             
31.             ArrayResize(Values, arg1.Size());
32.             for (uint c = 0; c < arg1.Size(); c++)
33.             {
34.                 Values[c].k_value = arg1[c];
35.                 Values[c].h_value = arg2[c];
36.             }
37. 
38.             return true;
39.         }
40.     //+----------------+
41.         string Get(const uint index)
42.         {
43.             for (uint c = 0; c < Values.Size(); c++)
44.                 if (Values[c].k_value == index)
45.                     return ConvertToString(Values[c].h_value);
46. 
47.             return "-nan";
48.         }
49.     //+----------------+
50. };
51. //+------------------------------------------------------------------+
52. #define PrintX(X) Print(#X, " => ", X)
53. //+------------------------------------------------------------------+
54. void OnStart(void)
55. {
56.     const string T = "possible loss of data due to type conversion";
57.     const uint   K[] = {2, 1, 4, 0, 7, 5, 3, 6};
58. 
59.     st_Data <string> info;
60.     string H[];
61. 
62.     StringSplit(T, ' ', H);
63.     info.Set(K, H);
64.     PrintX(info.Get(3));
65. }
66. //+------------------------------------------------------------------+

Code 01

Although Code 01 works, it is somewhat annoying. This is because when we compile it, we get a response similar to the one shown below.

Figure 01

Things like this (as shown in Figure 01) cause some inconvenience. This happens because, depending on the type of implementation, compiler messages can lead to errors in the code in the future. This is not because the code is incorrect, but because the compiler may be warning us about a potential critical error. By ignoring error messages, we may end up overlooking a truly important message.

In any case, when executing Code 01, we will get the following response.

Figure 02

Interesting, isn't it? But before we continue, let's solve the problem shown in Figure 01. There are several ways to do this. However, they all ultimately lead to the same result: explicit conversion. Therefore, the way in which we achieve explicit conversion does not matter. What matters is that it takes place. To do this, simply change Code 01 to something like this:

                   .
                   .
                   .
15.     //+----------------+
16.         string ConvertToString(T arg)
17.         {
18.             if (typename(T) == "double") return DoubleToString((double)arg, 2);
19.             if (typename(T) == "float") return DoubleToString((float)arg, 2);
20.             if (typename(T) == "string") return (string)arg;
21.             return IntegerToString((long)arg);
22.         }
23.     //+----------------+
24.     public:
25.     //+----------------+
                   .
                   .
                   .

Code 02

Upon seeing that the solution to the problem in Figure 01 is the code snippet in Code 02, you are probably somewhat disappointed. Many likely expected that a full set of instructions and functions would be created, and in the end, we only need to do what is shown in this Code 02. That is part of the process. How disappointed we feel usually depends on how much we expected in the first place. Jokes aside, let us return to the code and understand how to make it more extensible so that it can handle different types of data without significant changes.

You might think this will be complicated. However, before explaining how to do this, I need you to truly understand how and why to link one element of an array to another element of another array.

The concept itself is very simple. It is merely about creating a translation. In other words, if we know the array element that will serve as the key, then the response will essentially be an element of another array. That is exactly what we will do with Code 01. And why not use a multidimensional array for this? The reason is that, although a multidimensional array would be the best option in this case, it is not suitable because we are using it as a proof of concept. In other words, this is about demonstrating the use of a specific type of language resource.

Alright, I assume that what we are doing has been explained clearly enough, and I want you to look again at Code 01 and tell me what prevents its use not only for translating values but also in other similar contexts.

Do not rush to conclusions; think about it first. Now we will look at one of the many ways to free up the code so that we can place anything into it. However, if we try to expand the code, we will notice that the problem lies in the structure declared in line 10. That is the issue, but not in the sense you might think.

The problem with the structure in line 10 is that it prevents the main structure from being used more universally. Since I understand that this is complex and takes time, we will continue with the article. But I ask you to read and reread this article and the previous ones in order to understand one thing.

Structures, like other types of constructs, are forms of representing more or less complex data. The way to solve a problem depends on its real complexity. Some tasks can be solved using discrete and simple types of data, such as floating-point representations or integer values.

When the task becomes more complex, discrete types may not be sufficient. In that case, we will need more complex types of data, such as arrays and unions. If the problem remains complex, we need an equally complex type: structures.

Thus, the structure we declared in line 10 of Code 01 is nothing more than a complex type. This must be understood in order to grasp what we are going to do.

First, we must take this structure from line 10 of the structure defined in line 04, while preserving Code 01. However, by doing so, we will create confusion in the information that will confuse you even more if you did not understand or could not follow the previous articles. This is because there is no way to implement the changes gradually, and this needs to be done in a very small number of steps. The first step is shown just below:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct st_Reg
05. {
06.     string  h_value;
07.     uint    k_value;
08. };
09. //+----------------+
10. struct st_Data
11. {
12.     //+----------------+
13.     private:
14.     //+----------------+
15.         st_Reg Values[];
16.     //+----------------+
17.     public:
18.     //+----------------+
19.         bool Set(const uint &arg1[], const string &arg2[])
20.         {
21.             if (arg1.Size() != arg2.Size())
22.                 return false;
23.             
24.             ArrayResize(Values, arg1.Size());
25.             for (uint c = 0; c < arg1.Size(); c++)
26.             {
27.                 Values[c].k_value = arg1[c];
28.                 Values[c].h_value = arg2[c];
29.             }
30. 
31.             return true;
32.         }
33.     //+----------------+
34.         string Get(const uint index)
35.         {
36.             for (uint c = 0; c < Values.Size(); c++)
37.                 if (Values[c].k_value == index)
38.                     return Values[c].h_value;
39. 
40.             return NULL;
41.         }
42.     //+----------------+
43. };
44. //+------------------------------------------------------------------+
45. #define PrintX(X) Print(#X, " => ", X)
46. //+------------------------------------------------------------------+
47. void OnStart(void)
48. {
49.     const string T = "possible loss of data due to type conversion";
50.     const uint   K[] = {2, 1, 4, 0, 7, 5, 3, 6};
51. 
52.     st_Data info;
53.     string H[];
54. 
55.     StringSplit(T, ' ', H);
56.     info.Set(K, H);
57.     PrintX(info.Get(3));
58. }
59. //+------------------------------------------------------------------+

Code 03

Warning: to make the code (and what we are going to do) as simple and clear as possible, we will take a step back and use the string type. Please note that the generalized version of the structure we were building no longer exists, and the execution result still corresponds to Image 02.

Perhaps if you look at Code 03, you might think: «Okay, you said this was complicated, but so far it's still simple. What's the complexity?» Well, then let us proceed. The second point that needs to be changed is that the Set and Get functions (which you see in lines 19 and 34, respectively) ARE NOT functions of st_Data; they are functions of st_Reg.

Now I ask: what are they doing in st_Data? The answer: They make it difficult for the structure to function in a more universal mode, which prevents it from handling data of any type. Therefore, we need to remove these functions from the st_Data structure as it appears in Code 03. For greater simplicity, let us look at what the corrected snippet will look like. It is shown below.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct st_Reg
05. {
06.     //+----------------+
07.     private:
08.     //+----------------+
09.         string  h_value;
10.         uint    k_value;
11.     //+----------------+
12.     public:
13.     //+----------------+
14.         void Set(const uint arg1, const string arg2)
15.         {
16.             k_value = arg1;
17.             h_value = arg2;
18.         }
19.     //+----------------+
20.         uint Get_K(void)    { return k_value; }
21.     //+----------------+
22.         string Get_H(void)  { return h_value; }
23.     //+----------------+
24. };
25. //+----------------+
26. struct st_Data
27. {
28.     //+----------------+
29.     private:
30.     //+----------------+
31.         st_Reg Values[];
32.     //+----------------+
33.     public:
34.     //+----------------+
35.         bool Set(const st_Reg &arg)
36.         {
37.             if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE)
38.                 return false;
39. 
40.             Values[Values.Size() - 1] = arg;
41. 
42.             return true;
43.         }
44.     //+----------------+
45.         st_Reg Get(const uint index)
46.         {
47.             for (uint c = 0; c < Values.Size(); c++)
48.                 if (Values[c].Get_K() == index)
49.                     return Values[c];
50. 
51.             return Values[0];
52.         }
53.     //+----------------+
54. };
55. //+------------------------------------------------------------------+
                   .
                   .
                   .

Code 04

«What madness is this?» Calm down, this is only the beginning. Now we have not one but two structures for combining data into the new st_Data structure. But before moving on to the main code (which will be in OnStart), we need to understand what is happening here.

In fact, although this code snippet works the same way as Code 03, there are several aspects here that are completely different from what we are used to. Nevertheless, in my view, this is still beginner-level code, since we are dealing with very simple things.

To begin with, it is important to remember that st_Reg is a structure designed to work with structural code. In other words, we will not directly interact with variables, but we will create a context in which we can manage them.

Essentially, st_Reg works the same way as it did before we started the topic of nested structures. Now begins the part that complicates things for many beginners, especially if they are unclear on the basic concepts of what a variable is and how to work with data types. Let us understand what is happening in the second structure, called st_Data. Since st_Reg is a complex data type, in line 31 we can declare a variable, which in this case is an array, to store data of that type—that is, st_Reg. So far, you probably do not see any problems, do you?

However, if we look at line 35, we see that things start to lose meaning, especially if we pay attention to line 37. My goodness, what is happening there?! In this line, we are simply trying to allocate memory space, dear readers. However, there is a small detail that complicates things a bit. The fact is that in MQL5, WE CANNOT RETURN POINTERS. Therefore, in the function shown in line 45, there is a small drawback that we are trying to fix in line 37.

So, pay attention. If we try to find a value inserted into the st_Data structure and DO NOT FIND IT, we have to return invalid data. In languages like C and C++, we do this by typically returning a null value. However, here in MQL5, WE CANNOT DO THAT. We need a little trick. Therefore, when in line 37 we attempt to allocate the first block to add data of type st_Reg, we allocate two memory cells. The first will be empty. However, the second will be assigned the value corresponding to line 40.

Since this is the first element, two positions will be allocated to it. Starting from the next call, line 37 will determine that at least two elements have already been allocated, and will allocate only one more. Thus, this will happen with each new call to the Set function of the st_Data structure. Please note that this has nothing to do with the Set procedure of the st_Reg structure, and there is a reason for this, which will be discussed later.

So, since the Get function of the st_Data structure is directly related to the explanation of the Set structure, we only need to understand that the value received as an argument by the Get function does not indicate which index in the Values array is being searched for, but rather which value is physically stored in that specific element. Therefore, we need a loop to search for a specific value in the st_Reg structure. We will discuss this issue later. In general, that's it.

Now we can review the main code. If you recall, the code we saw in the snippet from Code 04 could very well be placed in a header file, and it would work without issues. But before showing how to do this in order to generalize the code, we need to look at the rest of it. It is shown below. It is shown below.

                   .
                   .
                   .
55. //+------------------------------------------------------------------+
56. #define PrintX(X) Print(#X, " => [", X, "]")
57. //+------------------------------------------------------------------+
58. void OnStart(void)
59. {
60.     const string T = "possible loss of data due to type conversion";
61.     const uint   K[] = {2, 1, 4, 0, 7, 5, 3, 6};
62. 
63.     st_Data info;
64.     string H[];
65. 
66.     StringSplit(T, ' ', H);
67.     for (uint c = 0; c < H.Size(); c++)
68.     {
69.         st_Reg  reg;
70. 
71.         reg.Set(K[c], H[c]);
72.         info.Set(reg);
73.     }
74. 
75.     PrintX(info.Get(3).Get_H());
76.     PrintX(info.Get(13).Get_H());
77. }
78. //+------------------------------------------------------------------+

Code 05

And so begins the most fun and exciting part. Since you have probably already understood most of this code, as it was shown earlier, let's move on to the new part. This specifically concerns the loop in line 67, as well as lines 75 and 76.

Let's start with the loop. Since each element to be placed into the st_Data structure is of type st_Reg, we need line 71 to create an st_Reg structure, so that we can then store an element of type st_Reg in the st_Data structure using line 72. Now the question: why do we not do this directly in the st_Data structure? The answer is that if the recording of elements of type st_Reg were done directly in the st_Data structure, we would not be able to extend this structure later. But do not worry, you will soon understand it.

After all the elements have been written into the st_Data structure, we can use lines 75 and 76 to test this structure. To do this, in line 75 we search for a string whose index is three. And, looking at lines 60 and 61, you can see what value this index holds. In line 76, in turn, a search will be performed for an element whose index is 13. Since this index is not defined in line 61, the returned value will be the one defined in line 51 of Code 04.

With this preliminary knowledge, we can run the code. Thus, we will see the following result:

Figure 03

In other words, it works perfectly. Now we can generalize a significant part of the code, but to do so we will move on to a new topic. This way, we can calmly study what we have already done, and then try to understand what still needs to be done to make the generalizations.


Structures of structures, part three, revenge

If you found the information in the previous topic confusing, get ready, because things are about to get even more interesting. Here, things truly become more complex. So pay attention to the following fact. In Code 04 from the previous topic, the st_Data structure can only accept one data type, namely the st_Reg type, but this does not make it universal. This restricts the st_Data structure to only what is defined in the st_Reg structure, even if they are not related to each other.

However, if we slightly modify the code of the st_Data structure, we can free up its resources so that it can work with different data types. Note what I just said. When the st_Data structure becomes a template, it will not be completely free from the st_Reg structure, which is related to the Get function that will remain tied to the st_Reg type. Nevertheless, it is worth the effort.

As indicated above, all we need to do is transform the st_Data structure into a template and simultaneously adjust the main code to match this template, which the compiler will have to generate. This is done with the following code.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct st_Reg
05. {
06.     //+----------------+
07.     private:
08.     //+----------------+
09.         string  h_value;
10.         uint    k_value;
11.     //+----------------+
12.     public:
13.     //+----------------+
14.         void Set(const uint arg1, const string arg2)
15.         {
16.             k_value = arg1;
17.             h_value = arg2;
18.         }
19.     //+----------------+
20.         uint Get_K(void)    { return k_value; }
21.     //+----------------+
22.         string Get_H(void)  { return h_value; }
23.     //+----------------+
24. };
25. //+----------------+
26. template <typename T>
27. struct st_Data
28. {
29.     //+----------------+
30.     private:
31.     //+----------------+
32.         T Values[];
33.     //+----------------+
34.     public:
35.     //+----------------+
36.         bool Set(const T &arg)
37.         {
38.             if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE)
39.                 return false;
40. 
41.             Values[Values.Size() - 1] = arg;
42. 
43.             return true;
44.         }
45.     //+----------------+
46.         T Get(const uint index)
47.         {
48.             for (uint c = 0; c < Values.Size(); c++)
49.                 if (Values[c].Get_K() == index)
50.                     return Values[c];
51. 
52.             return Values[0];
53.         }
54.     //+----------------+
55. };
56. //+------------------------------------------------------------------+
57. #define PrintX(X) Print(#X, " => [", X, "]")
58. //+------------------------------------------------------------------+
59. void OnStart(void)
60. {
61.     const string T = "possible loss of data due to type conversion";
62.     const uint   K[] = {2, 1, 4, 0, 7, 5, 3, 6};
63. 
64.     st_Data <st_Reg> Info_1;
65.     string H[];
66. 
67.     StringSplit(T, ' ', H);
68.     for (uint c = 0; c < H.Size(); c++)
69.     {
70.         st_Reg  reg;
71. 
72.         reg.Set(K[c], H[c]);
73.         Info_1.Set(reg);
74.     }
75. 
76.     PrintX(Info_1.Get(3).Get_H());
77.     PrintX(Info_1.Get(13).Get_H());
78. }
79. //+------------------------------------------------------------------+

Code 06

Theoretically, Code 06 can contain any type of data structure or any data type inside the st_Data structure. In theory this is true. The reason lies precisely in the Get function, located in line 46 of Code 06. To make the code more universal, we only needed to add line 26 and replace the references to st_Reg in lines 32, 36, and 46 with the type T. After that, we needed to modify the declaration in line 64 so that the compiler could generate the corresponding structure.

The result of executing Code 06 will be the same as when executing Code 05, that is, figure 03. However, we are still, in a sense, tied to the st_Reg structure, although this binding (which is also a problem) can also bring some benefits. It all depends on the needs, level of creativity, and level of knowledge of each reader on certain topics. Note the following. The only remaining connection between the st_Data and st_Reg structures is the so-called Get_K function, located in line 49. This is the only link that still remains. Since the st_Reg structure can also be overloaded, in this case we can use different types.

But since such things can give rise to complex details that are difficult to understand in the final part of the article, we will not discuss them now.

However, even so, we can do something that I find interesting, and it can also be explained in the remaining part of the article. It concerns exactly line 49 of Code 06. Now pay attention, dear readers: in the article From Beginner to Intermediate Level: Overloading, we talked about a very simple and interesting way to use function and procedure overloading. In other articles of this series, we also showed that this can be done using names identical to those contained in the standard library.

Now here is the interesting part: what if in Code 06 we create a way to overload the Get_K function shown in line 49? What possibilities would this open up? As we have already said, this is where creativity and the application of concepts make things very interesting. This is because, if you look closely, you will notice that the st_Reg structure (shown in Code 06) contains only two variables. But we can overload the st_Reg structure to obtain another structure with more variables or even with completely different data types inside the structure.

However, by doing this, we would not necessarily have to delete the original st_Reg structure or modify the st_Data structure already created in Code 06. Since I am not sure that you understand what I mean, let us look at a code snippet where we can see what I am trying to show. This code is below.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. struct st_Reg
005. {
006.     //+----------------+
007.     private:
008.     //+----------------+
009.         string  h_value;
010.         uint    k_value;
011.     //+----------------+
012.     public:
013.     //+----------------+
014.         void Set(const uint arg1, const string arg2)
015.         {
016.             k_value = arg1;
017.             h_value = arg2;
018.         }
019.     //+----------------+
020.         uint Get_K(void)    { return k_value; }
021.     //+----------------+
022.         string Get_H(void)  { return h_value; }
023.     //+----------------+
024. };
025. //+------------------------------------------------------------------+
026. struct st_Bio
027. {
028.     //+----------------+
029.     private:
030.     //+----------------+
031.         string  h_value;
032.         string  b_value;
033.         uint    k_value;
034.     //+----------------+
035.     public:
036.     //+----------------+
037.         void Set(const uint arg1, const string arg2, const string arg3)
038.         {
039.             k_value = arg1;
040.             h_value = arg2;
041.             b_value = arg3;
042.         }
043.     //+----------------+
044.         uint Get_K(void)    { return k_value; }
045.     //+----------------+
046.         bool Get_Bio(string &arg1, string &arg2)
047.         {
048.             arg1 = h_value;
049.             arg2 = b_value;
050. 
051.             return true;
052.         }
053.     //+----------------+
054. };
055. //+------------------------------------------------------------------+
056. template <typename T>
057. struct st_Data
058. {
059.     //+----------------+
060.     private:
061.     //+----------------+
062.         T Values[];
063.     //+----------------+
064.     public:
065.     //+----------------+
066.         bool Set(const T &arg)
067.         {
068.             if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE)
069.                 return false;
070. 
071.             Values[Values.Size() - 1] = arg;
072. 
073.             return true;
074.         }
075.     //+----------------+
076.         T Get(const uint index)
077.         {
078.             for (uint c = 0; c < Values.Size(); c++)
079.                 if (Values[c].Get_K() == index)
080.                     return Values[c];
081. 
082.             return Values[0];
083.         }
084.     //+----------------+
085. };
086. //+------------------------------------------------------------------+
087. #define PrintX(X) Print(#X, " => [", X, "]")
088. //+------------------------------------------------------------------+
089. void CheckBio(st_Data <st_Bio> &arg)
090. {
091.     string sz[2];
092. 
093.     Print("Checking data in the structure...");
094.     for (uint i = 7; i < 11; i += 3)
095.     {
096.         Print("Index: ", i, " Result: ");
097.         if (arg.Get(i).Get_Bio(sz[0], sz[1]))
098.             ArrayPrint(sz);
099.         else
100.             Print("Failed.");
101.     }
102. }
103. //+------------------------------------------------------------------+
104. void OnStart(void)
105. {
106.     const string T = "possible loss of data due to type conversion";
107.     const string M[] = {"2", "cool", "4", "zero", "mad", "five", "what", "xoxo"};
108.     const uint   K[] = {2, 1, 4, 0, 7, 5, 3, 6};
109. 
110.     st_Data <st_Reg> Info_1;
111.     st_Data <st_Bio> Info_2;
112. 
113.     string  H[];
114. 
115.     StringSplit(T, ' ', H);
116.     for (uint c = 0; c < H.Size(); c++)
117.     {
118.         st_Reg  reg;
119.         st_Bio  bio;
120. 
121.         reg.Set(K[c], H[c]);
122.         bio.Set(K[c], M[c], H[c]);
123. 
124.         Info_1.Set(reg);
125.         Info_2.Set(bio);
126.     }
127. 
128.     PrintX(Info_1.Get(3).Get_H());
129.     PrintX(Info_1.Get(13).Get_H());
130.     CheckBio(Info_2);
131. }
132. //+------------------------------------------------------------------+

Code 07

When executing Code 07, an image very similar to this will appear in the terminal.

Figure 05

Oh my God! What a wild and crazy creation we have made! Calm down, my dear readers, what interests us here is precisely the information highlighted in Image 05, which was created by the procedure in line 89, on which we will focus our explanation, since most of the code is easy to understand. Understanding this procedure in line 89 is crucial to grasping what will be covered in the next article.

But before explaining what is happening in the procedure in line 89, we need to understand a few additional points. Let us start with line 26, where a new structure is created. It should be noted that it has a similar foundation to the st_Reg structure, but the similarity ends there, as the st_Bio structure contains more variables and a different set of operations within its context. What matters to us is the function in line 44. This is because it performs the same function as line 20, since it has the same goals and objectives. And this is important for what we will do next.

Please also note that the st_Data structure (from Code 07) is exactly the same as the st_Reg structure in Code 06. The only real change is the presence of the st_Bio structure. After this, we can move on to the main OnStart procedure, which is the entry point of our code. Notice that we have added some additional elements here that were not present in Code 06. These examples are precisely intended to show how flexible the st_Data structure can become if the implementation phase of the code is carefully thought out.

Essentially, we added lines 107 and 111. Now, please concentrate. Notice that the difference between lines 110 and 111 is precisely the type of data that will be placed into the st_Data structure. Therefore, inside the loop in line 116, we add a new variable in line 119, which will receive values in line 122, and in line 125 we assign or add a new piece of data to the structure. In this case, the data type will differ from the data type in line 124. So far, everything has happened as predicted in Code 06.

However, this is where the complexities begin, and at the same time, the more interesting parts: we have line 130, which will lead to the execution of the procedure in line 89. Now let us move on to the most interesting part. Please note that we are inside the loop in line 94, and what interests us most is line 97, which we should compare with line 128. What is the difference between them? Practically none, except that, since line 76 returns a structural type referring to the data type of the variable declared in line 62, there are no differences between the two lines.

In the article «From Basic to Intermediate: Variables (III)», we explained that a function is a special type of variable. Here, this concept is applied in a very interesting way, because based on the response we receive from the function in line 76, we can point to one or another data type, in this case, to a structure. Without the use of structural programming, it would be practically impossible (or extremely difficult) to create what is being done here. The point is that in line 97, a reference to the st_Bio structure is used. In line 128, we point to the st_Reg structure, and since both structures are inside the st_Data structure, we can process different types of information or datasets in the same way.

To understand what is really happening here, you need to practice and experiment. You will not be able to understand by simply looking at the code with little programming experience. And remember: we are still working with material that, in my view, is at a beginner level.


Final thoughts

In today's article, we showed how to approach solving problems by structuring them and creating a simpler and more appealing solution. Although the material presented here is educational and, therefore, not production code, it is essential to thoroughly grasp the concepts and knowledge presented here. Thus, in the future, you will be able to follow the code we will show, because as we progress, we will be able to reveal deeper and more interesting details of programming, which would be impossible if we had to explain every line of code.

Therefore, take this opportunity to carefully study the material from the index, without rushing. Study and apply this knowledge in practice, because in the next article we will continue from where we left off today. We have not yet finished discussing structural programming and how we can take advantage of it before moving on to object-oriented programming.

Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/15912

Attached files |
Anexo.zip (3.41 KB)
Trend Criteria. Conclusion Trend Criteria. Conclusion
In this article, we will consider the specifics of applying some trend criteria in practice. We will also try to develop several new criteria. The focus will be on the efficiency of applying these criteria to market data analysis and trading.
Market Simulation (Part 17): Sockets (XI) Market Simulation (Part 17): Sockets (XI)
The implementation of the part of the code that will run in MetaTrader 5 does not present any difficulty. However, there are several points that need to be taken into account. This is necessary so that you can make the system work. Remember one important thing: not just one program will be running. In reality, we will have to run three programs simultaneously. It is important to implement and structure each of them in such a way that they can interact and communicate with one another, and that each of them understands what the others are trying or intending to do.
Neuro-Structural Trading Engine — NSTE (Part II): Jardine's Gate Six-Gate Quantum Filter Neuro-Structural Trading Engine — NSTE (Part II): Jardine's Gate Six-Gate Quantum Filter
This article introduces Jardine's Gate, a six-gate orthogonal signal filter for MetaTrader 5 that validates LSTM predictions across entropy, expert interference, confidence, regime-adjusted probability, trend direction, and consecutive-loss kill switch dimensions. Out of 43,200 raw signals per month, only 127 pass all six gates. Readers get the complete QuantumEdgeFilter MQL5 class, threshold calibration logic, and gate performance analytics.
From Novice to Expert: Adaptive Risk Management for Liquidity Strategies From Novice to Expert: Adaptive Risk Management for Liquidity Strategies
In this article, we explore practical and robust risk management techniques specifically tailored for liquidity-based trading. You will learn how to protect positions during retests, handle false breakouts with confidence, and identify signs of potential level manipulation. By the end, you will have built an adaptive Expert Advisor capable of managing zone flips and executing strategic pending orders with integrated risk control.