Tạo thư viện ex5; xuất hàm
Để mô tả một thư viện, hãy thêm chỉ thị #property library
vào mã nguồn của mô-đun chính (được biên dịch) (thường là ở đầu tệp).
#property library
Việc chỉ định chỉ thị này trong bất kỳ tệp nào khác được bao gồm trong quá trình biên dịch thông qua #include
không có hiệu lực.
Thuộc tính library
thông báo cho trình biên dịch rằng tệp ex5 được chỉ định là một thư viện: một dấu hiệu về điều này được lưu trữ trong tiêu đề của tệp ex5.
Một thư mục riêng MQL5/Libraries
được dành sẵn cho các thư viện trong MetaTrader 5. Bạn có thể tổ chức một hệ thống phân cấp các thư mục lồng nhau trong đó, giống như đối với các loại chương trình khác trong MQL5.
Các thư viện không trực tiếp tham gia vào việc xử lý sự kiện, do đó trình biên dịch không yêu cầu sự hiện diện của bất kỳ trình xử lý tiêu chuẩn nào trong mã. Tuy nhiên, bạn có thể gọi các hàm được xuất của thư viện từ các trình xử lý sự kiện của chương trình MQL mà thư viện được kết nối.
Để xuất một hàm từ thư viện, chỉ cần đánh dấu nó bằng từ khóa đặc biệt export
. Bộ sửa đổi này phải được đặt ở cuối tiêu đề hàm.
result_type function_id ([ parameter_type parameter_id
[= default_value] ...]) export
{
...
}
2
3
4
5
Các tham số phải là các kiểu đơn giản hoặc chuỗi, cấu trúc với các trường thuộc các kiểu này, hoặc mảng của chúng. Con trỏ và tham chiếu được phép cho các kiểu đối tượng MQL5 (đối với các hạn chế khi nhập DLL, xem phần liên quan).
Hãy xem một số ví dụ. Tham số là một số nguyên tố:
double Algebraic2(const double x) export
{
return x / sqrt(1 + x * x);
}
2
3
4
Các tham số là một con trỏ đến một đối tượng và một tham chiếu đến một con trỏ (cho phép gán một con trỏ bên trong hàm).
class X
{
public:
X() { Print(__FUNCSIG__); }
};
void setObject(const X *obj) export { ... }
void getObject(X *&obj) export { obj = new X(); }
2
3
4
5
6
7
Tham số là một cấu trúc:
struct Data
{
int value;
double data[];
Data(): value(0) { }
Data(const int i): value(i) { ArrayResize(data, i); }
};
void getRefStruct(const int i, Data &data) export { ... }
2
3
4
5
6
7
8
9
Bạn chỉ có thể xuất các hàm chứ không phải toàn bộ lớp hoặc cấu trúc. Một số hạn chế này có thể được tránh bằng cách sử dụng con trỏ và tham chiếu, điều mà chúng ta sẽ thảo luận chi tiết hơn sau.
Các mẫu hàm không thể được khai báo với từ khóa export
và trong chỉ thị #import
.
Bộ sửa đổi export
hướng dẫn trình biên dịch đưa hàm vào bảng các hàm được xuất trong tệp thực thi ex5 đã cho. Nhờ đó, các hàm như vậy trở nên khả dụng ("hiển thị") từ các chương trình MQL khác, nơi chúng có thể được sử dụng sau khi nhập bằng chỉ thị đặc biệt #import
.
Tất cả các hàm dự định xuất phải được đánh dấu bằng bộ sửa đổi export
. Mặc dù chương trình chính không bắt buộc phải nhập tất cả chúng vì nó có thể chỉ nhập những hàm cần thiết.
Nếu bạn quên xuất một hàm nhưng đưa nó vào chỉ thị nhập trong chương trình MQL chính, thì khi chương trình sau được khởi chạy, sẽ xảy ra lỗi:
cannot find 'function' in 'library.ex5'
unresolved import function call
2
Một vấn đề tương tự sẽ phát sinh nếu có sự không nhất quán trong mô tả của hàm được xuất và nguyên mẫu được nhập của nó. Điều này có thể xảy ra, ví dụ, nếu bạn quên biên dịch lại thư viện hoặc chương trình chính sau khi thực hiện thay đổi đối với giao diện lập trình, thường được mô tả trong một tệp tiêu đề riêng.
Gỡ lỗi thư viện là không thể, vì vậy nếu cần, bạn nên có một kịch bản trợ giúp hoặc một chương trình MQL khác được xây dựng từ mã nguồn của thư viện ở chế độ gỡ lỗi và có thể được thực thi với các điểm dừng hoặc từng bước. Tất nhiên, điều này sẽ yêu cầu mô phỏng các lệnh gọi đến các hàm được xuất bằng một số dữ liệu thực hoặc nhân tạo.
Đối với DLL, mô tả các hàm được xuất được thực hiện khác nhau, tùy thuộc vào ngôn ngữ lập trình mà chúng được tạo ra. Hãy tìm chi tiết trong tài liệu của môi trường phát triển bạn đã chọn.
Hãy xem xét một ví dụ về một thư viện đơn giản MQL5/Libraries/MQL5Book/LibRand.mq5
, từ đó một số hàm được xuất với các kiểu tham số và kết quả khác nhau. Thư viện được thiết kế để tạo dữ liệu ngẫu nhiên:
- của dữ liệu số với phân phối giả chuẩn
- của chuỗi với các ký tự ngẫu nhiên từ các tập hợp đã cho (có thể hữu ích cho mật khẩu)
Cụ thể, bạn có thể nhận được một số ngẫu nhiên bằng hàm PseudoNormalValue
, trong đó giá trị kỳ vọng và phương sai được đặt làm tham số.
double PseudoNormalValue(const double mean = 0.0, const double sigma = 1.0,
const bool rooted = false) export
{
// use ready-made sqrt for mass generation in a cycle in PseudoNormalArray
const double s = !rooted ? sqrt(sigma) : sigma;
const double r = (rand() - 16383.5) / 16384.0; // [-1,+1] excluding borders
const double x = -(log(1 / ((r + 1) / 2) - 1) * s) / M_PI * M_E + mean;
return x;
}
2
3
4
5
6
7
8
9
Hàm PseudoNormalArray
điền mảng với các giá trị ngẫu nhiên theo số lượng đã cho (n
) và với phân phối yêu cầu.
bool PseudoNormalArray(double &array[], const int n,
const double mean = 0.0, const double sigma = 1.0) export
{
bool success = true;
const double s = sqrt(fabs(sigma)); // passing ready sqrt when calling PseudoNormalValue
ArrayResize(array, n);
for(int i = 0; i < n; ++i)
{
array[i] = PseudoNormalValue(mean, s, true);
success = success && MathIsValidNumber(array[i]);
}
return success;
}
2
3
4
5
6
7
8
9
10
11
12
13
Để tạo một chuỗi ngẫu nhiên, chúng ta viết hàm RandomString
, hàm này "chọn" từ tập hợp ký tự được cung cấp (pattern
) một số lượng đã cho (length
) các ký tự tùy ý. Khi tham số pattern
trống (mặc định), một tập hợp đầy đủ các chữ cái và số được giả định. Các hàm trợ giúp StringPatternAlpha
và StringPatternDigit
được sử dụng để lấy nó; các hàm này cũng có thể xuất (không được liệt kê trong sách, xem mã nguồn).
string RandomString(const int length, string pattern = NULL) export
{
if(StringLen(pattern) == 0)
{
pattern = StringPatternAlpha() + StringPatternDigit();
}
const int size = StringLen(pattern);
string result = "";
for(int i = 0; i < length; ++i)
{
result += ShortToString(pattern[rand() % size]);
}
return result;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Nói chung, để làm việc với một thư viện, cần xuất bản một tệp tiêu đề mô tả mọi thứ nên có sẵn trong đó từ bên ngoài (và các chi tiết của triển khai bên trong có thể và nên được ẩn). Trong trường hợp của chúng ta, tệp như vậy được gọi là MQL5Book/LibRand.mqh
. Cụ thể, nó mô tả các kiểu do người dùng định nghĩa (trong trường hợp của chúng ta, liệt kê STRING_PATTERN) và nguyên mẫu hàm.
Mặc dù cú pháp chính xác của khối #import
chưa được chúng ta biết, điều này không nên ảnh hưởng đến sự rõ ràng của các khai báo bên trong nó: các tiêu đề của các hàm được xuất được lặp lại ở đây nhưng không có từ khóa export
.
enum STRING_PATTERN
{
STRING_PATTERN_LOWERCASE = 1, // lowercase letters only
STRING_PATTERN_UPPERCASE = 2, // capital letters only
STRING_PATTERN_MIXEDCASE = 3 // both registers
};
#import "MQL5Book/LibRand.ex5"
string StringPatternAlpha(const STRING_PATTERN _case = STRING_PATTERN_MIXEDCASE);
string StringPatternDigit();
string RandomString(const int length, string pattern = NULL);
void RandomStrings(string &array[], const int n, const int minlength,
const int maxlength, string pattern = NULL);
void PseudoNormalDefaultMean(const double mean = 0.0);
void PseudoNormalDefaultSigma(const double sigma = 1.0);
double PseudoNormalDefaultValue();
double PseudoNormalValue(const double mean = 0.0, const double sigma = 1.0,
const bool rooted = false);
bool PseudoNormalArray(double &array[], const int n,
const double mean = 0.0, const double sigma = 1.0);
#import
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Chúng ta sẽ viết một kịch bản thử nghiệm sử dụng thư viện này trong phần tiếp theo, sau khi nghiên cứu chỉ thị #import
.