创建 ex5 库;函数 export
要说明一个库,请将 #property library 指令添加到主模块(已编译)的源代码中(通常在文件的开头)。
#property library |
在编译过程中,通过 #include 在任何其他文件中指定该指令将无效。
library 特性通知编译器给定的 ex5 文件是一个库:在 ex5 文件的头文件中存储了一个标记。
在 MetaTrader 5 中为库保留了一个单独的文件夹 MQL5/Libraries。可以在其中组织嵌套文件夹的层级,就像 MQL5 中对其他类型程序所做的操作一样。
库不直接参与事件处理,因此编译器不要求代码中存在任何标准处理程序。但是,您可以从该库所连接的 MQL 程序的事件处理程序中调用该库的导出函数。
要从库中导出一个函数,只需用一个特殊的 export 关键字。该修饰符必须放在函数头文件的最末端。
result_type function_id ( [ parameter_type parameter_id
|
参数必须是简单类型或字符串、具有这种类型的字段的结构或它们的数组。MQL5 对象类型允许指针和引用(有关导入 DLL 的限制,参见 相关章节)。
让我们来分析一些例子。参数是一个质数:
double Algebraic2(const double x) export
|
这些参数是指向一个对象的指针和指向一个指针的引用(允许您在函数内部分配一个指针)。
class X
|
参数是一个结构体:
struct Data
|
只能导出函数,而不能导出整个类或结构。在指针和引用的帮助下,可以避免这些限制,我们将在后面详细讨论有关内容。
不能使用 export 关键字和 #import 指令声明函数模板。
export 修饰符指示编译器将该函数包含在给定的 ex5 可执行文件的导出函数表中。借助这个功能,这类函数可以从其他 MQL 程序中获得(“可见”),使用特殊指令 #import 导入之后,它们就可以使用了。
所有要导出的函数都必须用 export 修饰符标记。主程序不需要导入所有这些函数,因为它只能导入必要的函数。
如果您忘记导出一个函数,但将它包含在主 MQL 程序的导入指令中,那么当后者启动时,将会出现一个错误:
cannot find 'function' in 'library.ex5'
|
如果导出函数和它的导入原型的描述存在差异,也会出现类似问题。例如,如果您在更改编程接口(通常在单独的头文件中说明)后忘记重新编译库或主程序,就会发生这种情况。
无法对库进行调试,所以如果有必要,应该有一个帮助脚本或另一个 MQL 程序,它是在调试器模式下从库的源代码构建的,并且可以通过断点或分步执行。当然,这将需要使用一些真实或人工数据来模拟对导出函数的调用。
对于 DLL,导出函数以不同方式描述,具体取决于创建它们的编程语言。详细情况,请查询您选择的开发环境的文档。
我们以一个简单库 MQL5/Libraries/MQL5Book/LibRand.mq5 为例进行分析,从这个库中导出了几个函数,它们具有不同类型的参数和结果。该库设计用于生成随机数据:
- 具有伪正态分布的数值数据
- 具有来自给定集的随机字符的字符串(可能适用于密码)
特别是,可以使用 PseudoNormalValue 函数获得一个随机数,其中期望值和方差被设置为参数。
double PseudoNormalValue(const double mean = 0.0, const double sigma = 1.0,
|
PseudoNormalArray 函数用给定数量 (n) 的随机值填充数组,并具有所需的分布。
bool PseudoNormalArray(double &array[], const int n,
|
为了生成一个随机字符串,我们编写了 RandomString 函数,它从提供的字符集 (pattern) 中“选择”给定数量 (length) 的任意字符。当 pattern 参数为空(默认)时,假定为一组完整的字母和数字。辅助函数 StringPatternAlpha 和 StringPatternDigit 用于获取这些字母和数字;这些函数也是可导出函数(本书未列出,请参见源代码)。
string RandomString(const int length, string pattern = NULL) export
|
一般来说,要使用一个库,必须发布一个头文件,说明可以从外部获得的所有内容(内部实现的细节可以而且应该被隐藏)。在我们的例子中,该文件被称为 MQL5Book/LibRand.mqh。特别是,它说明了用户定义的类型(在我们的例子中是 STRING_PATTERN 枚举)和函数原型。
虽然我们还不知道 #import 块的确切语法,但这不应该影响它内部声明的清晰性:这里重复了导出函数的头文件,但没有使用 export 关键字。
enum STRING_PATTERN
|
探讨了 #import 指令之后,我们将在下一节编写一个使用该库的测试脚本。