外部函数¶
external
¶
在插值章节,我们讨论了InterpolateExternalVector
函数相关的例子。我们了解到可以通过Modelica语言来调用由其他语言定义的函数。通常,这些函数都是用C或Fortran语言来定义的。使用Modelica之外的语言所定义的函数不包含algorithm
区域。相反的,函数应该包括external
语句,以提供外部函数的信息以及如何实现与外部函数的信息传递。
对于外部函数的最低要求是需包含关键字external
,即:
external;
在这里,我们假设外部函数是由C语言定义的。而且,函数的名称需与Modelica语言的“包装”函数相匹配。最后,函数的参数必须以相同的顺序传递给Modelica函数的input
参数。
让我们考虑一个稍微复杂的情况,例如在插值例子中展示的VectorTable
类型:
function destructor "Release storage"
input VectorTable table;
external "C" destroyVectorTable(table)
annotation(IncludeDirectory="modelica://ModelicaByExample.Functions.Interpolation/source",
Include="#include \"VectorTable.c\"");
end destructor;
我们可以看到,此函数定义的语言被明确的指定为"C"
。还有另外两种可以实现上述功能的语言:"FORTRAN 77"
和"builtin"
。对"builtin"
使用感兴趣的主要是一些Modelica工具厂商。
我们看到,上述语句已经明确指定函数的名称。external
语句内的destroyVectorTable(table)
这部分明确指定了与外部函数传递的参数及其顺序。
有些情况下,还需明确定义传递给外部函数的参数值及输出变量和函数调用结果之间的映射关系。我们可以在下述function
定义中,看到上述信息:
function constructor
input Real ybar[:,2];
output VectorTable table;
external "C" table=createVectorTable(ybar, size(ybar,1))
annotation(IncludeDirectory="modelica://ModelicaByExample.Functions.Interpolation/source",
Include="#include \"VectorTable.c\"");
end constructor;
在这个例子中,外部函数需要获取ybar
数组的大小。因为这项信息没有直接地传递给外部函数。此外,上述函数还声明了createVectorTable
的计算结果应该分配给output
变量table
。很明显,我们可以看到C函数的返回值就是Modelica语言定义的函数返回值。但是,有些情况下,output
变量应该作为参数传递给函数。接下来我们就会看到,在这种情况下,外部函数可以使用指针完成相应变量的赋值。
数据映射¶
C语言¶
下面这张表展示了将参数传递给外部函数时,Modelica语言与C语言数据类型之间的映射关系。
Modelica语言 |
C语言(输入变量) |
C语言(输出变量) |
Real |
double |
double * |
Integer |
int |
int * |
Boolean |
int |
int * |
String |
const char * |
const char ** |
T[d1] |
T' * , size_t d1 |
T' * , size_t d1 |
T[d1,d2] |
T' * , size_t d1 , size_t d2 |
T' * , size_t d1 , size_t d2 |
T[d1,...,dn] |
T' * , size_t d1 , ..., size_t dn |
T' * , size_t d1 , ..., size_t dn |
size(...) |
size_t |
N/A |
enumeration |
int |
int * |
record |
struct * |
struct * |
我们简单解释一下上述表格。首先,它假设所有的字符串都是以空字符(\0
)终止的。另外,数组类型T'
在C语言与Modelica语言间可以相互映射(使用相同的表)。最后,Modelica语言的record
类型对应C语言的struct
类型。而且struct
类型与record
类型的数据成员顺序一致。record
成员的数据类型使用该表格第二列的映射关系(即把它们当做输入参数)。
对于C函数的返回值类型,采用下表的类型映射关系:
Modelica语言 |
C语言 |
Real |
double |
Integer |
int |
Boolean |
int |
String |
const char * |
T[d1] |
T' * , size_t |
T[d1,d2] |
T' * , size_t d1 , size_t d2 |
T[d1,...,dn] |
T' * , size_t d1 , ..., size_t dn |
size(...) |
size_t |
enumeration |
int |
record |
struct * |
Fortran语言¶
如果你需要调用Fortran语言定义的函数或子程序,采用下表定义的类型映射关系:
Modelica语言 |
Fortran语言 |
Real |
DOUBLE PRECISION |
Integer |
INTEGER |
Boolean |
LOGICAL |
T[d1] |
T' , INTEGER |
T[d1,d2] |
T' , INTEGER d1 , INTEGER d2 |
T[d1,...,dn] |
T' , INTEGER d1 , ..., INTEGER dn |
size(...) |
INTEGER |
enumeration |
INTEGER |
对于上述表格,有两点需特别注意。首先,字符串和记录类型没有相应的映射关系。其次,Fortran语言语法是传递引用,所有输入、输出这些函数的变量都被假定为指针。出于这个原因,Fortran函数不会区分获取的变量是Modelica函数的输入还是输出。
特殊函数¶
在与Modelica交互运行时,有许多特殊函数可以从外部函数进行调用。下面对这些函数的名称、原型以及开发的目的进行详细的介绍。
ModelicaVFormatMessage
¶
void ModelicaVFormatMessage(const char*string, va_list);
此函数的输出与C函数vprintf
相同格式的消息。
ModelicaError
¶
void ModelicaError(const char* string);
此函数主要处理类似Modelica代码的声明错误,输出错误信息的字符串(无输出格式控制),对于调用函数无返回值。
ModelicaFormatError
¶
void ModelicaFormatError(const char* string, ...);
此函数主要处理类似Modelica代码的声明错误,输出与C函数printf
相同格式的错误信息,对于调用函数无返回值。
ModelicaVFormatError
¶
void ModelicaVFormatError(const char* string, va_list);
此函数主要处理类似Modelica代码的声明错误,输出与C函数vprintf
相同格式的错误信息,对于调用函数无返回值。
ModelicaAllocateString
¶
char* ModelicaAllocateString(size_t len);
此函数主要用于为外部Modelica函数的返回参数分配内存。需注意的是,该字符串数组的存储空间(即指向字符串数组的指针)仍然由被调用函数提供。当出现错误时,该函数不返回主函数,而是调用ModelicaError函数输出错误信息。
ModelicaAllocateStringWithErrorReturn
¶
char* ModelicaAllocateStringWithErrorReturn(size_t len);
该函数功能和ModelicaAllocateString函数几乎一样。不同点只是在出现错误时,函数会返回0值。当出现错误时,此功能允许外部函数关闭文件并清空其他的资源。完成资源清理以后,调用ModelicaError函数和ModelicaVFormatError函数输出错误信息。