Русский 中文 Español Deutsch 日本語 Português
preview
Master MQL5 from Beginner to Pro (Part III): Complex Data Types and Include Files

Master MQL5 from Beginner to Pro (Part III): Complex Data Types and Include Files

MetaTrader 5Examples |
2 373 5
Oleh Fedorov
Oleh Fedorov

Introduction

This article is a continuation of the series for beginners. Here I assume that the reader already has an understanding of the material from the previous two articles.

The first article is an introduction. It assumes that the reader has no prior experience with programming, and introduces the required tools for programmers, describes the main program types, and introduces some basic concepts, in particular, the concept of a "function".

The second article describes operations with data. It introduces the concepts of "literal", "variable", "data type", "operator", etc., and examines the main data modification operators: arithmetic, logical, bitwise, and others

In this article I will describe how a programmer can create complex data types:

  • Structures
  • Union
  • Classes (at the beginner level)
  • Types that allow a variable name to be used as a function. This allows, among other things, to pass functions as parameters to other functions.

The article also describes how to include external text files using the #include preprocessor directive to ensure our program is modular and flexible. Let me remind you that data can be organized in different ways, but the compiler must always know how much memory our program will need, and therefore before using the data, it must be described by specifying its type.

Simple data types such as double, enum, string, and others were described in the second article. It examined in detail both variables (data that changes during operation) and constants. However, when programming, situations often arise when it is more convenient to create more complex types from simple data. It is precisely these "constructions" that we will talk about in the first part of this article.

The more modular structure a program has, the easier it is to develop and maintain it. This becomes especially important when working in a team. It is also much easier for "solo developers" to look for errors not in a lengthy piece of code, but in its small fragments. Especially if you return to the code after a long time in order to add some features to your program or fix some logical errors that were not immediately noticeable.

If you provide proper data structures, separate convenient functions rather than use lengthly lists of conditions and loops, and distribute different logically related blocks of code across different files, making changes will be much easier.


Structures

A structure describes a complex set of data that can be conveniently stored in a single variable. For example, information about the time of execution of an intraday trade should contain hours, minutes, and seconds.

Of course, you can create three variables for each component and access each of them as needed. However, since these variables are parts of a single description and are most often used together, it is convenient to describe a separate type for such data. The structure can also contain additional data of other types, such as a time zone or anything else the programmer needs.

In the simplest case, the structure is described as follows:

struct IntradayTime {
  int hours;
  int minutes;
  int seconds;
  string timeCodeString;
};  // note the semicolon after the curly brace

Example 1. An example structure for describing the trade time.

This codes creates a new data type IntradayTime. Between the curly braces of this declaration, you can see all the variables that we want to combine. Therefore, all variables of the type IntradayTime will contain hours, minutes, and seconds.

Each part of the structure within each variable can be accessed via a period ".".

IntradayTime dealEnterTime;

dealEnterTime.hours = 8;
dealEnterTime.minutes = 15;
dealEnterTime.timeCodeString = "GMT+2";

Example 2. Using structure type variables.

When we describe a structure, its "internal" variables (often referred to as fields) may have any valid data type, including the use of other structures. For example:

// Nested structure
struct TradeParameters
{
   double stopLoss;
   double takeProfit;
   int magicNumber;
};

// Main structure
struct TradeSignal
{
   string          symbol;    // Symbol name
   ENUM_ORDER_TYPE orderType; // Order type (BUY or SELL)
   double          volume;    // Order volume
   TradeParameters params;    // Nested structure as parameter type
};

// Using the structure
void OnStart()
{

// Variable description for the structure
   TradeSignal signal;

// Initializing structure fields
   signal.symbol = Symbol();
   signal.orderType = ORDER_TYPE_BUY;
   signal.volume = 0.1;

   signal.params.stopLoss = 20;
   signal.params.takeProfit = 40;
   signal.params.magicNumber = 12345;

// Using data in an expression
   Print("Symbol: ",  signal.symbol);
   Print("Order type: ",  signal.orderType);
   Print("Volume: ",  signal.volume);
   Print("Stop Loss: ",  signal.params.stopLoss);
   Print("Take Profit: ",  signal.params.takeProfit);
   Print("Magic Number: ",  signal.params.magicNumber);
}

