Nasal for C++ programmers

From FlightGear wiki
Revision as of 13:09, 17 August 2012 by Hooray (Talk | contribs) (Created page with "== Some words on Nasal for fellow C++ programmers == Compared to C++, there is really nothing "low quality" about Nasal per se: Nasal is just the "script glue" that connects ...")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Some words on Nasal for fellow C++ programmers

Compared to C++, there is really nothing "low quality" about Nasal per se: Nasal is just the "script glue" that connects different parts of the simulator: Many Nasal scripts leverage C++ code - and it is very easy to add new C++ code that can be called from Nasal. So this is a perfect option to combine the speed of C++ with the flexibility of scripting.

History has shown, that most code in FlightGear will eventually be made more configurable and more accessible, this usually happens in the same steps:

  • replacing static variables with variables stored in the property tree
  • using listeners and callbacks to get update notifications for important variables
  • fully exposing a "control" interface by making it accessible in the property tree (see e.g. the AI system)
  • adding new fgcommands
  • providing new scripting hooks

Even if you should know C or C++ already, Nasal probably remains the most accessible and the most powerful method for customizing the simulator in the beginning, simply because it is extremely easy and fast to get started, you don't need an "integrated development environment", you don't need to install compilers and you don't need to satisfy any 3rd party dependencies; bottom line being: if you can run FlightGear, you can also run Nasal and directly create new code.

In addition, Nasal code is fairly abstract code, too. Once you start looking at some existing Nasal scripts, you will see that it is also fairly high level code, much more high level than C++ - so Nasal has a much higher density of code, too. Nasal's role in FlightGear really is like JavaScript's role in Firefox, where it is also used for many/most core-related logics (CSS/XUL).

Nasal is an extension language, a scripting language that is much more high level than C++ and specifically meant to be used for extending a host application (FlightGear). C++ on the other hand is a systems programming language (most APIs in Nasal are FlightGear specific).

Nasal's power is due to the fact that it is not as low level as C++ code, and that it is very easy for non-programmers to get started adding custom features to FlightGear, without requiring a background in software development or programming.

Aircraft developers or other base package contributors don't need to be familiar with C++ programming, they don't even need an IDE or a working build environment. They can just program scripts in a text editor and drop them into the base package, and things will simply work. That is because FlightGear is the runtime environment for Nasal scripts.

Obviously, an experienced programmer can make use of more complex Nasal features.

Basically, there really is nothing in either language that you cannot also do in the other language - ever heard of "turing completeness"?

Of course, some things are better handled in C++ space, while others are better (=more easily) done in Nasal.

It really boils down to what needs to be done, usually new features can be implemented (or at least prototyped) with just some Nasal code, sometimes new Nasal functions may be required to interface to the rest of FlightGear.

Some words on plugins

Experienced C++ developers may be looking for an SDK to create plugins for FlightGear. This is a discussion that has been brought up frequently by new FlightGear users who would like to create new modules for FlightGear.

FlightGear doesn't have such an SDK, and the general consensus is that FlightGear doesn't need an SDK for creating binary plugins. FlightGear is an open source flight simulator, developers are invited to directly contribute to FlightGear itself.

While adding support for binary plugins is of course possible, and not even that complicated, it would be a pretty bad idea for a number of reasons:

FlightGear supports a fairly powerful (and high level) scripting language to provide extensibility - dynamically linking to binary plugins (DSOs, DLLs) however is highly platform specific and has also many other disadvantages (e.g. mutual library dependencies and versioning requirements).

Not to mention, that plugin developers need to be able to build/compile their modules before using them. Nasal scripts on the other hand "just work" by dropping them into the base package folder.

Instead, FlightGear is usually extended by either using its support for Nasal scripting or by adding new C++ code to the source tree, or a combination of both approaches (so that Nasal scripts can call C++ code).

Occasionally, plugin developers for commercial flight simulators such as MSFS or X-Plane highlight how important such a plugin system is. But the truth is, it is only since fairly recently, that there are discussions going on in the X-Plane community to also add scripting support to their simulator, ironically by the use of a python plugin specifically for scripting:

Cquote1.png What we need is a scripting system. The scripting system would provide a relatively simple text-file syntax to do simple scripting of systems and instruments for airplanes.
— X-Plane Scenery Blog[1]
Cquote2.png

Now let's look at this decision: They have like how many years experience working with binary plugins? How come they still intend to support scripting, probably because it's the better solution!

So, in this aspect, FlightGear is really years ahead of the other two large flight simulators, both of which provide fairly limited script-ability.

Also, you cannot circumvent the requirements of the GPL 2, so any binary modules (plugins) linked into GPL'ed software, need to be GPL'ed too - which requires the corresponding source code to be also distributed alongside with any binary releases.

