About ChaiScript

ChaiScript is the first and only scripting language designed from the ground up with C++ compatibility in mind. It is an ECMAScript-inspired, embedded functional-like language.

ChaiScript is licensed under the BSD license.

Download

Version: 2.3.3 Released: 5/15/2010

Source
Windows
Linux

ChaiScript C++ API

For an example usage of these techniques see the file example.cpp

ChaiScript


ChaiScript is the main object you use to interact with the scripting engine.

chaiscript::ChaiScript chai;

ChaiScript::add


The "add" member is used for adding components to the ChaiScript system. Components can be either functions, types, "modules" or data objects.

To Utilize the "add" member, you need to specify the type of object you want to add:

fun


"fun" is used to add a function to the system. A function can be a free function, a boost::function<>, a member function or member data.

Given the following code:

class MyClass
{
public:
  MyClass(int t_value) : m_value(t_value) {}
 
  int get_value() { return m_value; }
  void multiply_value(int v) { m_value *= v; }
  std::string data;
 
private:
  int m_value;
};
 
std::string concat(const std::string &first, const std::string &second)
{
  return first + second;
}

We can fully expose this set of functions to ChaiScript with just a few lines of C++:

chai.add(fun(&MyClass::get_value), "get_value");
chai.add(fun(&MyClass::multiply_value), "multiply_value");
 
// the data member can be added like other class members
chai.add(fun(&MyClass::data), "data"); 
 
//"fun"'s can also be implicitly created using the "constructor" function
//for exposing class constructors
chai.add(constructor<MyClass (int)>(), "MyClass");
 
//Exposing a free function is the same:
chai.add(fun(&concat), "my_concat");
 
//Also, a boost::function can be added
chai.add(fun(boost::function<std::string (const std::string)>(boost::bind(&concat, "Hello ", _1))), "my_bound_concat");

Additionally, "fun" overloads exist which automatically bind in up to 2 parameters in at function creation time. These overloads are helpful when wanting to add bound object methods.

class MyClass
{
  public:
    MyClass(int t_val) : m_value(t_val) {}
 
    int add_value(int i) { return m_val + i; }
 
  private:
    int t_val
};
 
MyClass object(10);
chai.add(fun(&MyClass::add_value, &object), "add_value"); // Add "add_value" which is bound to the "object" variable.

var


Var is used to specify a C++ variable to add. Variables can be either boost::shared_ptr<>, boost::ref, pointer or data. Note, if you add a variable as data, the value is copied into the system, and the variable is not shared with ChaiScript.

Examples:

int i = 5;
chai.add(var(i), "i"); // Add a copy of i, and call it "i"

The above example adds a copy of a local variable to the ChaiScript system. The safest way to share data (as opposed to copying) is with a boost::shared_ptr<>.

boost::shared_ptr<MyClass> myclass(new MyClass(5));
chai.add(var(myclass), "myclass"); // Add our shared MyClass object to the system

Options that are not as safe for sharing data include the use of pointers or references, seen below:

// This option is a little dangerous, if the local i were to go out of scope,
// the pointer would become invalidated
int i = 5;
chai.add(var(&i), "i"); // Add a pointer to i. 
// This option is dangerous in the same way that the pointer version is
std::string s = "hello world";
chai.add(var(boost::ref(s)), "str"); // Add a boost::reference to our string

const_var


const_var is used to create a const value, and follows all of the same rules of "var" otherwise.

int i = 5;
chai.add(const_var(&i), "consti"); // Add a const pointer to i

user_type


User_type is completely optional, but provides the system with a name for your new type. Notice that up to this point we have not explicitly told ChaiScript about MyClass. The system is happy to work with what we have given it so far.

User_type is required if you want your type to be support by the "clone" and "new" functions in ChaiScript. "Clone" and "new" do a lookup of the typename of an object to determine how to call its constructor.

That is, if you want your type to be fully supported by the ChaiScript system, you need to add a default constructor, a copy constructor and a user_type entry all with the same exact name.

chai.add(user_type<MyClass>(), "MyClass");

Module


A Module is simply a collection of functions and type information. The files bootstrap.hpp and bootstrap_stl.hpp provide many helper functions that create Modules for you.

The two most notable examples are for a constructor adding helper and for bootstrapping of STL types.

//Add default and copy constructors for our type
chai.add(bootstrap::basic_constructors<MyClass>("MyClass"));
//Add a new type, a vector of our MyClass
chai.add(bootstrap::vector_type<std::vector<MyClass> >("MyClassVec"));

ChaiScript::add_global_const


The "add_global_const" member is used for adding global const variables to the ChaiScript system. All globals must be const and globals are visible across all thread contexts and all stack levels. Add_global_const must be used in conjunction with const_var, to create and add a const value

//Add a new global const value
chai.add(const_var(1), "myconstint");

ChaiScript::eval


ChaiScript::eval is the main mechanism used to evaluate ChaiScript code. In the most simple case, we execute one off pieces of ChaiScript:

chai.eval("var j = 5+7; print(j)");

We can also type-safely handle C++ return values from our ChaiScript:

int i = chai.eval<int>("5+7");

Building on the previous example, we see that we can call our MyClass constructor, and share the result with C++:

