
From Basic to Intermediate: Overload
Introduction
In the previous article named “From Basic to Intermediate: Floating point“ we discussed fundamentals of working with floating-point numbers. I first presented this material as a starting point, because it is extremely important. In my opinion, understanding how float and double types work is absolutely necessary in order to further cope up with more complex issues.
Although the content of the article covers only a basic part of what everyone really needs to master if they want to become a good programmer, this is already quite enough to move on to other topics. However, it is quite possible that at some point we will have to return to floating-point numbers, but at a more advanced level. However, everything has its own time.
Nevertheless, this article, in my opinion, has already helped many people understand that when we are dealing with a program, or rather, with a trading platform, where a mistake means losing money, we cannot strive for absolute accuracy in our calculations. This is due to the fact that the very nature of floating-point numbers does not allow us to be absolutely accurate and precise. All we can do is try to get as close as possible to the value that we consider acceptable or reasonable, so that we are able to make a decision on the purchase or sale of a particular asset or financial instrument.
However, these problems relate more to operators or users of the platform than to our main activity, which in this case is programming. As programmers, we must warn users of our applications that there is a certain risk in calculations. And further, the user or trader himself must independently adjust the data and analyze how much they make sense to him at the time of making a decision to make a purchase or sale.
However, I still do not see that we have a solid enough and well-built base so that we can start talking about how to program or implement an indicator or a trading expert advisor, also called a robot. Although we already have a good idea of what is necessary and what can be done, in my opinion, at certain moments we may still find ourselves with our hands tied. That is because there are things that still have not been explained. And they are really necessary in different situations and for different purposes. They often allow us to do the things that would be impossible to implement without appropriate knowledge.
And since I am not happy about the prospect of going into too tedious and cumbersome details in future articles, I want to create a complete and truly solid base for presenting more advanced materials. This will not only make the articles more interesting, but also help improve the quality of the content provided. After all, without wasting time explaining such details, I will be able to focus on transferring even more volume of knowledge in future materials.
There are still a few things that need to be explained, and which can be quite difficult for many beginners. And it is really necessary to spend time on them before we move to a higher level.
Just as it was done with respect of arrays and strings, where I showed that one leads to the other, one mechanism also needs to be clarified here before moving on to something more complex. The reason is simple: it will be much clearer how one seemingly confusing mechanism allows implementing another one, which many beginners do not use simply because they do not understand it. And they are forced to create a number of routines, functions and procedures that are often completely unnecessary in a more sophisticated code. Or rather, in code written by a programmer with deeper knowledge of how to implement something in a particular language. In our case — in MQL5.
So, to separate one from the other, let us proceed to a new topic.
What is overload?
If there is something that can completely confuse novice programmers or even amateur programmers and make it impossible to understand at least something in a code snippet, it is when there are two functions or procedures with the same name in the same code. Yes, this does happen, and when it does, a less experienced or less savvy programmer gets completely disoriented, unable to fix the code, improve it, or even create their own based on it.
This kind of thing, despite the fact that it may seem extremely confusing and completely devoid of logic to unaware people, is quite possible, admissible and acceptable to the compiler, provided certain simple rules are followed.
I understand and know that many people will probably say that I have lost my mind or completely lost the thread of the narrative, because they simply have no idea how to use two calls with different purposes, but at the same time containing the same name. Let us take a simple example — really very simple — just to show what I am talking about and what I am going to explain.
Now, let us look at the code below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(Sum(10, 25)); 07. Print(Sum(-10, 25.)); 08. } 09. //+------------------------------------------------------------------+ 10. ulong Sum(ulong arg1, ulong arg2) 11. { 12. Print(__FUNCTION__, "::", __LINE__); 13. return arg1 + arg2; 14. } 15. //+------------------------------------------------------------------+ 16. double Sum(double arg1, double arg2) 17. { 18. Print(__FUNCTION__, "::", __LINE__); 19. return arg1 + arg2; 20. } 21. //+------------------------------------------------------------------+
Code 01
So, my dear reader, I want you to look at code 01 and say as honestly as possible, even before you see the result: how exactly will this code run? Or, to make it clearer, you should already know that when we use the Print call in the code, we want to output some value on MetaTrader 5 terminal. And here we have four calls of Print library procedure.
So, to rephrase the question asked above: can you tell me what will be displayed on the terminal? Obviously, you can look at lines six and seven and say, "Well, value 35 and value 15 will be printed”. And this is the most obvious part of the operation. I'm interested in which of the two functions will be executed: Sum function in line ten or Sum function in line sixteen? Hm, let me think. Oops, but it will not compile, because we have two functions with the same name — in both line ten and line sixteen. Did you think to catch me on this?
Well, my dear reader, actually, and you're absolutely right, there really could be a catch here, since two functions or procedures CANNOT HAVE THE SAME NAME. This is a fact, and anyone who claims otherwise is lying or at least hiding something. However, this does not apply to this case, no matter how strange it may sound. The two Sum functions shown in lines ten and sixteen are NOT THE SAME FUNCTION. Despite the fact that they both do the same job and do it correctly, the compiler treats them not as one, but as two different functions, even though they have the same name.
However, now I'm really confused, because as far as I know, two functions or procedures cannot have the same name when we're going to use them in the same code. And this is true — they cannot. But here we are doing what is known as overload of functions or procedures.
The problem of overloading is one of the things that bring me the most pleasure when programming. Because if overloading is planned correctly, it allows us to implement much more readable code. Although there are more effective ways to achieve the same thing, without understanding what the overload you see in code 01 is, it is almost — if not impossible — to understand another mechanism present in the MQL5 language. But let us leave that for later. First, let us figure out what overload is and how it works.
Overloading is exactly what you see in lines ten and sixteen of this code 01. Once you complete it, what is shown in the image below will be generated.
Figure 01
We are interested here not in the value that will be printed in lines six and seven, but in the values that are highlighted in the image. This is the data that is really of interest to us at the moment. Note that in one case we are referring to line 12, and in the other case we are referring to line 18. Why so?
The reason is in the type of value being passed in the calls in lines six and seven. When the compiler tries to generate an executable file, it will look at these values and say: OK, this value corresponds to the expected parameter of this function; that value corresponds to that other parameter. And so it will permit the calls in such a way that we will have multiple calls with the same name.
However, and this is important, the arguments or expected parameters MAY NOT BE THE SAME. They may have some similarities, but they must differ in number, or at least one of them must be of a different type. Otherwise, the compiler will not be able to understand where the code should be directed, and this will be considered an error.
Note that in this case, both the Sum in line ten and the Sum in line sixteen use different types. Since the second argument in line seven differs from an integer in type, the compiler understands that in this case it is necessary to call a function in which a floating-point argument is expected.
To consolidate this knowledge, let us look at another case.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(Sum(10, 25)); 07. Print(Sum((char)-10, 25)); 08. } 09. //+------------------------------------------------------------------+ 10. ulong Sum(int arg1, ulong arg2) 11. { 12. Print(__FUNCTION__, "::", __LINE__); 13. return arg1 + arg2; 14. } 15. //+------------------------------------------------------------------+ 16. double Sum(char arg1, ulong arg2) 17. { 18. Print(__FUNCTION__, "::", __LINE__); 19. return (double)(arg1 + arg2); 20. } 21. //+------------------------------------------------------------------+
Code 02
Now be careful, because code 02 gives the same result as shown in image 01. However, here we are dealing with something not as simple as before. This is the type of code that many not very experienced programmers find too confusing, so they try to change it to eliminate the resulting confusion. However, by doing so, they end up creating a problem for themselves, because the result is not what was expected, or as the author of the code intended.
For this reason, before trying to change an unknown code, my dear reader, try to study how it works. If possible, run it in a controlled environment so that you can understand the progress. It is possible that you are missing something, and trying to change something without even realizing it is one of the biggest mistakes beginning programmers make.
Now let us figure this out: Sum function as presented in lines ten and sixteen of code 02 still returns the same type of value. Note that we use an explicit conversion in line 19 so that the compiler does not complain about type mismatches.
But now the following point is important: note that the difference between Sum function in line ten and Sum in line sixteen lies in the type of the first argument they take. Usually, this confuses many people, because at first glance it seems pointless to use a 4-byte type for a function from line ten and a 1-byte type for a function from line sixteen.
In fact, this is completely unnecessary in this example. However, for some reason a programmer may want to work inside functions in such a way that the function in line sixteen really should take a value of char type, since int type may not be suitable. Let me remind you that we can solve this problem in another way, but it will be discussed in another article. Here we assume that you do not know how to do this, so you decide to implement it as shown in code 02.
Question: is it bad? No, my dear reader, creating, writing, and implementing code as shown in code 02 is not a bad thing. It just means that you should learn a little more about how to program in MQL5. But definitely, it is not bad.
So, these were simple forms of overload. There is another form where instead of declaring parameters of different types, we use a different number of parameters. This case is probably the most common, because unlike C and C++, where we can implement an almost infinite number of parameters without changing the declaration of a function or procedure, here in MQL5 we cannot do this. Well, this is in test mode, because there is a way to implement it. I showed how in the previous article, although this was just an introduction to what can actually be done when certain mechanisms are explained.
But let us take into account again that you, my dear reader, are really studying and want to study properly. So what would this other form of overload look like? You can see it just below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(Sum(10, 25)); 07. Print(Sum(-10, 25, 4)); 08. } 09. //+------------------------------------------------------------------+ 10. long Sum(long arg1, long arg2) 11. { 12. Print(__FUNCTION__, "::", __LINE__); 13. return arg1 + arg2; 14. } 15. //+------------------------------------------------------------------+ 16. long Sum(long arg1, long arg2, long arg3) 17. { 18. Print(__FUNCTION__, "::", __LINE__); 19. return arg1 + arg2 + arg3; 20. } 21. //+------------------------------------------------------------------+
Code 03
In this case, the result will be as shown in the following image.
Figure 02
As far as I have noticed, most beginners find this type of code easier to understand than others, as in example 03, because it makes it easier to understand which function or procedure will be called in each specific case. Many people do not consider this type of implementation (which is presented in code 03) as a form of overloading. However, in the literature, many authors really consider this as a kind of overload, since the function name remains the same.
Okay, I think it is now clear what is overload and what is not. However, one question remains: why does this work, and how does the compiler manage to distinguish between these two implementations, just on the basis that different types are used? It does not seem very logical, at least at first glance.
Actually, my dear reader, this is not entirely logical, because when we use a function, we call it using the name under which it was declared, and not under some other name. So why does it work? The reason is that in order for the code to actually become executable, the compiler should understand how to properly direct the execution flow. And when overloading is used, in this case of functions and procedures, the compiler creates a unique internal name. I say "internal" because you do not know exactly how it will be formed, although, in general, it is done using some simple rules.
For example: looking at code 03, in line ten, the compiler can call this Sum function as Sum_long_long; while it can call the function from line sixteen Sum_long_long_long. Please note that despite the apparent simplicity, this already makes a big difference, as it is very similar to creating a function with the unique name.
The same applies to code 02. There, the compiler can interpret the function from line ten as Sum_int_ulong, and that from line sixteen as Sum_char_ulong. Please note that now everything is starting to make sense, and from this the fact follows that the compiler is able to understand where the execution flow should be directed at any given moment.
Important note: I use this nomenclature here only for didactic purposes, because depending on the implementation and what the compiler developers intended, this nomenclature may be completely different. Just keep in mind that internally the compiler does something similar.
Great, we are almost done with the topic of using the overload. There are only two examples missing, which, in my opinion, are very interesting to show. The first one is shown below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int v = -5; 07. 08. Sum(v, Sum(10, 25)); 09. Print(v); 10. } 11. //+------------------------------------------------------------------+ 12. long Sum(long arg1, long arg2) 13. { 14. Print(__FUNCTION__, "::", __LINE__); 15. return arg1 + arg2; 16. } 17. //+------------------------------------------------------------------+ 18. void Sum(int &arg1, long arg2) 19. { 20. Print(__FUNCTION__, "::", __LINE__); 21. 22. arg1 += (int)arg2; 23. } 24. //+------------------------------------------------------------------+
Code 04
In this case, we have the option to use the overloaded function along with the procedure. Note that they have the same names, but due to the difference in types, you can redirect the execution flow in one direction or another. It is important to note that depending on the way this 04 code is implemented, what types of arguments were used, and how the code itself was structured, it may give one result or another. And all because we have changed some little thing in declarations: whether it is the type or the way line eight is interpreted.
Note the following fact: although line eight appears to refer to the function provided in line 12, the very fact of using a variable as the first argument makes it clear that we intend to invoke line 18. However, if the type declared in line eight is incompatible with that expected in line 18, we can, in fact, never call line 18, since all actions will be performed in line 12. For this reason, it is not recommended to use overloading of functions and procedures improperly or without due care, as this may lead to results that are very different from those expected.
Nevertheless, in the form in which code 04 is presented, when being executed, we will see the result shown in the following image.
Figure 03
Again, it is worth noting that this result arose solely because the types perfectly matched, and the compiler was able to correctly understand our intention. However, if some programmer, without studying and understanding the code, decides that he does not want to use long type in his code (since it is 64-bit) and decides to use only int type (since it is 32-bit) - see what happens. After making the described changes, code 04 will turn into code 05, which can be seen below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int v = -5; 07. 08. Sum(v, Sum(10, 25)); 09. Print(v); 10. } 11. //+------------------------------------------------------------------+ 12. int Sum(int arg1, int arg2) 13. { 14. Print(__FUNCTION__, "::", __LINE__); 15. return arg1 + arg2; 16. } 17. //+------------------------------------------------------------------+ 18. void Sum(int &arg1, int arg2) 19. { 20. Print(__FUNCTION__, "::", __LINE__); 21. 22. arg1 += (int)arg2; 23. } 24. //+------------------------------------------------------------------+
Code 05
Please note that a completely innocent change was made with no particular intention of causing problems, but when trying to create an executable file, it turns out that the compiler does not understand what exactly is being implemented. The message printed in the compiler output is shown just below.
Figure 04
That is, a completely innocent attempt to change the code to something more suitable, in the programmer's opinion, eventually resulted in the code becoming completely incomprehensible, since the compiler does not know whether to address to line 12 or line 18 when line eight should be executed.
However, my dear reader, I want you to understand one thing here: the error is not in line 8. The mistake actually lies in the fact that we have two calls that are exactly the same in compiler's understanding. A programmer with little experience will quickly think that the problem is in line eight, although in fact it is in line 12 or 18, as explained above, since the compiler can create a unique internal name trying to generate the final executable file.
This approach, which may seem simple to solve here, can be extremely difficult in practice. Because one of the calls may be in one header file, and the other may be in a completely different header file that has nothing to do with the first one at all. This makes the situation even more complicated and confusing.
And as a final example, let us consider another use case for overloading. It is shown in the code below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. long v = -16, 07. m[]; 08. bool b; 09. 10. Add(m, Sum(10, 25)); 11. Print("Return: ", b = Sum(v, m)); 12. if (b) Print("Value: ", v); 13. ArrayFree(m); 14. } 15. //+------------------------------------------------------------------+ 16. void Add(long &arg[], long value) 17. { 18. ArrayResize(arg, arg.Size() + 1); 19. arg[arg.Size() - 1] = value; 20. } 21. //+------------------------------------------------------------------+ 22. long Sum(long arg1, long arg2) 23. { 24. Print(__FUNCTION__, "::", __LINE__); 25. return arg1 + arg2; 26. } 27. //+------------------------------------------------------------------+ 28. bool Sum(long &arg1, const long &arg2[]) 29. { 30. Print(__FUNCTION__, "::", __LINE__); 31. 32. if (arg2.Size() == 0) 33. return false; 34. 35. for (uchar c = 0; c < arg2.Size(); c++) 36. arg1 += arg2[c]; 37. 38. return true; 39. } 40. //+------------------------------------------------------------------+
Code 06
This case is really very interesting and has many practical consequences. Because often, at least in MQL5 environment, where the goal is to create mechanisms for graphical interpretation of prices and deals, we implement code which main assignment or purpose is to perform some factorization using multiple quotes. Usually, this kind of factorization is in some way related to development of a specific indicator or trading model intended for the use in the Expert Advisor, in order to form a visual graphical signal and attract the attention of a trader or a user of MetaTrader 5 platform to position opening or closing, or at least to a certain movement, which may happen at some point.
I understand that many people, especially beginners, are eager to create something and immediately see how it is executed. However, before we can do this, it is necessary to thoroughly explore the kind of things that we are currently considering in these articles. Here, the material is more basic and is aimed at creating a solid foundation so that you can later work with something more complex and task-oriented. Since I do not want to spend a lot of time explaining simple details in the future, I am creating this knowledge base now. Thus, my dear reader, you will be able to adapt or even improve what you see in articles with more specialized content, not only mine, but also written by any other programmer whose idea or code you want to use.
So, in this code 06 we have just an example of overload, where in one case we use a discrete value in a very interesting way, and in the other, a set of values contained in a data array. However, despite the external simplicity, we often have problems with calculations being executed, due to minor errors in understanding some basic concepts. Among these errors is the fact that we often do not pay attention to the initial values of the parameters to be passed to the function. These kinds of errors can sometimes be difficult to track down and fix, at least until we understand the real reason for the failure.
But such things are easier to understand in practice, and as you gain experience you will make fewer and fewer such errors. And if they still occur it will only be due to a lack of diligence during implementation.
Now let us see what we have here, in code 06. When executing the code shown above, you will get the following result, shown below.
Figure 05
Now understand this, my dear reader. As mentioned earlier, we often have problems when executing multiple factorization. This is because we use the wrong number of elements when factoring. To avoid this, we refuse to return a value from the function and instead return a feature. It will indicate whether to accept the factorized value or not. Therefore, in line 32 we check exactly this. Note that Sum function is still overloaded, because in line 22 it has the same name as in line 28. However, the fact that the return value has a different purpose does not negate the fact that the function is overloaded anyway.
This kind of thing can often be confusing, especially if we only look at the lines where the function is used. Look at lines ten and eleven — you will clearly notice that what is being returned is not entirely logical. Therefore, if you have doubts about how the code works, try to study its details. Do not think that just because a function or procedure has the same name as another one that you already know, they necessarily work the same way. It is not always the case.
Finally, we can make a small change to code 06. It is shown in the below snippet.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. long v = -16, 07. m[]; 08. bool b; 09. 10. // Add(m, Sum(10, 25)); 11. Print("Return: ", b = Sum(v, m)); 12. if (b) Print("Value: ", v); 13. ArrayFree(m); 14. } 15. //+------------------------------------------------------------------+ . . .
Snippet 01
Please note that in this snippet I am removing one line from the code, turning it into a comment. In this case, this is line ten. But this simple change, which apparently only means that we did not add the right number of elements for factorization, will lead to the result being as shown below.
Figure 06
Experiment with making other changes to code 06 to gain other types of understanding, as this will greatly help you in the future.
Final considerations
Perhaps this article turned out to be the most confusing for novice programmers, because here I showed that in the same code not always all functions and procedures will have unique names. Yes, we can easily use functions and procedures with the same name — and this is called overload. Although this is acceptable, caution should be exercised when using this practice. Because it often makes the code not only difficult to implement, but also difficult to fix it later.
I hope that this article will serve as an explanation for you and make you more careful not to automatically assume that an already known function or procedure will necessarily behave the same as another function or procedure implemented using overload.
In the appendix, I will leave snippets of the code discussed in this article. Try to study this material, making changes to the codes from the application in order to understand how overloading can affect both your understanding of the code and the potential errors that may arise due to misuse of overloading. It is better to learn how to deal with errors while the mechanisms are still simple than to try to deal with an error in a more complex system. Therefore, study the issue of overloading now, while everything is relatively simple. After all, as we proceed everything will only get more complicated.
Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/15642
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.






- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use