AngelScript

Введение


В процессе рассмотрения LUA и Python я выделил для себя, что LUA является достаточно быстрым, но с немного непривычным синтаксисом. Python же обладает очень простым синтаксисом и массой полезных библиотек, но, к сожалению, он оказался довольно медленным, и его довольно тяжело привязывать к С++. И тут на работе мне подсказали использовать AngelScript, мол, он удобный для связки, быстрее LUA и имеет С-подобный синтаксис. Как только я начал его изучать, я понял, что это тот самый скриптовый язык моей мечты.

Превью


Вот что можно прочитать про этот язык на Википедии:

AngelScript представляет собой движок, в котором приложение может регистрировать функции, свойства и типы, которые могут использоваться в скриптах. Скрипты компилируются в модули. Количество используемых модулей варьрируется в зависимости от нужд. Приложение может также использовать различные интерфейсы для каждого модуля с помощью групп конфигурации. Это особенно полезно, когда приложение работает с несколькими типами скриптов, например, GUI, AI и т.д.

Программа «Hello, world» в простейшем случае выглядит так:

void main() 
{ 
   print("Hello world\n"); 
}  


Да, синтаксис языка радует с самого начала. Язык поддерживает как методы функционального программирования, так и ООП. С самого начала он подкупает своей простотой регистрации функций, переменных, типов.

Например, регистрация глобальной переменной:

g_Engine->RegisterGlobalProperty("int SomeVal",&SomeVal);  


где SomeVal — это переменная типа int.

Регистрация глобальной функции:
g_Engine->RegisterGlobalFunction("void Print(string val)", 
                                 asFUNCTION(Print), asCALL_CDECL); 

void Print(string val) 
{ 
   cout<<val.data(); 
}


Да, для AngelScript не нужно писать функции биндинга, что является огроменным плюсом в сравнении с другими языками. Для регистрации своих типов придётся написать парочку функций. Фабрику для создания экземпляров и счётчик ссылок, для типа Тип-ссылка, и вызовы конструктора и деструктора, для объекта типа Тип-значение.

Например, у нас есть класс float3, который мы хотели бы зарегистрировать.

// класс счётчик ссылок
class RefC
{
private:
     int refC;
public: 
     RefC(){refC=1;} 
     void AddRef(){refC++;} 
     void Release() 
     { 
          if(!--refC) 
               delete this; 
     } 
};

// Класс, который мы хотим зарегистрировать 
class float3:public RefC 
{
public:
     float x;
     float y;
     float z;


     float3(){x=y=z=0;}

     void Normalize()
     {
          float Len=sqrt(x*x+y*y+z*z);
          Len=Len?Len:1;
          x/=Len;
          y/=Len;
          z/=Len;
     }
}

// Фабрика
float3* Float3FactoryE()
{
    return new float3();
}

// Функция вывода на экран
void PrintF3(float3* val)
{
    cout<<"x="<<val->x<<",y="<<val->y<<",z="<<val->z;
}


Для этого мы регистрируем объект как Тип-ссылка и указываем ему фабрику, счётчик ссылок, метод и функцию вывода данных на экран, и вот как это выглядит.

g_Engine->RegisterObjectType("float3",0,asOBJ_REF);

g_Engine->RegisterObjectMethod("float3"," void Normalize()",asMETHOD(float3, Normalize),asCALL_THISCALL);
g_Engine->RegisterObjectBehaviour("float3",asBEHAVE_FACTORY,"float3@ new_float3()",asFUNCTION(Float3FactoryE),asCALL_CDECL);
g_Engine->RegisterObjectBehaviour("float3",asBEHAVE_ADDREF,"void AddRef()",asMETHOD(float3,AddRef),asCALL_THISCALL);
g_Engine->RegisterObjectBehaviour("float3",asBEHAVE_RELEASE,"void Release()",asMETHOD(float3,Release),asCALL_THISCALL);

g_Engine->RegisterGlobalFunction("void Print(float3@ val)",asFUNCTION(PrintF3),asCALL_CDECL);


Мы на этом конечно же не остановимся, так как нам нужен доступ к значениям xyz, поэтому их мы тоже должны зарегистрировать, что мы и делаем написав.
g_Engine->RegisterObjectProperty("float3","float x",offsetof(float3,x));
g_Engine->RegisterObjectProperty("float3","float y",offsetof(float3,y));
g_Engine->RegisterObjectProperty("float3","float z",offsetof(float3,z));


Всё предельно просто и понятно. Теперь в скрипте можно написать
float3@ ObjPos;
ObjPos.x=1;
ObjPos.y=2;
ObjPos.z=3;

ObjPos.Normalize();
Print( ObjPos );


Выполнив этот скрипт, мы увидим на экране значение нормализованного вектора.

Особенности



Меня очень порадовала возможность перегрузки операторов в AngelScript. В С++ для это существует ключевое слово operator и символ оператора. AngelScript для этого использует определённые функции.