boost::shared_ptr<MyClass> obj = chai.eval<boost::shared_ptr<MyClass> >("var obj = MyClass(2)");
 
obj.multiply_value(3);
 
chai.eval("print(obj.get_value())");
chai.eval("obj.multiply_value(2)");
 
std::cout << obj.get_value() << std::endl;

A shortcut to using "eval" is the () operator, which does not perform any runtime type checking:

chai("obj.multiply_value(18)");

Also, we can use the eval (or the () shortcut) to handle return values in a generic fashion using Boxed_Value.

Boxed_Value bv = chai("MyClass()"); // construct a MyClass in a generic factory-style way

ChaiScript::eval_file


eval_file behaves exactly the same as "eval" but expects a file name to parse instead of a string.

ChaiScript::functor


The functor utility function allows us to treat ChaiScript code as if it where native C++ functions. It is a templated function that takes a function signature, and a string to parse.

boost::function<void ()> print_value = chai.functor<void ()>("fun() { print(obj.get_value()) }");
 
print_value();
 
boost::function<int (int, int)> int_max = chai.functor<int (int, int)>("fun(x, y) { if (x>y) {x} else {y} }");
 
int m = int_max(6,7);
 
//Note, the returned boost::function need not necessarily be stored in a local
//variable. An interesting shortcut would be to do:
int s = chai.functor<void (int, int)>("fun(x, y) { x+y }")(4,5);
 
//The above technique is particularly handy when you need to substitute
//in C++ variables into your executed ChaiScript code.

ChaiScript::get_state & ChaiScript::set_state

Get_state and set_state allow the user to save and restore the current state of the ChaiScript system. The currently loaded set of of functions, variables, types nad modules is saved and restored, so the developer can revert to a previous system state. The functions operate on chaiscript::ChaiScript::State objects.

ChaiScript::State state = chai.get_state();
chai.set_state(state);

boxed_cast


boxed_cast is used to cast a Boxed_Value to another type. "Eval" and "eval_file" both return Boxed_Value objects. Taking the "factory_style" example from above:

Boxed_Value bv = chai("MyClass()"); 
 
//Various ways of extracting type safe data from a Boxed_Value
boost::shared_ptr<MyClass> o1 = boxed_cast<boost::shared_ptr<MyClass> >(bv);
MyClass &o2 = boxed_cast<MyClass &>(bv);
MyClass o3 = boxed_cast<MyClass>(bv); // This version makes a copy
MyClass *o4 = boxed_cast<MyClass *>(bv);
 
//If we try a boxed_cast that is invalid a bad_boxed_cast is thrown
try {
  int i = boxed_cast<int>(bv);
} catch (const bad_boxed_cast &) {
  // failure in boxed_cast
}

functor


The functor free function is similar to the ChaiScript::functor method in that it creates a C++ type safe function call from a ChaiScript object.

The functor free function differs from ChaiScript::functor because it expects a chaiscript::Proxy_Function object. This can be used for generating C++ callbacks from ChaiScript code.

struct Callback_Handler
{
  std::vector<boost::function<int (const std::string &)> > m_funcs;
 
  void add(const chaiscript::Proxy_Function &f)
  {
    //Create a boost::function<> from the passed in Proxy_Function
    //and add it to our vector of chaiscript
    m_funcs.push_back(chaiscript::functor<int (const std::string &)>(f));
  }
};
 
int main()
{
  chaiscript::ChaiScript chai;
  chai.add(fun(&Callback_Handler::add), "add");
 
  Callback_Handler ch;
  chai.add(var(&ch), "callback_handler");
 
  //Create a chaiscript lambda function and add it to the list 
  //list of callbacks 
  chai.eval("challback_handler.add(fun(x) { x.size(); })");
}

example.cpp has a more robust example of using functor to support ChaiScript callbacks.

Operators

C++ operators are just like any other function pointer, so adding them can be as simple as

chai.add(fun(&MyClass::operator+), "+");

Helper functions for adding conanical forms of opreators are provided that optionally take a ModulePtr and return a ModulePtr.

chai.add(operators::less_than<MyClass>());

The full list of supported operators:

  • assign<>()  =
  • assign_bitwise_and<>()  &=
  • assign_xor<>()  ^=
  • assign_bitwise_or<>()  |=
  • assign_difference<>()  -=
  • assign_left_shift<>()  <<=
  • assign_product<>()  *=
  • assign_quotient<>()  /=
  • assign_remainder<>()  %=
  • assign_right_shift<>()  >>=
  • assign_sum<>()  +=
  • prefix_decrement<>()  --
  • prefix_increment<>()  ++
  • equal<>()  ==
  • greater_than<>()  >
  • greater_than_equal<>()  >=
  • less_than<>()  <
  • less_than_equal<>()  <=
  • logical_compliment<>()  !
  • not_equal<>()  !=
  • addition<>()  +
  • unary_plus<>()  +
  • subtraction<>()  -
  • unary_minus<>()  -
  • bitwise_and<>()  &
  • bitwise_compliment<>()  ~
  • bitwise_xor<>()  ^
  • bitwise_or<>()  |
  • division<>()  /
  • left_shift<>()  <<
  • multiplication<>()  *
  • remainder<>()  %
  • right_shift<>()  >>