Open-source project
ChaiScript/ChaiScript avatar
ChaiScript/ChaiScript

ChaiScript: a header-only embedded scripting language built for C++17

Embedded Scripting Language Designed for C++

3,128 stars366 forksC++NOASSERTION

At a glance

What is it?
ChaiScript is an embedded scripting language written in C++ and designed around C++17 language features. It suits projects that want user-editable scripts with type-checked calls into native code, and it is a poor fit for teams that need a stable release cadence.
Who is it for?
Adopt ChaiScript when your application is C++17, you want scripts to call registered native functions with type checking, and you can build against the develop branch because the newest tagged release is 6.1.0 from 2018. Avoid it if you need a guaranteed support window, a documented rollback path, or a language whose specification is separate from its implementation.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 151 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What ChaiScript solves for a C++ application

Most applications that grow a scripting layer end up with a boundary problem. The host program is written in C++, the script is written in something else, and every value that crosses between them has to be converted by hand. ChaiScript's stated goal is to remove that boundary. The README describes it as "one of the only embedded scripting language designed from the ground up to directly target C++, working with the developer how they would expect it to work." The audience is therefore narrow and specific: C++ developers who want to expose part of an existing native codebase to scripts that users or integrators can edit.

The README lists three properties it claims over other embedded languages. It uses a header-only approach, so integration does not require a separate build step for a runtime library. It maintains type safety between the C++ application and user scripts. And it supports C++ techniques including callbacks, overloaded functions, class methods and STL containers. Those three claims define the trade-off. A header-only design means the parser, evaluator and standard library arrive as source you compile, which is convenient for integration and expensive at build time. Type safety across the boundary is the feature that distinguishes it from a language where every argument arrives as an untyped variant.

Requirements are explicit. ChaiScript needs a C++17 compiler with support for variadic templates, and the README states it has been tested with gcc 7 and clang 6 with libcxx. That is a floor, not a ceiling, but it tells you the project assumes a modern toolchain rather than accommodating older ones. If your product still ships on a pre-C++17 compiler, this is the first thing that rules ChaiScript out.

How the engine, registration and evaluation fit together

The architecture is a single engine object plus a registration step. You instantiate chaiscript::ChaiScript, and the README says the default behaviour is to load the ChaiScript standard library from a loadable module. A second option, documented as compiling the library into your code, avoids the loadable module entirely. That choice matters for deployment: a loadable standard library module is a file that has to be found at runtime, while a compiled-in library is part of your binary. The README presents both without recommending one, and for a shipped application the compiled-in path is the one that removes a runtime dependency.

Once the engine exists, native functions become visible to scripts only after registration. The mechanism is chai.add with chaiscript::fun wrapping a function pointer and a string name. The README gives this example:

cpp
chai.add(chaiscript::fun(&my_function), "my_function_name");

After that call, scripts see the function as my_function_name. Nothing is exposed implicitly. Every native entry point you want scripts to reach has to be registered by name, which keeps the surface area of the boundary under your control and makes the set of callable functions auditable by reading your own registration code.

Evaluation has two entry points. chai.eval(string) processes source a line at a time, and chai.eval_file(fname) processes a file at a time. The README's shortest complete example uses the string form with an explicit return type: chai.eval<double>("function(3, 4.75);"). The template parameter is where the type safety shows up. The host asks for a double, and the call is resolved against the registered function signature rather than against an untyped value that the host must cast. For a fuller picture of registration, the README points at example.cpp in the samples directory and describes it as verbose, showing every possible way of working with the library. The unittests directory is named as the place where the language itself is covered most thoroughly.

Installing ChaiScript with vcpkg and running a first script

The README documents one package-manager install path, vcpkg, and notes that the ChaiScript port there is kept up to date by Microsoft team members and community contributors. The commands below are the ones the README lists, in order.

bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
vcpkg install chaiscript

After bootstrap-vcpkg.sh runs, vcpkg integrate install wires the installed packages into your build system, and vcpkg install chaiscript fetches the port. If the port is behind, the README directs you to open an issue or pull request on the vcpkg repository rather than on ChaiScript itself.

For a manual setup the README gives three steps: add the ChaiScript include directory to your project's header search path, add #include <chaiscript/chaiscript.hpp> to your source file, and instantiate the engine. The complete example from the README combines registration and evaluation in one file.

cpp
/// main.cpp
#include <chaiscript/chaiscript.hpp>

double function(int i, double j)
{
  return i * j;
}

int main()
{
  chaiscript::ChaiScript chai;
  chai.add(chaiscript::fun(&function), "function");

  double d = chai.eval<double>("function(3, 4.75);");
}

What you should see is a program that constructs an engine, registers one native function under the name function, evaluates a one-line script that calls it with an int and a double, and assigns the result to a double. The samples directory holds smaller scripts to try next, including hello.chai, if.chai, for.chai, while.chai, lambda.chai, range.chai, scope.chai and vector.chai. Those file names tell you the language covers control flow, lambdas, ranges, scoping and vectors, and each is short enough to run through eval_file as a first experiment.

Where ChaiScript stops being the right tool

The most concrete limitation is release cadence. The repository's recent releases list shows v6.1.0 tagged on 2018-05-29 and v6.0.0 on 2017-02-22. The only newer entry is wasm-latest, a WASM build published on 2026-05-02, which is a build artifact rather than a versioned language release. The default branch is develop, and the last push to the repository was on 2026-05-02. So the code is being touched, but a team that pins to tagged releases is pinning to something from 2018. If your process requires a versioned release with a changelog entry for every change you depend on, that gap is a real problem.

