Оглавление

Регистрация функции

Эта статья призвана обьяснить методы регистрации функций в AngelScript и некоторые различия между C++ и AngelScript.

Описанные тут принципы используются в нескольких функциях, таких как RegisterGlobalFunction, RegisterObjectMethod, RegisterObjectBehaviour, RegisterGlobalBehaviour, и т.д..

Как получить адрес функции или метода

Макросы asFUNCTION, asFUNCTIONPR, asMETHOD, и asMETHODPR были введены для облегчения задачи получения указателя на функцию, передавая их в скриптовый движок.

Макрос asFUNCTION принимает имя функции как параметр, который работает для всех глобальных функций и не может быть перегружен. Если вы хотите использовать перегруженную функцию с одним и тем же именем, но с разными параметрами, вам пригодиться макрос asFUNCTIONPR. Он принимает в качестве параметра имя функции, список параметров и возвращаемый тип, так что компилятор C++ может определить какая функция перегружена, чтобы вернуть ее адрес.

// Глобальная функция
void globalFunc();
r = engine->RegisterGlobalFunction("void globalFunc()", asFUNCTION(globalFunc), asCALL_CDECL); assert( r >= 0 );

// Перегруженная глобальная функция
void globalFunc2(int);
void globalFunc2(float);
r = engine->RegisterGlobalFunction("void globalFunc2(int)", asFUNCTIONPR(globalFunc2, (int), void), asCALL_CDECL); assert( r >= 0 );

Аналогично работают asMETHOD/asMETHODPR. Отличия от asFUNCTION/asFUNCTIONPR заключаются в том, что методам необходимо дополнительно указывать имя класса, к которому они принадлежат.

class Object
{
  // Метод класса
  void method();
  
  // Перегруженный оператор присваивания
  Object &operator=(const Object &);
  Object &operator=(int);
  
  // Перегруженный метод
  void method2(int input);
  void method2(int input, int &output);
  
  // Константный метод
  int getAttr(int) const;
};

// Регистрируем метод класса
r = engine->RegisterObjectMethod("object", "void method()", asMETHOD(Object,method), asCALL_THISCALL); assert( r >= 0 );

// Регистрируем оператор присваивания
r = engine->RegisterObjectBehaviour("object", asBEHAVE_ASSIGNMENT, "object &f(const object &in)", asMETHODPR(Object, operator=, (const Object&), Object&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("object", asBEHAVE_ASSIGNMENT, "object &f(int)", asMETHODPR(Object, operator=, (int), Object&), asCALL_THISCALL); assert( r >= 0 );

// Регистрируем перегруженный метод
r = engine->RegisterObjectMethod("object", "void method2(int)", asMETHODPR(Object, method2, (int), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("object", "void method2(int, int &out)", asMETHODPR(Object, method2, (int, int&), void), asCALL_THISCALL); assert( r >= 0 );

// Регистрируем константный метод
r = engine->RegisterObjectMethod("object", "int getAttr(int) const", asMETHODPR(Object, getAttr, (int) const, int), asCALL_THISCALL); assert( r >= 0 );

Соглашение о вызовах

AngelScript использует общие соглашения о вызовах, которые использует C++ (cdecl, stdcall, thiscall). Так же существуют родные соглашения о вызовах, специальные для целевой платформы. Все функции и отношения должны быть зарегестрированы как asCALL_CDECL, asCALL_STDCALL, asCALL_THISCALL, или asCALL_GENERIC. Эти флаги указывают скриптовому движку какой тип вызова используется. Специальные типы - asCALL_CDECL_OBJLAST и asCALL_CDECL_OBJFIRST могут быть использованы там, где принимается asCALL_THISCALL.

Виртуальное наследование не поддерживается

Регистрация методов класса для классов с виртуальными наследование не поддерживается в связи с высокой сложностью реализацией данного механизма. Это не является большой потерей, так как классы с виртуальным наследстванием довольно редки, и легко написать свои прокси-классы.
class A { void SomeMethodA(); };
class B : virtual A {};
class C : virtual A {};
class D : public B, public C {};

// Требуется прокси-функция для регистрации метода SomeMethodA класса D
void D_SomeMethodA_proxy(D *d)
{
  // Компилятор C++ определит виртуальный метод для нас
  d->SomeMethodA();
}

// Регистрируем глобальную функцию как метод класса, но используя asCALL_CDECL_OBJLAST
engine->RegisterObjectMethod("D", "void SomeMethodA()", asFUNCTION(D_SomeMethodA_proxy), asCALL_CDECL_OBJLAST);

Если у вас есть много классов с виртуальным наследстванием, вам стоит подумать о написании шаблонов для прокси-функций, что вам не пришлось вручную писать все функции.

Небольшие отличия типов

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

Все примитивные типы С++ соответствуют типам AngelScript, хотя иногда отличаются названием, например char в C++, но int8 в AngelScript. Вы можете увидеть список всех типов и их соответствие тут.

Указатели не существуют в AngelScript в таком виде, как в C++, поэтому необходимо решить, как их передавать. Для этого у вас есть два варианта, лиюо как ссылку, либо как хендл. Большинство используемых указателей на объекты, представленные в виде ссылок\хендлов, легко обрабатываеются в AngelScript, но для некоторых необходимо написать более простой интерфейс, чтобы враппер справился.

Параметры ссылки в AngelScript имеют дополнительные ограничения. Вы должны указать предполагаемый тип параметра - входной\выходной, или сразу и тот и другой. Это делается с помощью ключей in, out, inout после &. Если ключ не указа, AngelScript подставит ключ inout автоматически. Типы значений могут быть только in и out, так как AngelScript не может гарантировать безопасность ссылок на них.

Хендлы - указатели на обьект с подсчетом ссылок, поэтому используя их обращайте внимание на механизм подсчета ссылок, методы создания и удаления обьекта. Если вы не готовы сами следить за подсчетом ссылок, используйте автохендл (@+).

Строки тут немного сложнее, чем на C++, так как нет стандарта их реализации, поэтому AngelScript предлагает вам пару оберток (в аддонах).


Перевод - arroy.one@gmail.com. При копировании материалов указывайте ссылку на источник.