If you want to add new features to FlightGear, the usual way is to add a new subsystem - for an example of how this is done in C++, you may want to search the FG forums for the discussion on "ridge lift" that took place a while ago which shows how the fellow forum member "WooT" implemented support for ridge lift by adding a new subsystem to FlightGear, see these threads for details:

Related discussions:

Performance

Obviously, C++ code will usually be faster than the corresponding Nasal code. But, while performance is not a design goal, Nasal isn't especially slow either. For example, early benchmarks of the garbage collector showed it as faster than perl's reference counter, and its number crunching performance is on par with python. But in all cases, simplicity, transparency and a sane feature set are preferred over speed in Nasal.

Nasal was specifically designed for use as an extension language in an larger project such as FlightGear. The problem with many otherwise excellent languages in this environment is that they are huge. Perl and python are great, but enormous. Even their "core" interpreters and library code are larger than most projects that require an embedded language. They cannot be readily shipped with their host application and need to be installed system-wide. This is a pain and a compatibility hassle.

The real goal with Nasal is to have a language that supports most "normal" programming idioms (objects, functions, arrays, hashes) while avoiding the bloat that comes from "platform" scripting languages like perl, python, ruby and php.


Garbage collection

Nasal garbage collects runtime storage, so the programmer need not worry about manual allocation, or even circular references. The current implementation is a simple mark/sweep collector, which should be acceptable for most applications. Future enhancements will include a "return early" capability for latency-critical applications. The collector can be instructed to return after a certain maximum delay, and be restarted later.


As far as speed goes, the last time any benchmarking for Nasal was done, it was about as fast as Perl 5 or Python 2.2 at most things. It's garbage collector was faster, its symbol lookup about the same or slightly faster, and its bytecode interpreter somewhat slower. i

Thread safety

Unlike almost all other script interpreters (and unlike the FlightGear/Nasal interface itself currently) , Nasal is thread safe and scalable when called from multiple CPU threads (as opposed to the user space interpreter threads implemented by Ruby for example).

No special treatment is required (as for perl, which clones a separate interpreter with separate data for each thread and uses locking around specifically-designated shared data) and the threads can be scheduled simultaneously. There is no global lock on the interpreter, as used by Python or Lua. The only limit on scalability is garbage collection, which must block all interpreter threads before running.

When running threaded code, Nasal provides "minimal thread safety", meaning that the interpreter itself can be safely called from multiple CPU threads without risk of corrupting or deadlocking the interpreter internals. Multithreaded operations are therefore "safe", although they are not guaranteed to be atomic. In particular, poorly synchronized insertions into containers can "drop" objects into oblivion (which is OK from an interpreter stability standpoint, since the GC will clean them up normally). Nasal itself provides no synchronization primitives to address this; thread architecture is a "top-level" design job, and Nasal is intended to be an extension language in a larger project. Choice of synchronization mechanisms is going to be highly application dependent.

Exception handling

Like python, nasal supports exception handling as a first-class language feature, with built-in runtime-inspectable stack trace. Rather like perl, however, there is no special "try" syntax for exception handling, nor inheritance-based catching semantics. Instead, you use the call() builtin to invoke a function object and inspect the results to determine what error was thrown (either with the die() builtin or via an internal runtime error) and what the stack trace looked like. Elaborate exception handling isn't really appropriate for embedded scripting languages.

High level programming

Thus, programmers already familiar with C++ shouldn't just disregard Nasal as a "toy" that doesn't seem suitable for serious development: some of the more complex Nasal scripts can literally make one's head spin around and it would quite obviously take much more C++ or Java code to implement the same features, while sacrificing all the flexibility and power that a scripting language offers.

Some features can certainly be more easily implemented in Nasal space, than in C++ space. Often, the Nasal solution is "on par" with similar solutions in C++.

Accessibility

For instance, Nasal code cannot only be easily run and contributed by all users, but it can also be easily reused and maintained by other users. This means, that given the number of active C++ developers, compared to the number of base package contributors, your code is more likely to be actively maintained by fellow users if it is written in Nasal.

In other words, if there are some experimental features you'd like to explore, Nasal is an excellent way to ensure that other FlightGear users can easily test your new features. This could be witnessed during the development of the local weather system or the bombable addon,too.

This is in stark contrast to features developed solely in C++ space, because these can usually only be tested by people able to build FlightGear from source, especially if your code isn't yet in the main repository, where it would eventually be available in the form of a binary snapshot.

Obviously, none of this is to say that Nasal is the perfect solution for any problem, there are many things for which Nasal isn't necessarily a perfect choice, such as low level code for example (i.e. rendering).

On the other hand, Nasal really is a powerful tool in FlightGear, and if you find that something should, but currently cannot, be done in Nasal space, it is extremely easy to add support for new features to the Nasal engine using extension functions or property listeners to trigger C/C++ code.
  1. Benjamin Supnik (14 February 2009). Scripting: A Line In the Sand.