Example 3. Using a structure to describe the type of fields of another structure.


If you use constants rather than expressions as initial values for a structure, you can use a shorthand notation for initialization. Here you should use curly braces. For example, the initialization block from the previous example could be rewritten as:

TradeSignal signal = 
  {
    "EURUSD", 
    ORDER_TYPE_BUY, 
    0.1, 
 
     {20.0,  40.0,  12345}
  };

Example 4. Initializing a structure using constants.


The order of constants must match the order of fields in the description. You can also initialize only part of the structure by listing values for initial fields. In this case, all other fields will be initialized to zeros.

MQL5 provides a set of predefined structures, such as MqlDateTime, MqlTradeRequest, MqlTick, and others. As a rule, their use is no more complicated than described in this section. The list of fields of these and many other structures is described in detail in the language reference.

In addition, this list for any structures (and other complex types) is visible in MetaEditor when you create a variable of the desired type, then type its name and press the period (".") on the keyboard.

List of structure fields

Figure 1. List of structure fields in MetaEditor.

All fields of the structure are available by default to all functions of our program.


About MQL5 Structures: A few words for those who know how to work with external dlls

Warning. This section may be difficult for beginners, so when you first read the article, you can skip it and go straight to unions, and then return to this section later.

By default, data in MQL5 structures is located in a packed form, i.e. directly one after another, so if you want the structure to occupy a certain number of bytes, you may need to add additional elements.

In this case, it is better to first place the largest data, then the smaller data. This way you can avoid many problems. However, MQL5 structures also have the ability to "align" data using a special operator pack:

struct pack(sizeof(long)) MyStruct1
     {
      // structure members will be aligned on an 8-byte boundary
     };

// or

struct MyStruct2 pack(sizeof(long))
     {
      // structure members will be aligned on an 8-byte boundary
     };

Example 5. Aligning the structure.

Inside the brackets of pack, you can use only numbers 1, 2, 4, 8, 16.

The special command offsetof will allow you to get the offset in bytes for any field of the structure relative to the beginning. For example, if we take the TradeParameters structure from example 3, you can use the following code to obtain the stopLoss field offset:

Print (offsetof(TradeParameters, stopLoss)); // Result: 0

Example 6. Using the operator offsetof.

The structures that DO NOT contain strings, dynamic arrays, class-based objects, and pointers are called simple. Variables of simple structures, as well as arrays consisting of such elements, can be freely passed to functions imported from external DLL libraries.

It is also possible to copy simple structures into each other using the assignment operator, but only in two cases:

  • either the variables are of the same type;
  • or the variable types are related by a directinheritance line.

    This means that if we have defined structures "plants" and "trees", any variable of "plants" can be copied to any variable created on the basis of "trees", and vice versa. However, if we also have "bushes", then you can copy from "bushes" to "trees" (or vice versa) only element by element.

In all other cases even structures with the same fields must be copied element by element.

The same rules apply to type casting: you can't cast "bush" directly to "tree", even if they have the same fields, but you can cast "plant" to "bush".

Well, if you really need to cast the "bush" type to a "tree", you can use unions. However, you should note the restrictions on unions which are described in the relevant section of this article. In short, any numeric fields can be easily converted.

//---
enum ENUM_LEAVES
  {
   rounded,
   oblong,
   pinnate
  };

//---
struct Tree
  {
   int               trunks;
   ENUM_LEAVES       leaves;
  };

//---
struct Bush
  {
   int               trunks;
   ENUM_LEAVES       leaves;
  };

//---
union Plant
  {
   Bush bush;
   Tree tree;
  };

//---
void OnStart()
  {
   Tree tree = {1, rounded};
   Bush bush;
   Plant plant;

// bush = tree; // Error!
// bush = (Bush) tree; // Error!
   plant.tree = tree;
   bush = plant.bush; // No problem...

   Print(EnumToString(bush.leaves));
  }
//+------------------------------------------------------------------+

Example 7. Converting structures using unions.

That's it for now. The complete description of all capabilities of structures contain more details and nuances than described in this article. You may want to compare MQL5 structures with other languages or learn further details. In this case, again, please check the language reference.

But for beginners, in my opinion, the material written about structures is quite sufficient, so I move on to the next section.


