What is the best way to generate the generated code

I appreciate the use of code generation for my flight simulation project. In particular, there is a requirement to allow the "average engineer" (without any wrongdoing myself) to define differential equations that describe a dynamic system in a more natural syntax than C ++. The idea is to develop an abstract descriptor language that can be easily understood and edited to generate C ++ code. This descriptor is provided by the modeling engineer and is used by those who implement and maintain the simulation environment to generate code.

I have something like this:

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 

It is important to maintain the flexibility of the C language, so the descriptor language is intended only to define certain parts of the definition and implementation of the model class. Thus, one engineer provides the model from the descriptor language, as illustrated above, and the maintenance engineer will add all the code to read the parameters from the files, start / stop / pause the simulation, and how a specific object is created.

First of all, I want to generate two files from the descriptor file: one .h file containing declarations and one .cpp file containing the implementation of certain functions. Then they should be # included in the appropriate places

 [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" 

Any thoughts or suggestions?

EDIT1:

I would like to clarify that there is a structure for expressing differential equations and (in real time) modeling a dynamic system. The problem is that most engineers with knowledge of the field are very skeptical about using C ++. In fact, we would like to make it easier for them to contribute (mathematical formulation), while maintaining the flexibility of C ++.

Of course, it is possible to use the MEX compiler to generate C code from MATLAB / Simulink models, but we would like to avoid the corresponding license fees.

Refactoring everything to use a common scripting language is likely to go beyond the capabilities of the workforce. In addition, this, as far as I can understand now and on the fly, requires engineers to also learn the scripting language. I am not yet convinced that this will be simpler than the syntax above.

EDIT2: accepted answer

I agree with Richard Harrison's answer because he shared his experience with custom code generators. I will also consider hints at scripting interfaces, although I am on the sidelines. I will not find time to implement all this as part of my dissertation. However, these pointers can help convince my colleagues to consider a more consistent class hierarchy. Many thanks!

+4
source share
3 answers

As a rule, I always found on the basis of code generators that I wrote that code generators are best avoided, since they are usually inconvenient, and an artificial limitation is difficult to implement. In fact, the code generator will grow beyond its original intentions.

It’s wise to realize that if you really don’t want to develop and implement a new language specifically designed for your domain, it’s best for you to find a solution using existing technologies.

I know that it seems that the code generator will simplify the work, preserve typing and, as a rule, will be wonderful, but realistic is too optimistic.

If I did this in C ++, I would first spend some time creating a consistent object model, perhaps using the approach made using JSBSim to have the model contained in the XML file.

So, after your editing, I would say that it is best to spend time creating a clean model, some well-documented examples and some preparation.

The following is an example of a rough prototype model. Perhaps this is not so important if you are editing, but, as I just spent putting it together, I decided that I could publish it.

 #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
source

The problem with this approach is that any errors in the mathematical syntax will be detected only when the code is really compiled by the C ++ compiler. At this point, it is very difficult to give good error messages that relate to specific error strings in the specification language. Therefore, if your specification language processor also does not perform syntactic and semantic checks on the specification (i.e., it is at least a partial compiler for the specification language), I would not use this approach.

+2
source

I would go the other way. Implement all primitive types, composition functionality, crawl, modeling and flow control functions in C / C ++ and add a rich interactive shell.

This can be done with minimal effort using SWIG . It adds a scripting interface to the application, and many scripting languages ​​are supported.

With a simulator with a rich shell interface, engineers (not software developers) with domain knowledge can easily prototype scripts to simulate / simulate, debug and configure these scripts, and then, if performance is not enough, the bottleneck code can be easily transferred by the owner of the software providing from scripts to native C / C ++ code.

This approach is used in most EDA systems (Cadence, Synopsys, etc.), which should simulate systems s> 10e9 units in the model and have proven to be the best solution for CAD software. In fact, it is almost never necessary to rewrite scripts in their native language, because most of the time is spent on solvers of systems of differential equations implemented in native languages.

Addition: You can look at the tutorial , explaining how to enable the scripting interface in a C ++ program. I would choose TCL , because it is a very simple, but sufficient scripting language, all commands can fit on one page. There should not be any problems with the teachers of engineers, and you can always document only a small set of functions to mimic the original syntax example.

Your simulation script will look below

 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 
+1
source

Source: https://habr.com/ru/post/1311992/


All Articles