使用代码生成器模板生成Engee函数的代码
动机
Engee允许您使用[Engee块函数]在模型中使用自定义代码(https://engee.com/helpcenter/stable/ru/base-lib-user-defined-function/engee-function.html但是,在这种情况下,代码生成将是不可能的,因为代码生成器将无法"理解"用户的代码。 为源代码生成器编写模板可以解决此问题。 作为一个例子,让我们考虑从ef_adder模型为Engee功能块编写一个模板。
AddDemo模块实现了两个输入的加法。 块输入是固定大小的向量,长度为16。 此外,我们将通过提出使用矢量计算的条件来完成任务。
我们将通过为源代码生成器编写模板来解决问题。
源代码生成器模板的解剖
源代码生成器模板分为两类:
*块的模板
*Main()函数的模板
在这个例子中,我们只处理块的模板。
块的模板有*。cgt扩展,并以类似的评论开始
``'cpp
/*!
BlockType=:EngeeFunction!AddDemo
TargetLang=:C
*/
BlockType是可以从其参数中获得的块的类型。
TargetLang是目标语言
模板代码本身如下。 模板包含目标语言的代码,用特殊注释和宏标记。 这些注释和宏包含**Julia**语言(所谓的宿主语言)中的代码。
## 矢量化加法的模板
要查看文本文件的内容,请创建一个宏:
macro showfile(file::String)
    f = open(file)
    s = read(f, String);
    println(s);
    close(f)
end
现在我们可以查看模板代码。:
@showfile "add_vectors.cgt"
可以看到,模板被分成使用像这样的结构的部分 //! @
这些宏提供了一种机制,用于指定将模板中的源代码插入到生成代码的相应部分中的位置。
所以,使用宏 //! @Toplevel 我们告诉代码生成器,下面的所有代码,直到下一个宏,将被放置在模型头文件的开头。
让我们注意模板的以下几行。 它们很有趣,因为它们演示了宿主语言对模板的调用。
例如,如果该行以 //! 然后这个字符串将被识别为宿主语言中的代码。:
//! vector_width=convert(Int32,prod(input_size(1))*(input(1).泰位)/8)
使用这样的注释,您可以计算一个常数,以便在代码中进一步使用。:
打字,打字 $(input_datatype_name(1)) $vType__attribute__((vector_size($(vector_width))));
Чтобы встроить результаты работы некоторой функции или значение переменных в сгенерированном коде мы оборачиваем вызов функции или переменной в конструкцию вида $()
与此同时,如果有必要用宿主语言编写多行代码,那么我们可以进行多行注释。 例如:
``'茱莉亚
/*!
如果isempty(input_size(1))
sz=1;
其他
sz=input_size(1)
结束
*/
模板代码显示函数调用,如 \`output\_datatype\_name(1)\` -这些是对特殊模板函数的调用,允许您使用输入,输出和块状态的属性。
## 使用模板生成代码
为了应用我们的模板,我们需要将其文件夹添加到Engee搜索路径。:
demoroot = @__DIR__
engee.addpath(demoroot)
然后,我们将使用engee命令生成代码。生成_code:
modelName = "ef_adder"
engee.generate_code(joinpath(demoroot,"$(modelName).engee"),
                    joinpath(demoroot,"vector_code"),
                    target = "c"
                    )
我们来看看生成的代码:
@showfile "vector_code/ef_adder.c"
可以看出,代码从各节 @Step 和  @Defenitions 被内置到函数中 step() 模特。 模型头文件也是如此。:
@showfile "vector_code/ef_adder.h"
让我们检查生成的代码是否可以在没有错误的情况下组装。:
;gcc -shared -fPIC ./vector_code/ef_adder.c -I ./vector_code -march=native -O3
现在让我们确保Engee中的块和生成的代码以相同的方式工作。 为此,我们将创建加法器_ccall测试模型,并将C函数块放入其中,该函数块调用生成的代码。 然后我们将建立一个测试模型。:
 
对于测试,不要忘记设置C功能块并保存模型。:
mdl = engee.load("adder_ccall.engee")
engee.set_param!("adder_ccall/C Function","SourceFiles"=>"$(demoroot)/vector_code/ef_adder.c","IncludeDirectories"=>"$(demoroot)/vector_code");
engee.save(mdl,"adder_ccall.engee";force=true)
engee.close(mdl;force=true)
运行模型后,我们将获得输出的值:
 
可以看出它们匹配,这意味着代码和模型的工作方式相同。
在工作结束时,不要忘记将搜索路径返回到原始状态。:
engee.rmpath(demoroot)
结论和后续步骤
源代码生成器模板是一个功能强大的源代码自定义工具,允许您生成任何代码,包括使用编译器扩展。 但是,由于用户可以完全控制代码生成的逻辑,因此用户有责任确保模板是正确的。