Unions

For some tasks, you may need to interpret the data in one memory cell as variables of different types. Most often, such problems are encountered when converting structure types. Similar requirements may also arise during encryption.

The description of such data is almost no different from the description of simple structures:

// Creating a type
union AnyNumber {
  long   integerSigned;  // Any valid data types (see further)
  ulong  integerUnsigned;
  double doubleValue;
};

// Using
AnyNumber myVariable;

myVariable.integerSigned = -345;

Print(myVariable.integerUnsigned);
Print(myVariable.doubleValue);

Example 8. Using unions.

To avoid errors in unions, it is recommended to use data whose type occupies the same memory space (although this my be unnecessary or even harmful with some conversions).

The following data types cannot be union members:

  • Dynamic arrays
  • Strings
  • Pointers to objects and functions
  • Class objects
  • Structure objects that have constructors or destructors
  • Objects of structures that have elements from points 1-5

No other restrictions apply.

Please remember this: if your structure uses any string field, the compiler will give an error. Always take this into account.


Basic understanding of Object-Oriented Programming

Object-oriented programming is a programming paradigm fundamental to many programming languages. Within this approach, everything that happens in the program is broken down into separate blocks. Each such block describes a certain "entity": a file, a line, a window, a list of prices, etc.

The purpose of each block is to collect data and the actions needed to process it in one place. If the blocks are constructed correctly, this structure gives many advantages:

  • Allows for multiple code reuse
  • Facilitates IDE operations, providing the ability to quickly substitute the names of variables and functions that relate to specific objects
  • Makes it easier to find errors and reduces the chances of adding new ones
  • Facilitates parallel operations for different people (or even teams) working on different parts of the code
  • Makes it easier to change your code, even if a lot of time has passed
  • All this ultimately leads to faster program development, increased reliability and easier coding in the end.

Such a layout is, in general, natural, since it follows everyday life principles. We always classify all sorts of objects: "This thing belongs to the animal class, that one belong to plants, the other one is furniture", and so on. Furniture, in turn, can be cabinet or upholstered. And so on.

All these classifications use certain features of objects and their descriptions. For example, plants have a trunk and roots, and animals have movable limbs on which they can move. So, each class has some characteristic attributes. Things are the same in programming.

If you set a goal to create a library for working with lines, you need to clearly understand what each line can do and what she has for it. For example, any line has a start point, end point, thickness and color.

These are its properties or attributes or fields of the line class. For actions, you can use the verbs "draw", "move", "copy with a certain offset", "rotate by a certain angle", and others

If a line object can perform all these actions independently, then programmers talk about the methods of this object.

Properties and methods together are referred to as the members (elements) of the class.

So, in order to create a line using this approach, you first need to create a class (description) of this line - and all the other lines in the program - and then inform the compiler: "These variables are lines, and this function uses them."

Class is a variable type, containing a description of properties and methods of objects belonging to this class.

In terms of the description way, a class is very similar to a structure. The main difference is that by default, all members of a class are accessible only within that class. In a structure, all its members are accessible to all functions of our program. Below is the general scheme for creating the desired class:

// class (variable type) description
class TestClass { // Create a type

private:          // Describe private variables and functions 
                  //   They will only be accessible to functions within the class 
  
// Description of data (class "properties" or "fields")
  double m_privateField; 

// Description of functions (class "methods")
  bool  Private_Method(){return false;} 

public:           // Description of public variables and functions 
                  //   They will be available to all functions that use objects of this class    

// Description of data (class "properties", "fields", or "members")   
  int m_publicField; 

// Description of functions (class "methods")   
  void Public_Method(void)
    {
     Print("Value of `testElement` is ",  testElement );   
    }
 }; 


Example 9. Description of class structure

Keywords public: and private: define the visibility zones of class members.

Everything below the word public: will be available "outside" the class, that is, for other functions of our program, even those that do not belong to this class.

Everything above this section (and below the word private:) will be "hidden", and access to these elements will only be available to the functions of the same class.

A class can contain any number of public: and private: sections.

However, despite the suggestion in the box, it is better to use only one block per scope (one private: and one public:) so that all data or functions with the same access levels are located close to each other. Some experienced developers still prefer to create four sections - two (private and public) for functions and two for variables. Now it's up to you to decide.