Header-only integration is the second cost. Compiling the engine and its standard library into every translation unit that includes chaiscript.hpp is convenient at the integration step and expensive at the build step. The README offers the loadable standard library module as the default precisely because it moves that cost out of your build, but then you have a runtime file to ship and locate. Neither option is free, and the README does not document rollback behaviour for a failed load of that module.

There is also a governance point worth stating plainly. The licence field on the repository is NOASSERTION, while the README says the project is "Release under the BSD license, see license.txt for details" and the top-level entries include both LICENSE and license.txt. Two licence files with an unasserted repository classification is exactly the kind of thing your legal reviewer will ask about, and the README does not resolve it. If your organisation requires a machine-readable licence identifier, that is unresolved here.

Finally, ChaiScript is a scripting layer, not a sandbox or a security boundary. Nothing in the README describes resource limits, instruction budgets or a restricted execution mode. If the scripts come from untrusted users, the registration model tells you what they can call but not how long they can run.

ChaiScript against Lua and Sol2

The comparison people search for is ChaiScript versus Lua, and the difference is where the type information lives. Lua is a separate C library with its own value model, and a C++ host binds to it through a stack machine where values are pushed and popped and converted by hand or by a binding layer. ChaiScript inverts that. The engine is C++ source compiled into your program, and the boundary is a registration call that carries the native signature. The README's claim of type safety between the application and the scripts follows from that design: the host asks for a double and the engine resolves the call against the registered function, rather than the host reading an untyped slot off a stack.

The practical consequence is that Lua buys you a stable, widely deployed runtime with a specification that exists independently of any one implementation, and ChaiScript buys you a boundary that speaks C++ types directly. Choosing between them is mostly a question of whether you want the runtime to be a separate artifact with its own versioning or a set of headers compiled into your build.

Sol2 is a different kind of answer to the same problem. It is a binding layer for Lua in C++, so it keeps the separate runtime and improves the ergonomics of the boundary. If your objection to Lua was the stack manipulation rather than the runtime, Sol2 addresses the objection without changing the underlying model. ChaiScript changes the model itself, which is why it carries the header-only build cost and the release-cadence gap that a Lua-based stack does not. AngelScript appears in the same searches as another embedded option, and the same question applies to it: whether you are willing to accept a runtime separate from your binary in exchange for a versioned language.

Maintenance, upgrades and the licence question

The last push to the repository was on 2026-05-02, so the project is not archived and the develop branch is being touched. That is not the same as a maintained release line. The newest tagged language release is v6.1.0 from 2018-05-29, and the only 2026 entry in the release list is wasm-latest, a WASM build. A team that wants to consume a fixed version and receive fixes for it has no recent tag to point at.

Upgrade cost is shaped by the header-only design. Because the engine is compiled into your application, moving to a newer revision means recompiling the code that includes chaiscript.hpp, and the README states the requirement as a C++17 compiler with variadic template support, tested with gcc 7 and clang 6 with libcxx. If you are already on that toolchain, the compiler side of an upgrade is not the obstacle. The obstacle is knowing what changed, because the release list does not offer a 2026 version to diff against.

On licensing, the README says the project is released under the BSD license and points to license.txt, while the repository carries both a LICENSE and a license.txt at the top level and the licence field is NOASSERTION. I am not giving legal advice, and the honest reading is that the authoritative text is in those files and that the repository metadata does not confirm a specific BSD variant. If you need a precise identifier for a compliance record, read both files and get a determination from whoever handles that for your organisation. The README alone does not settle it.

Editorial conclusion

Adopt ChaiScript when your application is C++17, you want scripts to call registered native functions with type checking, and you can build against the develop branch because the newest tagged release is 6.1.0 from 2018. Avoid it if you need a guaranteed support window, a documented rollback path, or a language whose specification is separate from its implementation. Before committing, generate the doxygen documentation from the build folder, read example.cpp in samples/, and run the unittests directory against your target compiler to see which C++17 features it exercises.

Frequently asked questions

What is ChaiScript?

ChaiScript is an embedded scripting language written in C++ and designed to target C++ directly, using a header-only approach so it can be compiled into an existing project. It maintains type safety between the host application and user scripts and supports callbacks, overloaded functions, class methods and STL containers.

Can C++ be used as a scripting language?

ChaiScript is not C++ used as a script, but it is a scripting language built for C++ hosts. Its engine is C++ source compiled into your application, and native functions are exposed to scripts through chaiscript::fun registration rather than through a separate runtime.

How do I install ChaiScript?

The README documents installing it through vcpkg, whose port is kept up to date by Microsoft team members and community contributors. Alternatively you add the ChaiScript include directory to your header search path and include chaiscript/chaiscript.hpp directly.

Which compiler does ChaiScript require?

It requires a C++17 compiler with support for variadic templates. The README states it has been tested with gcc 7 and clang 6 with libcxx.

How are C++ functions made callable from ChaiScript?

They must be registered with the engine. The README shows chai.add(chaiscript::fun(&my_function), "my_function_name"), after which the function is visible to scripts under the name you passed.

What is the newest ChaiScript release?

The newest tagged language release is v6.1.0 from 2018-05-29. The only later entry in the release list is wasm-latest, a WASM build published on 2026-05-02, which is a build artifact rather than a versioned language release.

Official sources

  1. ChaiScript/ChaiScript on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chaiscript-chaiscript.svg)](https://hysenlabs.com/projects/chaiscript-chaiscript)