З інтерпретатора можна отримувати покажчики на оголошені там функції (навіть анонімні):
chai ( "var f = fun (a, b) {a + b}") - // Створюємо функцію в ChaiScript // Отримуємо покажчик на неї boost :: function f = chai.eval ( "f") - cout lt; lt; f (2,5) lt; lt; endl- // Викликаємо функцію в С ++
Ідилія закінчується, коли доводиться повертати з ChaiScript вектори:
chai ( "vec = [1,2,3,4]"); vector vec = chai.eval gt; ( "vec"); terminate called after throwing an instance of `chaiscript :: exception :: bad_boxed_cast` what (): Can not perform boxed_cast
Проблема легко з`ясовна: вектор в ChaiScript - це vector, а нам потрібен vector.
Для С ++ це абсолютно різні типи, а перетворити один в інший можна тільки поелементно, що дуже занадто багато роботи. У цієї проблеми є просте рішення - зареєструвати в інтерпретаторі тип vector.
Таким чином можна додавати вектори будь-яких об`єктів. Також є аналогічні функції map_type для типів std :: map та list_type для std :: list. При цьому, на жаль, втрачається зручний синтаксис ініціалізації масивів на льоту.
Розглянута вище функція bootstrap :: standard_library :: vector_type і приклади її використання в документації ніде не описані. Її можна «відкопати» тільки в анотаціях до вихідних кодів і на форумі. І для ChaiScript це звичайна ситуація ...
Цінним джерелом інформації в таких випадках є юніт-тести ChaiScript в директорії unittests. Переглядаючи їх можна, наприклад, дізнатися, що в ChaiScript існують досить розвинені засоби інтроспекції і відображення, які також ніяк не задокументовані.
Модулі та багатопоточність
Інтерпретатор ChaiScript може без проблем викликатися з різних потоків, якщо при компонуванні використовується бібліотека Boost.thread. Приклад такого виклику знаходиться в файлі src / multithreaded.cpp.
У багатопотоковому режимі інтерпретатор працює повільніше, але на практиці це зазвичай не помітно, оскільки все одно всі виклики переадресовуються функцій С ++. Реалізацією многопоточности ChaiScript вигідно відрізняється від інших вбудованих мов. На відміну, наприклад, від Python в ChaiScript немає власних коштів для запуску потоків, але кожен потік батьківської програми може вільно викликати будь-які команди інтерпретатора, не побоюючись накладок і не роблячи ніяких спеціальних дій для захисту даних в скрипті.
Однією з потенційно корисних можливостей ChaiScript є динамічно завантажувані модулі. Саме для них при компонуванні в Linux необхідна бібліотека ldl. В скрипті можна під час виконання завантажувати спеціальним чином створені динамічні бібліотеки, використовуючи функцію load_module ( "ім`я модуля").
Модуль в ChaiScript - це лише винесений в динамічну бібліотеку набір функцій-він не створює новий простір імен як в уже згаданому Python. Щоб створити найпростіший модуль, потрібно написати приблизно такий код:
#include #include #include void hello_module () { std :: cout lt; lt; "Hello from my_module!" lt; lt; std :: endl; } CHAISCRIPT_MODULE_EXPORT chaiscript :: ModulePtr create_chaiscript_module_my_module () { chaiscript :: ModulePtr m (new chaiscript :: Module ()) - m-gt; add (chaiscript :: fun (hello_module), "hello_module") - return m; }
Все досить інтуїтивно зрозуміло: модуль експортує одну функцію hello_module, а макрос CHAISCRIPT_MODULE_EXPORT відповідає за всю внутрішню «бухгалтерію». Однак домогтися того, щоб скомпільований модуль завантажувався в інтерпретатор, виявилося не так-то простою справою. Методом проб і помилок вдалося з`ясувати два критичних моменти.
По-перше, в рядку:
create_chaiscript_module_my_module ()
обов`язковим префіксом є «create_chaiscript_module_», а весь наступний - це власне ім`я модуля.
По-друге, ім`я одержуваної бібліотеки повинно бути lib.so (в нашому випадку libmy_module.so). У мене в 64-бітної Ubunu 11.10 успішною виявилася компіляція з такими прапорами:
$ G ++ -fPIC -Кпуть до ChaiScriptgt; -o my_module.o -c my_module.cpp $ G ++ -fPIC -shared -o libmy_module.so -ldl my_module.o
На екрані з`явиться «Hello from my_module!». Модулі дозволяють розбити програму на незалежні компоненти, які можна при необхідності завантажувати прямо з скриптів, не втрачаючи при цьому в швидкості.
висновки
ChaiScript, безумовно, мова не для всіх. Хоча б тому, що він не є самодостатнім, як Python або Ruby. Ідейно він найбільше нагадує браузерні javascript, який не потрібний у відриві від самого браузера (Втім, активне поширення Node.js прагне виправити цю ситуацію. - Прим. Ред.), Але в той же час робить браузери надзвичайно потужними інструментами. ChaiScript намертво прив`язаний до С ++, і в цьому його основна сила. Вбудовування повноцінного інтерпретатора в будь-яку програму на С ++ займає кілька хвилин (в найпростіших випадках) і навіть не вимагає ніякої додаткової компонування зі сторонніми бібліотеками.
Сама ідея мови, який повністю покладається на типи даних батьківської програми, здається мені по-справжньому революційною. Сумнозвісний бич всіх скриптових мов - перетворення даних між компільовані бекендом і скриптовою фронтен-будинок. У ChaiScript просто нічого перетворювати: все, що треба скрипту, оголошується на С ++ і реєструється в інтерпретаторі.
Є у ChaiScript і недоліки. Мова дуже простий, в ньому немає повноцінних класів, просторів імен, замикань, виразної стандартної бібліотеки. Документація, м`яко кажучи, неповна і організована не найкращим чином. Проте, ChaiScript може бути ідеальним варіантом для проектів на С ++, де потрібно «малою кров`ю» реалізувати скриптова мова і немає потреби в самодостатніх мовах, таких як Python або Lua.