Bao gồm thư viện; #import
của các hàm
Các hàm được nhập từ các mô-đun MQL5 đã biên dịch (tệp *.ex5
) và từ các mô-đun thư viện động của Windows (tệp *.dll
). Tên mô-đun được chỉ định trong chỉ thị #import
, theo sau là các mô tả về nguyên mẫu hàm được nhập. Một khối như vậy phải kết thúc bằng một chỉ thị #import
khác, hơn nữa, nó có thể không có tên và chỉ đơn giản là đóng khối đó, hoặc tên của một thư viện khác có thể được chỉ định trong chỉ thị, và do đó khối nhập tiếp theo bắt đầu cùng lúc. Một chuỗi các khối nhập luôn phải kết thúc bằng một chỉ thị không có tên thư viện.
Ở dạng đơn giản nhất, chỉ thị trông như sau:
#import "[path] module_name [.extension]"
function_type function_name([parameter_list]);
[function_type function_name([parameter_list]);]
...
#import
2
3
4
5
Tên của tệp thư viện có thể được chỉ định mà không có phần mở rộng: khi đó mặc định sẽ giả định là DLL. Phần mở rộng ex5
là bắt buộc.
Tên có thể được đặt trước bởi đường dẫn vị trí của thư viện. Theo mặc định, nếu không có đường dẫn, các thư viện được tìm kiếm trong thư mục MQL5/Libraries
hoặc trong thư mục bên cạnh chương trình MQL nơi thư viện được kết nối. Nếu không, các quy tắc khác nhau được áp dụng để tìm kiếm thư viện tùy thuộc vào loại là DLL hay EX5. Những quy tắc này được đề cập trong một phần riêng.
Dưới đây là một ví dụ về các khối nhập liên tiếp từ hai thư viện:
#import "user32.dll"
int MessageBoxW(int hWnd, string szText, string szCaption, int nType);
int SendMessageW(int hWnd, int Msg, int wParam, int lParam);
#import "lib.ex5"
double round(double value);
#import
2
3
4
5
6
Với các chỉ thị như vậy, các hàm được nhập có thể được gọi từ mã nguồn giống như các hàm được định nghĩa trực tiếp trong chính chương trình MQL. Tất cả các vấn đề kỹ thuật liên quan đến việc tải thư viện và chuyển hướng các lệnh gọi đến các mô-đun bên thứ ba được xử lý bởi môi trường thực thi chương trình MQL.
Để trình biên dịch phát hành chính xác lệnh gọi đến hàm được nhập và tổ chức việc truyền tham số, cần một mô tả đầy đủ: với kiểu kết quả, với tất cả các tham số, bộ sửa đổi và giá trị mặc định, nếu chúng có trong nguồn.
Vì các hàm được nhập nằm ngoài mô-đun đã biên dịch, trình biên dịch không thể kiểm tra tính đúng đắn của các tham số được truyền và giá trị trả về. Bất kỳ sự không khớp nào giữa định dạng của dữ liệu kỳ vọng và nhận được sẽ dẫn đến lỗi trong quá trình thực thi chương trình, và điều này có thể biểu hiện dưới dạng chương trình dừng đột ngột hoặc hành vi không mong đợi.
Nếu thư viện không thể được tải hoặc hàm được nhập được gọi không được tìm thấy, chương trình MQL sẽ kết thúc với một thông báo tương ứng trong nhật ký. Chương trình sẽ không thể chạy cho đến khi vấn đề được giải quyết, ví dụ, bằng cách sửa đổi và biên dịch lại, đặt thư viện cần thiết vào một trong những vị trí dọc theo đường dẫn tìm kiếm, hoặc cho phép sử dụng DLL (chỉ dành cho DLL).
Khi chia sẻ nhiều thư viện (không quan trọng là DLL hay EX5), hãy nhớ rằng chúng phải có tên khác nhau, bất kể thư mục vị trí của chúng. Tất cả các hàm được nhập nhận một phạm vi khớp với tên của tệp thư viện, tức là, nó là một dạng không gian tên, được phân bổ ngầm cho mỗi thư viện được bao gồm.
Các hàm được nhập có thể có bất kỳ tên nào, bao gồm cả những tên khớp với tên của các hàm tích hợp sẵn (mặc dù điều này không được khuyến nghị). Hơn nữa, có thể đồng thời nhập các hàm có cùng tên từ các mô-đun khác nhau. Trong những trường hợp như vậy, cần áp dụng thao tác quyền ngữ cảnh để xác định hàm nào nên được gọi.
Ví dụ:
#import "kernel32.dll"
int GetLastError();
#import "lib.ex5"
int GetLastError();
#import
class Foo
{
public:
int GetLastError() { return(12345); }
void func()
{
Print(GetLastError()); // gọi phương thức lớp
Print(::GetLastError()); // gọi hàm MQL5 tích hợp (toàn cục)
Print(kernel32::GetLastError()); // gọi hàm từ kernel32.dll
Print(lib::GetLastError()); // gọi hàm từ lib.ex5
}
};
void OnStart()
{
Foo foo;
foo.func();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Hãy xem một ví dụ đơn giản về kịch bản LibRandTest.mq5
, sử dụng các hàm từ thư viện EX5 được tạo trong phần trước.
#include <MQL5Book/LibRand.mqh>
Trong các tham số đầu vào, bạn có thể chọn số phần tử trong mảng số, các tham số phân phối, cũng như bước của biểu đồ, mà chúng ta sẽ tính toán để đảm bảo rằng phân phối gần đúng với quy luật chuẩn.
input int N = 10000;
input double Mean = 0.0;
input double Sigma = 1.0;
input double HistogramStep = 0.5;
input int RandomSeed = 0;
2
3
4
5
Việc khởi tạo bộ tạo số ngẫu nhiên tích hợp trong MQL5 (phân phối đều) được thực hiện bởi giá trị của RandomSeed
hoặc, nếu để 0 ở đây, GetTickCount
sẽ được chọn (mới mỗi lần khởi động).
Để xây dựng một biểu đồ, chúng ta sử dụng MapArray
và QuickSortStructT
(chúng ta đã làm việc với chúng trong các phần về chỉ báo đa tiền tệ và về sắp xếp mảng, tương ứng). Bản đồ sẽ tích lũy các bộ đếm số lần các số ngẫu nhiên rơi vào các ô của biểu đồ với bước HistogramStep
.
#include <MQL5Book/MapArray.mqh>
#include <MQL5Book/QuickSortStructT.mqh>
2
Để hiển thị biểu đồ dựa trên bản đồ, cần phải sắp xếp bản đồ theo thứ tự khóa-giá trị. Để làm điều này, chúng ta phải định nghĩa một lớp dẫn xuất.
#define COMMA ,
template<typename K,typename V>
class MyMapArray: public MapArray<K,V>
{
public:
void sort()
{
SORT_STRUCT(Pair<K COMMA V>, array, key);
}
};
2
3
4
5
6
7
8
9
10
11
Lưu ý rằng macro COMMA trở thành một biểu diễn thay thế của ký tự dấu phẩy ',' và được sử dụng khi macro SORT_STRUCT khác được gọi. Nếu không có sự thay thế này, dấu phẩy bên trong Pair<K,V> sẽ được trình tiền xử lý diễn giải như một dấu phân cách tham số macro thông thường, kết quả là 4 tham số sẽ được nhận ở đầu vào của SORT_STRUCT thay vì 3 như kỳ vọng — điều này sẽ gây ra lỗi biên dịch. Trình tiền xử lý không biết gì về cú pháp MQL5.
Ở đầu OnStart
, sau khi khởi tạo bộ tạo, chúng ta kiểm tra việc nhận một chuỗi ngẫu nhiên đơn lẻ và một mảng các chuỗi có độ dài khác nhau.
void OnStart()
{
const uint seed = RandomSeed ? RandomSeed : GetTickCount();
Print("Random seed: ", seed);
MathSrand(seed);
// gọi hai hàm thư viện: StringPatternDigit và RandomString
Print("Random HEX-string: ", RandomString(30, StringPatternDigit() + "ABCDEF"));
Print("Random strings:");
string text[];
RandomStrings(text, 5, 10, 20); // 5 dòng từ 10 đến 20 ký tự
ArrayPrint(text);
...
2
3
4
5
6
7
8
9
10
11
12
13
Tiếp theo, chúng ta kiểm tra các số ngẫu nhiên phân phối chuẩn.
// gọi một hàm thư viện khác: PseudoNormalArray
double x[];
PseudoNormalArray(x, N, Mean, Sigma); // điền mảng x
Print("Random pseudo-gaussian histogram: ");
// lấy 'long' làm kiểu khóa, vì 'int' đã được sử dụng cho truy cập chỉ số
MyMapArray<long,int> map;
for(int i = 0; i < N; ++i)
{
// giá trị x[i] xác định ô của biểu đồ, nơi chúng ta tăng thống kê
map.inc((long)MathRound(x[i] / HistogramStep));
}
map.sort(); // sắp xếp theo khóa (tức là theo giá trị)
int max = 0; // tìm kiếm tối đa để chuẩn hóa
for(int i = 0; i < map.getSize(); ++i)
{
max = fmax(max, map.getValue(i));
}
const double scale = fmax(max / 80, 1); // biểu đồ có tối đa 80 ký hiệu
for(int i = 0; i < map.getSize(); ++i) // in biểu đồ
{
const int p = (int)MathRound(map.getValue(i) / scale);
string filler;
StringInit(filler, p, '*');
Print(StringFormat("%+.2f (%4d)",
map.getKey(i) * HistogramStep, map.getValue(i)), " ", filler);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Dưới đây là kết quả khi chạy với cài đặt mặc định (ngẫu nhiên hóa bộ đếm thời gian - mỗi lần chạy sẽ chọn một seed
mới).
Random seed: 8859858
Random HEX-string: E58B125BCCDA67ABAB2F1C6D6EC677
Random strings:
"K4ZOpdIy5yxq4ble2" "NxTrVRl6q5j3Hr2FY" "6qxRdDzjp3WNA8xV" "UlOPYinnGd36" "6OCmde6rvErGB3wG"
Random pseudo-gaussian histogram:
-9.50 ( 2)
-8.50 ( 1)
-8.00 ( 1)
-7.00 ( 1)
-6.50 ( 5)
-6.00 ( 10) *
-5.50 ( 10) *
-5.00 ( 24) *
-4.50 ( 28) **
-4.00 ( 50) ***
-3.50 ( 100) ******
-3.00 ( 195) ***********
-2.50 ( 272) ***************
-2.00 ( 510) ****************************
-1.50 ( 751) ******************************************
-1.00 (1029) *********************************************************
-0.50 (1288) ************************************************************************
+0.00 (1457) *********************************************************************************
+0.50 (1263) **********************************************************************
+1.00 (1060) ***********************************************************
+1.50 ( 772) *******************************************
+2.00 ( 480) ***************************
+2.50 ( 280) ****************
+3.00 ( 172) **********
+3.50 ( 112) ******
+4.00 ( 52) ***
+4.50 ( 43) **
+5.00 ( 10) *
+5.50 ( 8)
+6.00 ( 8)
+6.50 ( 2)
+7.00 ( 3)
+7.50 ( 1)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Trong thư viện này, chúng ta chỉ xuất và nhập các hàm với các kiểu tích hợp sẵn. Tuy nhiên, các giao diện đối tượng với cấu trúc, lớp và mẫu thú vị hơn nhiều và được yêu cầu nhiều hơn từ góc độ thực tế. Chúng ta sẽ nói về các sắc thái của việc sử dụng chúng trong thư viện trong một phần riêng.
Khi thử nghiệm Cố vấn Chuyên gia và chỉ báo trong bộ kiểm tra, cần lưu ý một điểm quan trọng liên quan đến thư viện. Các thư viện cần thiết cho chương trình MQL chính được kiểm tra được xác định tự động từ các chỉ thị
#import
. Tuy nhiên, nếu một chỉ báo tùy chỉnh được gọi từ chương trình chính, mà một thư viện nào đó được kết nối, thì cần chỉ rõ rõ ràng trong thuộc tính chương trình rằng nó phụ thuộc gián tiếp vào một thư viện cụ thể. Điều này được thực hiện bằng chỉ thị:
#property tester_library "path_library_name.extension"