Как наилучшим образом интегрировать сгенерированный код - PullRequest
4 голосов
/ 06 июня 2010

Я оцениваю использование генерации кода для моего проекта симуляции полета. Более конкретно, существует требование, чтобы «средний инженер» (без обид я сам) определял дифференциальные уравнения, которые описывают динамическую систему в более естественном синтаксисе, чем 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, поэтому язык дескрипторов предназначен только для определения определенных частей определения и реализации класса модели. Таким образом, один инженер предоставляет модель на языке дескриптора, как показано выше, а инженер по техническому обслуживанию добавит весь код для чтения параметров из файлов, запускает / останавливает / приостанавливает выполнение симуляции и способ создания конкретного объекта. 1006 *

Сначала я должен сгенерировать два файла из файла дескриптора: один файл .h, содержащий объявления, и один файл .cpp, содержащий реализацию определенных функций. Затем они должны быть # включены в соответствующих местах

[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-компилятор для генерации C-кода из моделей MATLAB / Simulink, но мы хотели бы избежать связанных с этим лицензионных сборов.

Рефакторинг всего этого для использования общего языка сценариев, вероятно, выходит за рамки с точки зрения рабочей силы. Кроме того, для этого, насколько я могу понять сейчас и на лету, инженеры также должны изучить язык сценариев. Я еще не уверен, что это будет проще, чем приведенный выше синтаксис.

РЕДАКТИРОВАТЬ 2: Принятый ответ

Я принимаю ответ Ричарда Харрисона из-за того, что он поделился своим опытом с пользовательскими генераторами кода. Я также рассмотрю подсказки, касающиеся скриптовых интерфейсов, хотя я не могу найти время, чтобы реализовать все это как часть моей диссертации. Тем не менее, эти указатели могут помочь убедить моих коллег рассмотреть более последовательную классовую иерархию. Большое спасибо!

Ответы [ 3 ]

2 голосов
/ 06 июня 2010

Проблема этого подхода заключается в том, что любые ошибки в математическом синтаксисе будут обнаруживаться только тогда, когда код фактически компилируется компилятором C ++. В этот момент очень трудно дать хорошие сообщения об ошибках, которые относятся к конкретным ошибочным строкам на языке спецификации. Поэтому, если ваш процессор языка спецификаций также не выполняет синтаксические и семантические проверки спецификации (т. Е. Это, по крайней мере, частичный компилятор для языка спецификаций), я бы не стал использовать этот подход.

1 голос
/ 06 июня 2010

Как правило, я всегда обнаруживал, основываясь на написанных мной генераторах кода, что генераторов кода лучше избегать, так как они имеют тенденцию быть сложными, сложными для реализации и искусственными ограничениями. По сути, генератор кода выйдет за рамки оригинальных намерений.

Целесообразно осознавать, что если вы действительно не хотите разрабатывать и внедрять новый язык, специально адаптированный для вашего домена, вам лучше всего найти решение с использованием уже существующих технологий.

Я знаю, что кажется, что генератор кода облегчит задачу, сохранит набор текста и в целом будет замечательным, но на самом деле это слишком оптимистично.

Если бы я делал это на 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(); // 
    }
}
1 голос
/ 06 июня 2010

Я бы пошел другим путем. Реализуйте все примитивные типы, функциональность композиции, функции обхода, моделирования и управления потоком в C / C ++ и добавьте многофункциональную интерактивную оболочку.

Это можно сделать с минимальными усилиями, используя SWIG . Он добавляет в приложение интерфейс сценариев и поддерживается множество языков сценариев.

Имея симулятор с богатым интерфейсом оболочки, инженеры (не инженеры-программисты) со знанием предметной области могут легко создавать прототипные сценарии для моделирования / моделирования, отлаживать и настраивать эти сценарии, а затем, если производительности недостаточно, код узкого места может легко переносится владельцем программного обеспечения из сценариев в собственный код C / C ++.

Этот подход используется в большинстве систем EDA (Cadence, Synopsys и т. Д.), Имитирующих системы с> 10e9 единицами в модели, и оказался лучшим решением для программного обеспечения CAD. На самом деле почти никогда не требуется переписывать сценарии на родном языке, потому что больше всего времени уходит на решатели систем дифференциальных уравнений, реализованные на родных языках.

Сложение: Вы можете взглянуть на учебное пособие , объясняющее, как включить интерфейс сценариев в программе 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
...