Basically, the word private: can be omitted, since all members of the class not declared as public: will be private by default (unlike structures). But it is not recommended to do this, since such code will become inconvenient to read.

It is important to remember that in general, at least one function in the described class must be "public", otherwise the class will be useless in most cases. There are exceptions, but they are rare.

It is considered a good programming practice to place only functions (not variables) in the public: section in order to protect the data. This allows class variables to be modified only through methods of that class. This approach increases the reliability of the program code.

To use a described class, variables of the required type are created in the desired place of the program. Variables are created in a usual way. Access to the methods and properties of each such variable is typically done through the period symbol, as in structures:
// Description of the variable of the required type
TestClass myTestClassVariable;

// Using the capabilities of this variable
myTestClassVariable.testElement = 5;
myTestClassVariable.PrintTestElement();

Example 10. Using a class.

To illustrate how public and private properties work, try pasting the code from Example 11 inside the OnStart function definition of your script and compile the file. The compilation should be successful.

Then try uncommenting the line "myVariable.a = 5;" and compiling the code again. In this case, you will get a compilation error indicating that you are trying to access private members of a class. This feature of the compiler helps eliminate some of the subtle errors that programmers can make when working with other approaches.

class PrivateAndPublic 
  {
private:
    int a;
public:
    int b;
  };

PrivateAndPublic myVariable;

// myVariable.a = 5; // Compiler error! 
myVariable.b = 10;   // Success

Example 11. Using public and private properties of a class.

If we had to write all classes by ourselves, this approach would be no different from all the others, and there would be little point in it.

Fortunately, many standard classes are already available in the MQL5\Include directory. Also, a lor of useful libraries can be found in the Code Base. In many cases, we just need to include the appropriate file (as described below) to take advantage of other smart people's developments. This is of great help for programmers.

Huge books are devoted to OOP, and it definitely deserves a separate article. However, the purpose of this article is simply to give the beginner an idea of how to use complex data types in the programs. Now that you know how to define a basic class and how to use other people's classes, I'll just move on to the next section.


Functional data type (typedef operator)

Warning. This section may be difficult for beginners, so you can skip it when reading the article for the first time.

Understanding the material in this section will not affect your understanding of the rest of the material - or even, perhaps, the rest of your programming journey. Most problems can have multiple solutions, and functional types can be easily avoided.

However, the ability to assign certain functions to a variable (and therefore, in some cases, use them as arguments to other functions) can be convenient, and I think it's worth knowing about this ability, at least to be able to read someone else's code.

Sometimes it is useful to create variables of a "functional" type, for example to pass them as an argument to another function.

For example, in a trading situation, buy and sell orders are very similar and differ only in one parameter. However, the buying price is always Ask, and the selling price is always Bid.

Often programmers write their own Buy and Sell functions, which take into account all the nuances of a specific order. Then they write a function like Trade, which combines these two possibilities and looks the same both when trading "up" and when trading "down". It's convenient because Trade itself substitutes calls to written Buy or Sell functions depending on the calculated direction of price movement, and the programmer can focus on something else.

You can think of many cases when you want to say: "Robot, do it!" and let the function decide which of the options should be called in a given situation. When calculating take profit, should the number of points be added or subtracted from the price? When calculating stop loss? When placing an order at an extremum, should it look for maximums or minimums? And so on.

In such cases, the approach described below is sometimes used.

As usual, you first need to describe the type of the variable you need. In this case, this type is described using the following template:

typedef function_result_type (*Function_type_name)(input_parameter1_type,input_parameter1_type ...); 

Example 12. Template for describing a functional type.

Where:

  • function_result_type is the type of the return value (nay valid type, such as int, double, and others).
  • Function_type_name is the name of the type we will use when creating variables.
  • input_parameter1_type is the type of the first parameter. The parameter list follows the rules of normal function lists.

Note the asterisk (*) before the type name. It is important, and without it nothing will work.

It means that the variable of this type will not contain a result or a number, but the function itself, which has a full set of capabilities, and, therefore, this variable will combine the capabilities inherent in both other variables and functions.

