外部函数

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函数输出错误信息。