如何最好地集成生成的代码

时间:2010-06-06 09:11:52

标签: c++ code-generation

我正在评估我的飞行模拟项目中代码生成的使用。更具体地说,需要允许“普通工程师”(我自己没有冒犯)来定义以比C ++提供的更自然的语法描述动态系统的微分方程。我们的想法是设计一种抽象的描述语言,可以很容易地理解和编辑它来生成C ++代码。该描述符由建模工程师提供,并由实现和维护模拟环境的人员使用以生成代码。

我有这样的想法:

model Aircraft has
   state x1, x2;     
   state x3;
   input double : u;
   input bool : flag1, flag2;
   algebraic double : x1x2;
   model Engine : tw1, tw2;
   model Gear : gear;
   model ISA : isa;
   trim routine HorizontalFight;
   trim routine OnGround, General;
   constant double : c1, c2;
   constant int : ci1;
begin differential equations
   x1' = x1 + 2.*x2;
   x2' = x2 + x1x2;
begin algebraic equations
   x1x2 = x1*x2 + x1';
end model

保留C语言的灵活性非常重要,因此描述符语言仅用于定义模型类的定义和实现的某些部分。这样,一名工程师从描述语言中提供模型,如上所示,维护工程师将添加所有代码以从文件中读取参数,启动/停止/暂停模拟的执行以及具体对象如何实例化。 / p>

我的第一个是从描述符文件生成两个文件:一个包含声明的.h文件和一个包含某些函数实现的.cpp文件。然后需要在适当的地方#include

[File Aircarft.h]

class Aircraft
{
public:
    void Aircraft(..); // hand-written constructor
    void ReadParameters(string &file_name); // hand-written

private:
    /* more hand wirtten boiler-plate code */

    /* generate declarations follow */
    #include "Aircraft.generated.decl" 

};

[File Aircraft.cpp]

Aircraft::Aircraft(..) { /* hand-written constructor implementation */ }

/* more hand-written implementation code */

/* generated implementation code follows */
#include "Aircraft.generated.impl"

有任何想法或建议吗?

EDIT1:

我想澄清一下,存在一个表达动态系统的微分方程和(实时)模拟的框架。问题是大多数具有领域知识的工程师对C ++的使用非常犹豫。实际上,我们希望为他们提供更轻松的方式来贡献他们的部分(数学公式),同时保留C ++的灵活性。

当然可以使用MEX编译器从MATLAB / Simulink模型生成C代码,但我们希望避免相关的许可费用。

重构整个事务以使用通用脚本语言可能超出了可用的劳动力范围。此外,就我现在和飞行中所掌握的而言,这也需要工程师学习脚本语言。我还不相信这比上面例举的语法更容易。

EDIT2:接受的答案

我接受Richard Harrison给出的答案,因为他与自定义代码生成器分享了他的经验。我还将考虑有关脚本接口的提示,尽管我处于困境中,我将没有时间将所有这些作为我的论文的一部分来实现。仍然这些指针可能有助于说服我的同事考虑一个更连贯的阶级的heriarchy。非常感谢!

3 个答案:

答案 0 :(得分:2)

这种方法的问题是,只有在C ++编译器实际编译代码时,才会检测到数学语法中的任何错误。此时,很难提供与规范语言中的错误特定行相关的良好错误消息。因此,除非您的规范语言处理器还对规范执行语法和语义检查(即它至少是规范语言的部分编译器),否则我不会采用这种方法。

答案 1 :(得分:1)

我会采取另一种方式。在C / C ++中实现所有原始类型,组合功能,遍历,模拟和流控制功能,并添加丰富的交互式shell。

使用SWIG可以轻松完成此操作。它为应用程序添加了脚本界面,并支持许多脚本语言。

拥有丰富shell接口的模拟器,具有领域知识的工程师(而不是软件工程师)可以轻松创建用于仿真/建模的原型脚本,调试和调整这些脚本,然后,如果性能不够,瓶颈代码可以可以由软件所有者轻松地从脚本移植到本机C / C ++代码。

这种方法在大多数EDA系统(Cadence,Synopsys等)中使用,必须模拟模型中具有> 10e9单位的系统,并且已被证明是CAD软件的最佳解决方案。事实上,几乎不需要将脚本重写为母语,因为大部分时间都花在以本机语言实现的微分方程系统求解器上。

此外: 您可以查看tutorial,了解如何在C ++程序中启用脚本接口。我会选择TCL,因为它是一种非常简单但足够的脚本语言,所有命令都可以放在一个页面上。教学工程师应该没有任何问题,您只能记录一小部分功能,以模仿原始语法示例。

您的模拟脚本如下所示

package require my_models ;# load and initialize models
package require my_preconditioners ;# load and initialize preconditioners
package require my_solvers ;# load and initialize solvers
package require my_simulators ;# load simulators