Such a construction, which, when describing a data type, uses the object itself (a function, an object of some class, etc.), and not a copy of the object's data or its operation result, is called a pointer.

We will talk more about pointers in future articles. Let's look at a working example of using the typedef operator.

Let us have functions Diff and Add, which we want to assign to some variable. Both functions return integer values and take two integer parameters. Their implementation is straightforward:

//---
int Add (int a,int b)
  {
    return (a+b);
  }

//---
int Diff (int a,int b)
  {
    return (a-b);
  }

Example 13. Summation and difference functions for functional type testing.

Let's describe the TFunc type for variables that can store any of these functions:
typedef int (*TFunc) (int,  int);

Example 14. Type declaration for variables that can store Add and Diff functions.


Now let's check how this description will work:

void OnStart()
  {
    TFunc operate;       //As usual, we declare a variable of the described type
 
    operate = Add;       // Write a value to a variable (in this case, assign a function)
    Print(operate(3, 5)); // Use the variable as a normal function
                         // Function output: 8

    operate=Diff;
    Print(operate(3, 5)); // Function output: -2
  }

Example 15. Using a variable of a functional type.

I would like to note that the typedef operator only works with custom functions

You cannot use standard functions like MathMin or similar ones directly, but you can make a "wrapper" for them. For example:

//---
double MyMin(double a, double b){
   return (MathMin(a,b));
}

//---
double MyMax(double a, double b){
   return (MathMax(a,b));
}

//---
typedef double (*TCompare) (double,  double);

//---
void OnStart()
  {
    TCompare extremumOfTwo;

    compare= MyMin;
    Print(extremumOfTwo(5, 7));// 5

    compare= MyMax;
    Print(extremumOfTwo(5, 7));// 7
  }

Example 16. Using wrappers to work with standard functions.


