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.
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.
For an example usage of these techniques see the file example.cpp
chaiscript::ChaiScript chai;
To Utilize the "add" member, you need to specify the type of object you want to add:
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.
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
int i = 5; chai.add(const_var(&i), "consti"); // Add a const pointer to i
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");
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"));
//Add a new global const value chai.add(const_var(1), "myconstint");
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
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.
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_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 }
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.
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<>() >>