set mdl [model_create "boeing_777_new"] ;# create jet model
set wing [model_load "ceramic_112233_long" "/mnt/proj/777/wing.models"] ;# load wings models

model_set_param $wing "paint" "silver_xx_233445" ;# change some parameter

model_add_element $mdl "wing_left" [model_new_instance $wing] #; instantiate a wing and add it to the jet, left
model_add_element $mdl "wing_right" [model_new_instance $wing] #; instantiate a wing and add it to the jet, right

set sim [simulator_load "simplified_linear_air_flow" "/mnt/proj/777/worlds.xml"] #; load some linear simulator with predefined parameters from an xml file

simulator_add_object [model_new_instance $mdl] ;# instantiate a jet in the simulator

simulator_set_param $sim "altitude" 12000
simulator_set_param $sim "temperature" -54

simulator_set_output "/tmp/sim.dmp"
simulator_run "10 sec"

exit

答案 2 :(得分:1)

一般来说,我总是根据我编写的代码生成器找到代码生成器最好避免使用,因为它们往往是繁琐,难以实现和人为限制。实际上,代码生成器将超越原始意图。

明智的做法是,除非您真的想要设计和实施专门针对您的领域量身定制的新语言,否则您最好使用预先存在的技术找到解决方案。

我知道似乎代码生成器会让事情变得更容易,节省打字并且通常很棒,但实际上这太乐观了。

如果我在C ++中这样做,我首先会花一些时间设计一个连贯的对象模型,可能使用JSBSim方法将模型包含在XML文件中。

因此,按照您的编辑,我会说最好花时间建立一个干净的模型,一些很好的文档示例和一些培训。

以下是用于说明的粗略原型模型。这可能与你的编辑无关,但由于我花了一段时间把它放在一起,我想我也可以发布它。

#include <string>
using namespace std;

//
// Fundamental element of simulation - an ExecModule is a concise unit of simulation work. It will
// be called by the main real time executive at the frequency specified by the getExecRate() method.
// Communication between modules is either via datapool, or by using BusMessages.
class ExecModule
{
public:
    virtual bool initialise(long time_ms) = 0;
    virtual long run(long ms)  = 0;
    virtual long getExecRate()  = 0;
    virtual string getModuleDescription() = 0;
}

class GeoCoordinate
{
public:
    GeoCoordinate(double lat, double lon, double alt);
};

class Model
{
public:
    virtual void DifferentialEquations() = 0;
    virtual void AlgebraicEquations() = 0;

};
class State
{
public:
    Value Prime();
    Prime(Value &v);
    State operator *(State c);
}

class AircraftModel : public ExecModule, public Model
{
private:
    State x1, x2;
    State x3;
    InputDouble u;
    InputBool flag1, flag2;
    AlgebraicDouble  x1x2;
    Model tw1, tw2; // engine
    Model gear;
    Model isa;
    TrimRoutine HorizontalFight;
    TrimRoutine OnGround, General;
    ConstantDouble c1, c2;
    ConstantInt : ci1;

public:
    AircraftModel()
    {
    }

public:
    virtual void DifferentialEquations()
    {
        x1.Prime(2.0*x2);
        x2.Prime(x1x2);
    }

    virtual void AlgebraicEquations()
    {
        x1x2 = x1 * x2 + x1.Prime();
    }

public: // Required for ExecModule
    string getModuleDescription()
    {
        return "Aircraft Model";
    }

    long getExecRate()
    {
        return 33L;//ms (30hz)
    }

    long run(long ms)
    {
        return 0L;
    }

    bool initialise(long time_ms)
    {
        // called by the Exec (in sequence) when initialisation is required.
    }

};

class SimLoad
{
public:
// exec modules to load
    class Model *aircraft_model;
    class Model *engine_model;
    class Model *aerodynamics_model;
    class GPSSimulator *gps;
    class FeaturesDataProvider *runways;
    class ArincDB *arincDB;
    class ExecSystem *execSystem;

    SimLoad()
    {
        engine_model = new EngineModel();
        aerodynamics_model = new AeroDynamicsModel();
        aircraft_model = new AircraftModel();
        arincDB = new ArincDB();
        gps = new GPSSimulator();

        // ensure that the simulated systems are loaded in the correct
        // sequence. Notice that the exec system provides two schedulers which
        // we select manually to allow for threading. Each thread within the exec is 
        // synchronised at the start of each frame (iteration) however within each frame
        // each thread is free running so care needs to be taken when scheduling dependant
        // modules across different threads.
        execSystem.scheduler.addModule(engine_model);
        execSystem.scheduler.addModule(aerodynamics_model);
        execSystem.scheduler.addModule(aircraft_model);
        execSystem.scheduler1.addModule(gps);

        runways = new ArincRunwayProvider(arincDB);

        execSystem.start(); // 
    }
}