Including external files (#include preprocessor directive)

Any program can be divided into several modules.

If you are working with large projects, dividing them is imperative. The modularity of the program solves several problems at once.

  • First, modular code is easier to navigate.
  • Second, if you work in a team, each module can be developed described by different people, which greatly speeds up the process.
  • And third, created modules can be reused.

The most obvious "module" is a function. Furthermore, you can create modules for all constants, some complex data type descriptions, combinations of several related functions (for example, functions for changing the appearance of objects or mathematical functions), etc.

In large projects, it is very convenient to place such blocks of code in separate files, and then include these files in the current program.

To include additional text files into a program, we use the #include preprocessor directive:

#include <SomeFile.mqh>     // Angle brackets specify search relative to MQL5\Include directory 
#include "AnyOtherPath.mqh" // Quotes specify search relative to current file

Example 17. Two forms of #include.

If the compiler encounters the #include instruction anywhere in your code, it tries to insert the contents of the specified file in place of this instruction, but only once per program. If the file has already been used, it cannot be included a second time.

You can test this statement using the script described in the next section.

Most often, included files are given the extension *.mqh, since this is convenient, but in general the extension can be any.


Script to test the operation of the #include directive

Test the compiler's actions when this preprocessor directive is encountered, we need to create two files.

First, let's create a file named "1.mqh" in the scripts directory (MQL5\Scripts). The contents of this file will be very simple:

Print("This is include with number "+i);

Example 18. The simplest include file can contain only one command.

I hope it's clear what this code does. Assuming that a variable i was declared somewhere, this code creates a message for the user by appending the value of a variable to the message, and then prints that message to the log.

Variable i will be a marker indicating at what point in the script the instruction was called. Nothing else should be written in this file. Now, in the same directory, (where the file "1.mqh" is located) we create a script containing the following code:

//+------------------------------------------------------------------+ 
//| Script program start function                                    | 
//+------------------------------------------------------------------+ 
void OnStart() 
  { 
    //---   
    int i=1; 
#include "1.mqh"   
    i=2; 
#include "1.mqh" 
  } 
//+------------------------------------------------------------------+

// Script output:
// 
//   This is include with number 1
//
// The second attempt to use the same file will be ignored

//+------------------------------------------------------------------+ 

Example 19. Testing repeated inclusion of a file.

In this code, we tried to use the file "1.mqh" twice to receive two trigger messages.

When we run this script in the terminal, we will see that the first message works as expected, displaying the number 1 in the message, but the second message does not appear.

Why is this restriction applied? Why can't the file be used multiple times?

This is an important principle because include files often contain declarations of variables and functions. You already know, in one program at the global level (outside all functions) there should be only one variable with a certain name.

If, for example, variable int a; is declared, then the exact same variable cannot be declared at this level a second time. You can only use the one that exists. As for functions, the situation is a little more difficult but the idea is the same: each function must be unique within our program. Now imagine that the program uses two independent modules, but each of them includes the same standard class located in the file <Arrays\List.mqh> (Figure 2).

Using the same class by two modules

Figure 2. Using the same class by two modules.

Without that restriction, the compiler would return an error message, since it is forbidden to declare the same class twice. But in this case, such a construction is quite workable, since after the description of the FieldOf_Module1 field, the CList description is already included in the compiler lists, and therefore simply uses this description for module 2.

Understanding this principle, you can create even "multi-layered" nesting, for example, when some class elements depend on each other "circularly", as in Figure 3.

You can even describe a variable of the same class within a class.

All of these are acceptable constructions, because #include works exactly once for one file.

Circular dependency: each class contains elements that depend on another

Figure 3. Circular dependency: each class contains elements that depend on another.

In conclusion of this section, I would like to remind you once again that the files of the MetaTrader 5 standard libraries, which you can include to your code, are in the MQL5\Include directory. To open this directory in explorer, select the menu "File" -> "Open data directory" in the MetaTrader terminal (Figure 4).

Open data directory

Figure 4. How to open the data directory.

If you want to open the files from this directory in MetaEditor, find the Include folder in the navigator panel. You can create your own include files either in the same directory (preferably in separate folders), or you can use your program's directory and its subdirectories (see comments in example 17). As a rule, #include directives are used at the beginning of the file, before all other actions begin. However, this rule is not strict, and everything depends on the specific tasks.


Conclusion

Let me once again briefly name the topics that were discussed in this article.

  1. We have covered the preprocessor directive #include, which allows us to include additional text files into our program, usually some libraries.
  2. We have discussed complex data types: structures, unions, and objects (variables based on classes), as well as functional data types.

I hope that now the data types described in this article are "complex" for you only in structure, but not in application.

Unlike simple types, which are built into the language, "complex" types must first be declared and only then variables can be created. However, working with such data is essentially no different from working with "simple" types: you create variables and call components (members) of these variables (if any) or use the variable name as the function name if you have created a functional type.

Initialization of variables created using structures can be done using curly braces.

I hope you now understand that the ability to create your own complex types and breaking the program into modules stored in external files makes program development flexible and convenient.

Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/14354

Last comments | Go to discussion (5)
MrBrooklin
MrBrooklin | 19 Jul 2024 at 05:52

I am a beginner who has learnt the basics of programming. I have read your next article and made a conclusion: a beginner with complete lack of any knowledge will not understand anything from this article. This is my personal opinion, not claiming to be the truth in the last instance.

Let's take the Structures section of the article as an example. The beginning is good and clear enough. You have told what the structure is for and shown how to create it. And then bang, and new code!

IntradayTime dealEnterTime;

dealEnterTime.hours = 8;
dealEnterTime.minutes = 15;
dealEnterTime.timeCodeString = "GMT+2";

I have highlighted this part of the code on purpose. What should a beginner with zero knowledge understand from this line? What is it for him? I already understand it, but for a beginner with no knowledge it is another incomprehensible code fragment. That's why it is desirable to describe and fully explain each line. Otherwise it turns out that this article is not for beginners but for advanced programmers.

Regards, Vladimir.

Oleh Fedorov
Oleh Fedorov | 19 Jul 2024 at 08:32
MrBrooklin #:

I am a beginner who has learnt the basics of programming. I have read your next article and made a conclusion: a beginner with complete lack of any knowledge will not understand anything from this article. This is my personal opinion, not claiming to be the truth in the last instance.

Let's take the Structures section of the article as an example. The beginning is good and clear enough. You have told what the structure is for and shown how to create it. And then bang, and new code!

I have highlighted this part of the code on purpose. What should a beginner with zero knowledge understand from this line? What is it for him? I already understand it, but for a beginner with complete lack of knowledge it is another incomprehensible code fragment. That's why it is desirable to describe and fully explain each line. Otherwise, it turns out that this article is not for beginners but for advanced programmers.

Regards, Vladimir.

Is it just me - or is it this very structure that I created three lines earlier? And two lines ago I explained that it is a data type? And it should mean that this type should be used the same way as all the others? (Really, logic should be involved here, yes ;-))

