Ну, у меня есть абстрактная виртуальная машина (пешка«), который выполняется из моего кода, и сценарии могут выполнять функции, эти функции регистрируются в сценарии из кода C, который выполняется моим кодом C ++.
Код C ++ должен предоставлять массив в виде
{ "name_i_want_the_function_to_have_in_the_script" , function_in_my_cpp_code }
если функция отсутствует в массиве, она не может быть выполнена. (потому что он не «существует»)
Итак, это подводит нас к этому:
Мои функции выглядят так:
//Pawn Functions
#define PWNFUNC(a) static cell AMX_NATIVE_CALL a(AMX *amx, cell *params)
namespace PawnFunc
{
PWNFUNC(GGV)
{
return pGameInterface->FindGameVersion();
}
};//namespace PawnFunc
и массив с информацией о функциях сценариев находится в другом файле, например:
AMX_NATIVE_INFO custom_Natives[] =
{
{ "GetGameVersion", PawnFunc::GGV },
{ 0,0 }
};
и вопрос сейчас:
Можно ли сделать этот массив автоматически обновляется? (до / во время компиляции или во время инициализации кода)
на данный момент я должен добавить каждую функцию вручную. Что иногда раздражает и более подвержено ошибкам.
Я хотел бы изменить это, чтобы я мог сделать:
//Pawn Functions
#define PWNFUNC(a,b) ...?...
namespace PawnFunc
{
PWNFUNC(GGV,GetGameVersion)//{ "GetGameVersion", PawnFunc::GGV }, is now added to "custom_Natives" array
{
return pGameInterface->FindGameVersion();
}
};//namespace PawnFunc
Это вообще возможно? Если да, как я могу этого достичь?
может быть, можно зациклить пространство имен?
Изменить: вот какой-то псевдокод: http://ideone.com/btG2lx
И также примечание: я могу сделать это во время выполнения, но тогда это должно быть сделано в DLLMain (моя программа — DLL).
это #define
будет делать работу, если вы используете std::vector
в качестве хранилища для вашей информации скрипта.
(Обратите внимание, что стандарт гарантирует, что вы все равно получите массив в стиле C от &custom_Natives[0]
)
std::vector<AMX_NATIVE_INFO> custom_Natives;
#define PWNFUNC(NAME, FUNC) \
struct IMPL_ ## FUNC { \
IMPL_ ## FUNC() { \
AMX_NATIVE_INFO entry = { NAME, PawnFunc::FUNC }; \
custom_Natives.push_back( entry ); \
} \
} INSTANCE_ ## FUNC; \
static cell AMX_NATIVE_CALL FUNC(AMX *amx, cell *params)
Теперь такой код будет определять функцию а также добавить запись скрипта в custom_Natives
,
PWNFUNC("GetGameVersion", GGV)
{
return pGameInterface->FindGameVersion();
}
Что я мог бы придумать (предполагая массивы в стиле C и функции C-linkage):
AMX_NATIVE_INFO custom_natives[] =
{
{ "GetGameVersion", TheGGVFunc },
{ 0, 0 }
};
// here a function call named `GetGameVersion` was encountered,
// so let's look it up using a naive linear search
const char *toBeCalled = "GetGameVersion"; // obtain this somehow
void (*fptr)(void) = NULL;
for (int i = 0; i < sizeof(custom_natives) / sizeof(*custom_natives) - 1; i++) {
const char *name = custom_natives[i].name;
if (strcmp(toBeCalled, name) == 0) {
fptr = custom_natives[i].func;
break;
}
}
if (fptr != NULL) {
fptr();
}
Вы можете приблизить это; идея заключается в использовании глобального std::vector
вместо массива C, и использовать конструкторы глобальных объектов для расширения вектора. Таким образом, ваш массив будет инициализирован ко времени main()
начинает выполнять. Так что вместо custom_Natives
массив у вас будет
std::vector<MethodArrayElementType> custom_Natives;
вектор (заменить MethodArrayElementType
с именем структуры, которая содержит строку -> отображение указателя на функцию). Вы можете рассматривать этот вектор как обычный массив C, используя &custom_Natives[0]
,
Затем, рядом с каждой определяемой вами функцией, вы добавляете немного Registrar
Класс для регистрации метода:
PWNFUNC(GGV) {
// Your implementation goes here...
}
struct GGV_Registrar {
GGV_Registrar() {
MethodArrayElementType e = { "GetGameVersion", GGV };
custom_Natives.push_back( e );
};
} GGV_Registrar_instance;
Конструктор глобального GGV_Registrar_instance
конструктор будет вызываться раньше main()
называется, и он будет обновлять custom_Natives
вектор.
Мы делаем что-то вроде этого, но вместо использования массива мы используем связанный список. Таким образом, ваш пример станет
namespace PawnFunc
{
PWNFUNC(GGV)
{
return pGameInterface->FindGameVersion();
}
PawnRegister GGVfunc( "GetGameVersion", GGV );
};//namespace PawnFunc
Конструктор для PawnRegister добавляет все объекты (например, GVVfunc) в связанный список. Когда ваш скрипт-движок хочет найти функцию, он просматривает список, а не сканирует массив. Я полагаю, вы могли бы настроить PawnRegister, чтобы вместо этого добавлять записи в массив.