以前从未接触过脚本引擎,只是知道有vbscript和jscript其他的一概不知,由于工作需要对脚本做了一些了解,发现这个东西原来是这么的美好,我非常喜欢python,并且把它定为我以后必须掌握的一门动态语言。
最先看到的是python,后来发现他很庞大,对我的程序来说,不是很适合。然后又找到了lua,发现他非常适合我的需求,研究了一下发现他对C++的支持不是很好,于是找了几个封装类,tolua++,luaplus,luabind,等等,大概用了一下感觉不舒服,在搜寻怎么访问C++类成员函数的时候无意中发现了一个叫angelScript的脚本引擎,看了一下非常的棒,和C++的结合是无缝的,使用起来也很舒服,语法是类C/C++的,不用学习新的语法结构更是舒服。就是例子少了点,上网一通搜索也没有找到完整的例子,都是一些片段,下面我就总结一下我学习的成果,如果哪位朋友,有更好的相关的任何资料希望能够共享一下,共同进步。
关于该脚本的详细介绍还要看他的官方网站,我就不做翻译了:这是他的官方网站 http://www.angelcode.com/angelscript/ 主要特点: Script language: 语法和C/C++极为相似,仅仅有很少的差异静态编译,可以注册新的类型面向对象,sandboxing Engine: Run-time compiled Step by step execution - The scripts may be executed line by line, for controlled execution and debugging. Detailed exceptions - When an exception occurs information about why and where can easily be obtained. Line interpretation - The engine can interpret separate script lines using the currently compiled script. Useful for Quake like consoles. Saving/loading byte code - The compiled bytecode can be saved and reloaded at a later time. Modular - Scripts can be compiled into modules that can be dynamically linked with each other. Concurrent scripts - Several scripts can be executed in parallel, by suspending and resuming them one by one. This allows for easy implementation of multi-tasking schemes, or even co-routines. Debugging support - The application can examine the call stack and the value of local variables while running the scripts. The line callback feature allows the application to use breakpoints, do profiling, etc.
Integration Direct access - The script engine can directly access and use registered functions, objects, and object members without the need to write proxy functions (where native calling conventions are supported). C and C++ interface - There is a flat C interface that can be used by languages that don't interact well with C++ interfaces. It is successfully being used in Delphi projects. Multithreading - The library can be used in a multithreaded environment. Memory management - Objects are reference counted for easy management of objects that pass between script and application. An iterative garbage collector is used where circular references can occur. The application can also completely control the library's memory usage.
Portability
下面是测试代码 ,仅仅是向脚本注册一个类的实例scard,然后可以在脚本里面调用他的方法。我目前就需要这些使用mfc新建一个对话框程序,然后用下面的代码替换即可
包含头文件和lib库,生成lib库的时候要注意编译条件是否和你的需求一样比如,c++/code generation/runtime library的选项要符合你要编写的程序
#include <angelscript.h> #include "scriptstring.h"
#pragma comment(lib, "angelscriptd.lib")
脚本代码:
/*
main entry for script file
*/
void main()
{
astest();
}
void astest()
{
//call c function
scard.reconnect();
}
C++测试代码:
int LoadScript(asIScriptEngine * pScriptEngine, const char *filename, const char * module); void MessageCallback(const asSMessageInfo *msg, void *param) { const char *type = "ERR "; if( msg->type == asMSGTYPE_WARNING ) type = "WARN"; else if( msg->type == asMSGTYPE_INFORMATION ) type = "INFO"; CStringA str; str.Format("%s (%d, %d) : %s : %s/n", msg->section, msg->row, msg->col, type, msg->message); AfxMessageBox(CString(str)); } void CangelTestDlg::OnBnClickedOk() { // TODO: Add your control notification handler code here // Create the AngelScript script engine asIScriptEngine * pScriptEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION); if( pScriptEngine == 0 ) { AfxMessageBox( _T("Failed to create Angel Script engine.") ); return; } // Set the message callback pScriptEngine->SetMessageCallback(asFUNCTION(MessageCallback), NULL, asCALL_CDECL); // Register AngelScript Plugins RegisterScriptString(pScriptEngine); // Local function used to load a script file into memory // A separate function was created to unclutter this main function // a bit. if (LoadScript( pScriptEngine, "script.as","myModol" ) < 0) { pScriptEngine->Release(); AfxMessageBox(L"Failed to load script file."); return; } int s = pScriptEngine->RegisterObjectType("c_scard", sizeof(c_scard), asOBJ_REF); if(s < 0) AfxMessageBox( L"register c_scard failed" ); s = pScriptEngine->RegisterObjectBehaviour("c_scard", asBEHAVE_ADDREF, "void f()", asMETHOD(c_scard, AddRef), asCALL_THISCALL); if(s<0) AfxMessageBox( L"register behaviour ref failed" ); s = pScriptEngine->RegisterObjectBehaviour("c_scard", asBEHAVE_RELEASE, "void f()", asMETHOD(c_scard, Release), asCALL_THISCALL); if(s<0) AfxMessageBox( L"register behaviour release failed" ); // Register the function that the scripts will be allowed to use. // This also MUST be done BEFORE we build the script! Or you will get a runtime error. int ret = pScriptEngine->RegisterObjectMethod("c_scard", "void reconnect()", asMETHOD(c_scard,reconnect), asCALL_THISCALL); //int nRet = pScriptEngine->RegisterObjectMethod("myType","void ctest()", asFUNCTION(&CangelTestDlg::c_test), asCALL_CDECL); if(ret < 0) AfxMessageBox( L"register function failed" ); s = pScriptEngine->RegisterGlobalProperty("c_scard scard" , &m_scard); if(s<0) AfxMessageBox( L"register object failed" ); // Why do we need to build the script? // Scripts need to be compiled into byte code, that the virtual machine understands. asIScriptContext * pContext = NULL; ret = pScriptEngine->Build( "myModol" ); if( ret < 0 ) { printf("Failed to compile script/n"); goto skip_return; } // Create a context that will execute the script. // What is a "context" you ask? Well go read the documentation // for this tutorial and find out. :) pContext = pScriptEngine->CreateContext(); if( pContext == 0 ) { printf("Failed to create the context."); pScriptEngine->Release(); return; } // Register Script Function // If we had more functions in the module (script file) then we could register those // as well. Notice that our module is currently NULL which means we only have one // and don't intend to name it. int nFunctionID = pScriptEngine->GetFunctionIDByName("myModol", "main"); // Must "prepare" the function for execution // Prepare must be called to allow the context to prepare the function stack pContext->Prepare( nFunctionID ); // Execute the script function pContext->Execute( ); skip_return: // Release the engine pScriptEngine->Release(); // We must release the context(s) when no longer using them if ( pContext ) { pContext->Release(); } //OnOK(); } //----------------------------------------------------------------------------- // Name: LoadScript() [global function] // Desc: Reads a file specified by "filename" parameter and creates a new // Angelscript Section with the name specified by "module" parameter. //----------------------------------------------------------------------------- int LoadScript(asIScriptEngine * pScriptEngine, const char *filename, const char * module) { // Read the entire script file FILE * pFile = fopen(filename, "rb"); if( pFile == 0 ) { printf("Failed to open the script file./n"); return -1; } // Find the length of the script file fseek(pFile, 0, SEEK_END); int len = ftell(pFile); fseek(pFile, 0, SEEK_SET); // On Win32 it is possible to do the following instead // int len = _filelength(_fileno(f)); std::string code; code.resize(len); // Read length of script file size_t c = fread(&code[0], len, 1, pFile); fclose(pFile); if( c == 0 ) { printf("Failed to load script file./n"); return -1; } // Give the code to the script engine int nRet = pScriptEngine->AddScriptSection(module, filename, code.c_str(), len, 0); if( nRet < 0 ) { printf("An error occured while adding the script section./n"); } // At this point the engine has copied the code to an // internal buffer so we are free to release the memory we // allocated. // We can also add other script sections if we would like. // All script sections will be compiled together as if they // where one large script. return nRet; }
导入脚本的C++类:class c_scard { public: void AddRef() {refCount++;} void Release() {refCount--; if( refCount == 0 ) delete this;} int refCount; void reconnect(){AfxMessageBox(_T("reconnect invoked by script!"));}; protected: private: };scriptstring是他的字符串扩展库,还有一些,用法差不多,我也没用所以就不知道了。至于怎么让vc找到lib库,还有一些比较浅显的我就不说了,自己多看看应该能明白,如果有问题可以留言。相关资源:这里有两篇关于angelscript的文章,我借鉴了很多,在此表示感谢。http://huanzizone.blog.sohu.com/81849208.html下面是外国人写的指南,用来入门很不错。http://www.gameengineer.net/tutorials-angelscript.html