¦ – opNeg
¦ ~ opCom
¦ ++ opPreInc
¦ — opPreDec
¦ ++ opPostInc
¦ — opPostDec
¦ == opEquals
¦ != opEquals
¦ < opCmp
¦ <= opCmp
¦ > opCmp
¦ >= opCmp
¦ = opAssign
¦ += opAddAssign
¦ -= opSubAssign
¦ *= opMulAssign
¦ /= opDivAssign
¦ &= opAndAssign
¦ |= opOrAssign
¦ ^= opXorAssign
¦ %= opModAssign
¦ <<= opShlAssign
¦ >>= opShrAssign
¦ >>>= opUShrAssign
¦ + opAdd opAdd_r
¦ — opSub opSub_r
¦ * opMul opMul_r
¦ / opDiv opDiv_r
¦ % opMod opMod_r
¦ & opAnd opAnd_r
¦ | opOr opOr_r
¦ ^ opXor opXor_r
¦ << opShl opShl_r
¦ >> opShr opShr_r
¦ >>> opUShr opUShr_r
¦ [] opIndex

Допустим, мы хотим сделать возможность, чтобы наш вектор поддерживал прибавление к себе другого вектора, для этого слегка модифицируем наш класс.

class float3:public RefC 
{
public:
     float x;
     float y;
     float z;

     float3(){x=y=z=0;}

     void Normalize()
     {
          float Len=sqrt(x*x+y*y+z*z);
          Len=Len?Len:1;
          x/=Len;
          y/=Len;
          z/=Len;
     }

     float3* operator+=(float3* _rval)
     {
          x+=_rval->x;
          y+=_rval->y;
          z+=_rval->z;
          this->AddRef();
          return this;
     } 
};


Осталось только зарегистрированный новый метод.
g_Engine->RegisterObjectMethod("float3", "float3@ opAddAssign(float3@ _rval)",
                               asMETHOD(float3, operator+=), asCALL_THISCALL);


И теперь можно спокойно писать так:

float3@ ObjPos;
ObjPos.x=1;
ObjPos.y=2;
ObjPos.z=3;

float3@ ObjOffset;
ObjOffset .x=3;
ObjOffset .y=1;
ObjOffset .z=5;

ObjPos+=ObjOffset ; 
Print( ObjPos );  

и мы увидим на экране x=4, y=3, z=8.

AngelScript поддерживает свойства. Выглядит это так:
class  MyObj 
{
     type get_ValueName();
     type set_ValueName(type Val);
}
MyObj a; 
type tmp=a.ValueName;// вызовется get_ValueName
a.ValueName = tmp; // вызовется set_ValueName 


Также свойства поддерживаются для оператора индекса:
class MyObj
{
     float get_opIndex(int idx) ;
     void set_opIndex(int idx, float value);
}
MyObj a;
float val=a[1];// вызовется get_opIndex 
a[2]=val;// вызовется set_opIndex 


Полезные ссылки


Сайт разработчиков www.angelcode.com
SVN Репозиторий на WIP angelscript.svn.sourceforge.net/svnroot/angelscript/trunk
Русский мануал 13d-labs.com/angelscript_manual/main.html
Мануал на английском www.angelcode.com/angelscript/sdk/docs/manual/index.html
JIT Компилятор github.com/BlindMindStudios/AngelScript-JIT-Compiler
Поделиться публикацией

Комментарии 17

    0
    Воспользуйтесь тегом <сode>, читать будет легче!
      +14
      Воспользуйтесь тегом <source>…</source>, читать будет ещё легче!
        –3
        воспользуйтесь этим… тэгом и будет ещё лучше!
          0
          А если еще использовать перенос строк и отступы то будет совсем идеально.
            0
            Извините, уважаемый хабр съел мой тег. Короче: добавьте ещё в тег lang=«cpp» и всё будет совсем клёво!
            А по сабжу: статья хорошая, было интересно, но есть малюсенький минус: где это использовать? для чего и как? А прежде всего: зачем?
            Буду рад услышать ответы на эти вопросы. Да даже, если и не услышу, я ужерад!
              0
              Спасибо за замечания, поправил.
              Используется это для того чтобы часть логики своего приложения можно было вывести за пределы исходного кода, как это уже другой вопрос. Скриптовые движки активно используются в играх.
          0
          Лучше тегом source
          0
          Почувствовал едва уловимое дежавю от вступления, а Ваша статья была на gamedev.ru, оказывается :)

          Интересно, эта информация еще актуальна? Мне всегда импанировали AngelScript и Squirrel, но lua всегда может чем-то ответить своим конкурентам :)

            0
            Перегрузка операторов из D, любопытно.
            • НЛО прилетело и опубликовало эту надпись здесь
                +3
                Смотрим сюда: www.gamedev.ru/code/articles/AngelScript

                А теперь сюда: habrahabr.ru/info/help/rules/
                Хабр — не ЖЖ и не центр мирового кросспостинга. Не нужно копировать посты из других блогов и сайтов, указывая, что ранее они были опубликованы в другом месте.
                  +1
                  Строго говоря, он ведь не нарушил правило :) Т.к. не указывал, что эта статья ранее была опубликована на gamedev.ru ;)
                    0
                    Сильно подозреваю, что кросспостить не указывая оригинал — ещё хуже :)
                      0
                      Ну я же репост своей статьи делал, так что не думаю что я нарушил правила.
                        0
                        В правилах не уточняется насчёт «своих» или «чужих». Значит любых.
                  +1
                  Не хочу холивара, но Си-подобный синтаксис — очень спорная штука, записывать ее в плюсы не стоит.
                  Лично мне, наоборот, отсутствие точек с запятой и фигурных скобок помогает сконцентрироваться на самой задаче, а не средствах ее реализации.
                    0
                    Я отнёс это в плюс в основном потому что, если скорость работы скрипта не будет удовлетворять, то код скрипта можно будет вынести с минимальными изменениями.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое