Intro
Sometimes you need to extend your C++ Application dynamically during runtime with something what is not known at compile time yet.
For easy things you can provide config files which may be changed prior before the application is started. The Application reads the config files on startup and use the contained settings accordingly.
But there are more complex things which cannot be solved via a simple config file with static data. Additionally, there may be also custom tasks which shall be executed not necessarily on startup but at any specific point during runtime.
For these things you can provide customization points in your C++ Application which will execute custom provided script files for fulfill specific tasks. With that it is possible to extend your already compiled C++ Application with arbitrary things / features during runtime by yourself, the users/admins/… or a third-party script provider.
With customization points you can avoid expensive re-compiles, CIs, tests and deployments. Just change / provide a script file.
For be able to do that, you need a C++ Library for executing script files / code.
Comparison
Here follows a comparison between the most well known Script languages which are provided as a software library embeddable in C++ and the new “competitor” TeaScript.
This article is only be meant for a brief overview. The reader may then dive deeper into a specific script language/library via the provided links below.
The Script Libraries are:
- ChaiScript
- Lua
- Jinx
- Squirrel
- AngelScript
- Wren
- and of course TeaScript
I added Python as well, although it is not really be meant to be used as a library in other software – but it can be.
The comparison focusing on high-level criteria like: How easy can it be integrated? Does it provide a modern and easy to use C++ API? Does it have modern languages features? And so on.
Update (19th, Jan. in 2024): The feature table now reflects the development of TeaScript from the Release 0.12.
Legend:
✅ – is used if the language / library fulfill the criteria in a good way.
🔵 – is used if the criteria is present in some kind but with issues/concerns or lacking features. It fulfills the criteria only in a medium good or average way.
❌ – is used if the criteria is not present / fulfilled, or only in a less than average way with bigger issues / concerns.
❓ – is used if it is not completely clear if or how the criteria is fulfilled.
✅✅ – sometimes more than one library fulfills a criteria in a good way. But if one is better as the others it will use 2 green check mark boxes.
Feature | TeaScript | ChaiScript | Jinx | Squirrel | AngelScript | Wren | Lua | Python |
---|---|---|---|---|---|---|---|---|
C++ Header only Lib | ✅✅ | ✅ | ✅✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
C++ Library (ordinary) | ❌ (✅ planned**) | ❌ | ✅ | 🔵 | ✅ | ❌ | ❌ | ❌ |
Easy to use C++ API | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
Modern C++ (covers also coding techniques / style) | ✅✅ (C++20) | ✅ (C++11/14) | ✅✅ (C++17) | ❌ | ❌ | ❌ | ❌ | ❌ |
Line-By-Line capable Parser | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❓ | ✅ |
Intermediate evaluation of parsed input | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❓ | ❓ |
Suspendable (stop execution and continue later) | ✅ (planned for next*) | ❌ | ✅✅ | ❌ | ✅ | ✅ | ✅ | ❓ |
Close to C++ Syntax | ✅ | ✅✅ | ❌ | ❌ | ✅✅ | 🔵 | ❌ | ❌ |
Modern language features | ✅✅ | 🔵 | ✅✅ (very unique but procedural) | ✅ | ❌ | 🔵 | 🔵 | ✅ |
Unique/fresh/creative (and fitting) language design | ✅ | 🔵 | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ |
Multithreading support (from within the language) | ❌ (✅ planned**) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Coroutine support | ❌ (✅ planned**) | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Garbage Collector | ❌ | ❌ | ❌ | 🔵 | ✅ | ✅ | ❌ | ✅ |
RAII / RC counted | ✅ | ✅ | ✅ | 🔵 | ❌ | ❌ | ✅ | ✅ |
Bytecode / integrated VM | ✅ (planned for next*) | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Rich integrated core library | ✅ | 🔵 | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
Test, Analyze and debugging capabilities | ✅ (will be improved more) | ❌ | ❌ | ❌ | ❌ | ❌ | ❓ | ✅ |
Mature | 🔵 (✅ with 1.0 release) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Maintained | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
* planned for next means the feature will be added most likely within the next couple of pre-releases (minor version updates).
** planned means the feature will be most likely available before the first major release 1.0.
*** planned for later means the feature will probably be available only in a version after the 1.0 release.
Note: The “planned” and “planned for next” features for TeaScript are marked with ✅ already assuming they will be available in a not very far away future.
Some Feature explanation:
Line-By-Line capable Parser – means that the Parser can be fed at / with arbitrary line boundaries, regardless if the provided lines form a logically complete block. If the Parser is lacking this feature, it must be fed with the complete script code at once or at exact top-level code block boundaries.
Intermediate evaluation of parsed input – means that the evaluation / executing of the code can start already before the complete script code is passed to the Parser, e.g., execute all parsed top-level statements already. This feature does only make sense if the Parser can be fed with arbitrary amount of lines from the code.
Suspendable (stop execution and continue later) – means that script code can be stopped / paused somewhere in the middle of execution and continue later at some time within the same hosting process instance. This is useful for example if the Application needs to do more important work and wants to continue the task of the script after the more important work is done. This feature does not mean, that the complete state of a running script can be serialized and loaded from disk later for continuation.
Modern language features – Modern language features are for example generic programming, lambdas, pattern matching (or more likely in compiled languages: const as default, move as default, …) but also Uniform definition syntax, types are values, and many more …
Multi-threading support (from within the language) – means that script code can be executed in parallel from within the script code itself (e.g., spawning threads, async jobs, etc…)
Bytecode / integrated VM – means that the parsed code is transformed / “compiled” into a different binary form as an AST and executed in a kind of integrated virtual machine. For example, Python has its own “tiny” virtual stack machine and Lua comes with its own (more complex) virtual register machine.
UPDATE: It is planned to integrate a virtual stack machine, the ‘Tea StackVM’, to TeaScript for version 0.14. Read the Teaser at end of this article.
Some words to the Script Languages
Wren – The syntax of Wren reminds of C++, the operators are almost identical but it differs in other things like variable definition and object creation. The language is focusing on programming with classes. Wren only offers a pure C interface. So, I will not say more about the library (which is not a judging but I focus on C++).
Squirrel – This language exists at least since 2003 already. The age can also be seen in the code base. For me the language reminds me of JavaScript or sth. similar. The usage of curly brackets and semicolons is not enough for me to judge the syntax close to C++. The API leaves a bloated impression to me and is too complicated in use. Squirrel uses an old style of C++ internally but seems to offer only a C-like API.
AngelScript – This language points back at least to 2005 (probably older). AngelScript is the most close to C++ syntax of all compared script languages. As with Squirrel the age can be seen in the code base. The API is a mix of C like and some kind of older C++. The interface seems to be bloated and complicated as well. As far as I can see, AngelScript does not offer any modern programming features.
Lua – Only a few words to Lua since it does not offer a C++ API. The syntax is completely different to C++ (which is not bad in general) and from my opinion Lua is only usable by programming experts. It has some built-in traps, like 0 is evaluated to true. The C API seems to be complex and difficult to use (you need to know about the Lua internals / architecture). Lua is the fastest scripting language in the field since it uses an own internal virtual register machine.
Python – No details to Python since it is not really be meant for to be integrated in other applications.
ChaiScript – ChaiScript was the root point for the initial idea for TeaScript. Unfortunately it is not maintained anymore and it produces a lot of warnings when being compiled, but it is still usable. The syntax is very close to C++ but also has modifications and extensions. It can be easily integrated as a header only library and is written in modern C++11/14. But from my point of view the header only lib is too big already for only to be used as header only (due to the many templates). There should be at least the option to be used as an ordinary library. One key point of ChaiScript is the capability to register complete classes with all its constructors and methods so that they are usable in the script. Also it implements function/method overload resolution. It only offers a few modern programming features like lambdas. The architecture has some drawbacks in the recursive chained parsing and also in the evaluation for the AST-nodes. Compared to the other languages it is relatively slow.
Jinx – Jinx is a very unique language with a complete different syntax as C++ which reminds a bit of Lua. The interesting and most creative part of the language design is the possibility to look similar as a spoken language since the function names can contain spaces and the use of the apostrophe ‘s. Jinx is a procedural language and offers only some primitive types, strings (UTF-8), procedures and a collection type (for lists and maps). It does not offer a general purpose integrated core library usable by the script code. So, you need to provide some functionality by your own. One key point of Jinx is that the scripts are suspendable by default and they will break execution after some certain amount of instructions which can be fine tuned. The API leaves a well designed impression and is easy to integrate.
TeaScript – TeaScript is the newest script language in the field and offers an easy to use and modern C++ API. The syntax is still close to C++ but has also modifications and extensions, like the addressable loops. While the feature set is not complete yet, it offers already Functional Programming (inclusive lambdas and higher order functions), procedural programming, generic programming, “Types are Values” and more. It also comes with a integrated general purpose core library for string manipulation, time measurement, basic file io and more. The core library + the availability of a (portable) Host Application makes it also usable as a standalone scripting language.
The best way to know more about TeaScript is to read in the Overview and Highlights section.
The code is also browsable on Github. But I encourage you to use the download bundle from the download page since it contains more example script files.
Some final notes to TeaScript and Jinx
Jinx is a very well (and unique) designed language. Actually, TeaScript and Jinx are addressing different audiences. Jinx is focusing at the game dev field with its suspendable features, but is less optimal as a general purpose scripting solution. TeaScript on the other hand is aiming to be used in general purpose standalone scripts and also to be integrated in all kind C++ applications. Depending on the use case one of it could fit better than the other. The complete different syntax as well as the chosen programming paradigms must be taken into account as well when make a decision.
Can TeaScript be used for production already?
Yes, it can. I explain why and what is the best practice:
First, although the 1.0 release is not done yet, every release has 3 levels of quality assurance:
- UnitTests – on C++ as well as on TeaScript level (TeaScript files testing TeaScript features).
- Functional tests with scripts.
- Manual testing.
This ensures a high level of quality already.
Second, the TeaScript syntax and language features which are present already will stay compatible and are covered by the tests mentioned above. In new versions new syntax and new language features will be added, but old written scripts will still be functional in 99% of the cases (only if the script used some quirks or rely on broken functionality or if really big issues are addressed, this backward compatibility might not be hold.)
Third, usage of the high-level C++ API only. This API will stay backward compatible or a soft migration will be provided – except if major issues are detected or at very rare circumstances.
The high-level API consists of the classes teascript::Engine
/ teascript::EngineBase
, all public getters in teascript::ValueObject
as well as everything in Exception.hpp / SourceLocation.hpp and version.h (except where otherwise noted).