Although you're probably right, a comment on the type at least wouldn't hurt.... Thanks.

AKHMED Asmalov
AKHMED Asmalov | 11 Feb 2025 at 04:25
void OnStart()
class PrivateAndPudlic
}
private:
int a;
public:
int b;
};
PrivateAndPudlic myVariable;
//myVariable.a = 5; //Compiler error!

myVariable.b = 10; //It's ok, you can do it this way

It gives an error during compilation. Can you please tell me what is wrong, where is the error?

Oleh Fedorov
Oleh Fedorov | 19 Mar 2025 at 10:28
AKHMED Asmalov #:
void OnStart()
class PrivateAndPudlic
}
private:
int a;
public:
int b;
};
PrivateAndPudlic myVariable;
//myVariable.a = 5; //Compiler error!

myVariable.b = 10; //It's ok, you can do it that way.

I get an error when compiling. Can you please tell me what is wrong, where is the error?

Sorry for the delay in replying.

The code in this example is not quite complete. To make it work, you need to use the myVariable variable somewhere inside the function. For example:

  class PrivateAndPudlic
   {
  private:
     int a;
  public:
     int b;
   }; 

 PrivateAndPudlic myVariable; // Global variable

void OnStart(){ // All calls to actions (in this case, assignment) must take place only inside functions
  //myVariable.a = 5; //Compiler error!

   myVariable.b = 10; //It's okay, it's okay
}

Well, you turned the parenthesis around when reprinting it (instead of the opening "{" you put the closing "}" ) ;-)

Oleh Fedorov
Oleh Fedorov | 19 Mar 2025 at 11:34
Oleh Fedorov #:

Sorry for the delay in replying.

The code in this example is not quite complete. To make it work, you need to use the myVariable variable somewhere inside the function. For example:

Well, you turned the parenthesis around when reprinting it (instead of the opening "{" you put the closing "}" ) ;-)

Well, or as described in the article:

void OnStart(){

class PrivateAndPudlic
 {
  private:
     int a;
  public:
     int b;
   }; 
 PrivateAndPudlic myVariable;
 
//myVariable.a = 5; //Compiler error!

 myVariable.b = 10; //It's okay, it's okay

}
Monitoring trading with push notifications — example of a MetaTrader 5 service Monitoring trading with push notifications — example of a MetaTrader 5 service
In this article, we will look at creating a service app for sending notifications to a smartphone about trading results. We will learn how to handle lists of Standard Library objects to organize a selection of objects by required properties.
MQL5 Trading Toolkit (Part 7): Expanding the History Management EX5 Library with the Last Canceled Pending Order Functions MQL5 Trading Toolkit (Part 7): Expanding the History Management EX5 Library with the Last Canceled Pending Order Functions
Learn how to complete the creation of the final module in the History Manager EX5 library, focusing on the functions responsible for handling the most recently canceled pending order. This will provide you with the tools to efficiently retrieve and store key details related to canceled pending orders with MQL5.
From Basic to Intermediate: Variables (I) From Basic to Intermediate: Variables (I)
Many beginning programmers have a hard time understanding why their code doesn't work as they expect. There are many things that make code truly functional. It's not just a bunch of different functions and operations that make the code work. Today I invite you to learn how to properly create real code, rather than copy and paste fragments of it. The materials presented here are for didactic purposes only. Under no circumstances should the application be viewed for any purpose other than to learn and master the concepts presented.
Price Action Analysis Toolkit Development (Part 8): Metrics Board Price Action Analysis Toolkit Development (Part 8): Metrics Board
As one of the most powerful Price Action analysis toolkits, the Metrics Board is designed to streamline market analysis by instantly providing essential market metrics with just a click of a button. Each button serves a specific function, whether it’s analyzing high/low trends, volume, or other key indicators. This tool delivers accurate, real-time data when you need it most. Let’s dive deeper into its features in this article.