Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 02:40
Оценка:
Интегрирую данный движок в свой проект. В процессе столкнулся с проблемой создания "переходника" из унифицированной сигнатуры колбека в разные сигнатуры экспортируемых функций. Оставляя за кадром подробности, выглядит это примерно так:
  Скрытый текст
//экспортируемая функция
int SomeFunc(bool arg1, int arg2)
{
    //код
}

//shim function
//Указатель на shim передаётся движку v8 (то есть его сигнатура фиксированная и менять её нельзя).
void SomeFuncShim(const FunctionCallbackInfo<Value>& info)
{
    //проверка совпадения кол-ва параметром с ожидаемым
    auto argsCnt = info.Length();
    if (argsCnt != 2)
    {
        //ругаемся матом и прекращаем выполнение скрипта, код пропущен
        return;
    }

    //достаём аргументы

    //первый
    Local<Value> arg1 = info[0];
    //распаковываем в нужный тип
    bool arg1Val = arg1->Value();

    //второй
    Local<Value> arg2 = info[1];
    int arg2Val = arg2->Value();

    //прочие элементарные типы обрабатываются аналогично, за исключением строк - для них отдельный код распаковки

    //распаковываем указатель на целевую функцию
    auto funcPtr = static_cast<int(*)(bool arg1, int arg2)>(wrap->Value());

    //вызываем собственно функцию
    int result = funcPtr(arg1Val, arg2Val);


    //упаковываем результат и устанавливаем возвращаемое значение
    info.GetReturnValue().Set(result);
}

Хотелось бы как-то замутить универсальный "переходник" дабы не педалить кучу однотипного кода для разного количества и типов параметров и возвращаемого значения, но тут возникают следующие проблемы:
1. Распаковка параметра в зависимости от целевого типа. Это можно сделать через частичную специализацию по типу такого:
  Скрытый текст
template<typename T> T UnpackParameter(const Local<Value>& val)
{
    throw std::exception("no specialization exist");
}

template<> int UnpackParameter(const Local<Value>& val)
{
    auto arg = val->ToInt32();
    return arg->Value();
}

template<> bool UnpackParameter(const Local<Value>& val)
{
    auto arg = val->ToBoolean();
    return arg->Value();
}
//и т.п. для других типов
//использовать так:
auto arg1 = UnpackParameter<bool>(info[0]);

2. Распаковка указателя на функцию:
auto funcPtrVal = info.Data();
auto wrap = Local<External>::Cast(funcPtrVal);
void* funcPtrVoid = wrap->Value();
auto funcPtr = static_cast<int(*)(bool arg1, int arg2)>(funcPtrVoid); //откуда взять тип функции?

Вот тут я забуксовал в первый раз
3. Распаковка всех параметров в зависимости от сигнатуры, а также обратная упаковка возвращаемого значения (если оно есть). Тут тоже затык.

Регистрация функции выглядит так:
global->Set(String::NewFromUtf8(isolate, "SomeFunc"), FunctionTemplate::New(isolate, SomeFuncShim, External::New(isolate, SomeFunc)));


Я подозреваю, что тут можно как-то заюзать variadic templates, но мой опыт в этом деле в районе абсолютного нуля, посему прошу помощи у всезнающего all Надеюсь, что я достаточно подробно объяснил задачу.
[КУ] оккупировала армия.
Re: Интегрируем v8
От: bazis1 Канада  
Дата: 09.04.15 03:53
Оценка:
Здравствуйте, koandrew, Вы писали:

K>...

Из контекста сложно понять на 100% всю решаемую задачу, но рискну предположить, что Вы хотите сохранить в неком объекте указатель на функцию + ее аргументы и потом ее унифицированно вызвать.
Это таки делается через variadic templates, но к счастью готовая реализация под названием std::bind() включена в библиотеку. Мы реализаией похожей задачи недавно тестировали наш новый IntelliSense-движок для VisualGDB, который все распарсил на ура. Реализуется наподобие вот этого:
class IInvokable
{
public:
    virtual void Invoke() = 0;
    virtual ~IInvokable(){}
};

template<typename _Functor, typename _Result> class Invoker : public IInvokable
{
private:
    _Functor m_Functor;
    _Result m_Result;

public:
    Invoker(_Functor functor)
        : m_Functor(functor)
        , m_Result(_Result())
    {
    }

    virtual void Invoke() override
    {
        m_Result = m_Functor();
    }
};

template<typename _Func, typename... _Args> IInvokable *CreateInvoker(_Func func, _Args... args)
{
    auto functor = std::bind(func, args...);
    return new Invoker<decltype(functor), decltype(func(args...))>(functor);
}

Вызывать можно, например, так:
    IInvokable *pInvoker = CreateInvoker(printf, "Hello, %s!", "World");
    //...
    pInvoker->Invoke();  //таки напечатает "Hello, World!"
    delete pInvoker;
Re: Интегрируем v8
От: PM  
Дата: 09.04.15 07:32
Оценка: 6 (1) +1
Здравствуйте, koandrew, Вы писали:


K>Я подозреваю, что тут можно как-то заюзать variadic templates, но мой опыт в этом деле в районе абсолютного нуля, посему прошу помощи у всезнающего all Надеюсь, что я достаточно подробно объяснил задачу.


Порекламируюсь: https://github.com/pmed/v8pp

Библиотечку использую в разных проектах (embedded V8, Node.js addons) несколько лет. Недавно переделал на С++11 с variadic templates и выложил в публичный доступ.

Про детали реализации лень писать, даже документацию никак не сделаю Есть только немного примеров и тестов.

В частности, там для обертки указателей на С++ функции используется wrap_function_template()

Вот пример использования:

#include <node.h>
#include <v8pp/function.hpp>
#include <v8pp/object.hpp>

using namespace v8;

int MyFunction(int a, int b) {
  return a + b;
}

Handle<Function> CreateFunction(Isolate* isolate) {
  EscapableHandleScope scope(isolate);

  Local<FunctionTemplate> tpl = v8pp::wrap_function_template(isolate, &MyFunction);
  Local<Function> fn = tpl->GetFunction();

  // omit this to make it anonymous
  fn->SetName(v8pp::to_v8(isolate, "theFunction"));
  return scope.Escape(fn);
}

void Init(Handle<Object> exports, Handle<Object> module) {
  Isolate* isolate = Isolate::GetCurrent();
  v8pp::set_option(isolate, module, "exports", v8pp::wrap_function(isolate, "exports", &CreateFunction));
}

NODE_MODULE(addon, Init)
Re: Интегрируем v8
От: VTT http://vtt.to
Дата: 09.04.15 09:51
Оценка:
Здравствуйте, koandrew, Вы писали:

0. У меня очень редко выходит, что параметры в javascript функции четко соответствуют таковым в вызываемой функции. Обычно интерфейс для javascript максимально упрощен и при вызове одного javascript коллбэка производится целая цепочка действий (часть из которых может быть опущена при отсутствии определенного аргумента) или вызываются совсем разные методы. При возвращении результата и обработке ошибок часто тоже требуются дополнительные действия, которые проблематично упрятать в "упаковщик возвращаемого значения".
1. Пользы от дополнительной обертки для распаковки аргументов как-то не наблюдается:
UnpackParameter<int>(info[0]);
info[0]->ToInt32()->Value();

2. Тут я не очень понял, зачем понадобилось распаковывать (а перед этим запаковывать) указатель на функцию? Ведь и так понятно что в SomeFuncShim должна вызывать SomeFunc.
3. Из этого пункта становится понятно, что вам по сути нужна библиотека, генерирующая фасад для прямой проброски C/C++ функций в javascript. Она, конечно, будет полезна для работы с простыми функциями, навроде int SomeFunc(bool arg1, int arg2). Но для чего-то более сложного все равно придется писать много дополнительного кода. Я пошел по пути написания вспомогательных объектов для проверки параметров вызова javascript функции конкретной сигнатуре. Каждый такой объект содержит общие директивы (например что вызов функции должен быть конструктором, или что this должен указывать на объект определенного класса), коллекцию проверок для каждого аргумента (тип, обязательность, диапазон допустимых значений и т.п.); умеет проверять параметры вызова на соответствие и формировать текст для сообщения об ошибке.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Отредактировано 09.04.2015 9:55 VTT . Предыдущая версия .
Re[2]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 13:37
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>1. Пользы от дополнительной обертки для распаковки аргументов как-то не наблюдается:

Это понадобится в (3)

VTT>2. Тут я не очень понял, зачем понадобилось распаковывать (а перед этим запаковывать) указатель на функцию? Ведь и так понятно что в SomeFuncShim должна вызывать SomeFunc.

Все эти пункты были (как вижу, не очень успешной) попыткой объяснить задачу тем, что не знаком с v8. Дело в том, что по задумке один и тот же Shim будет использоваться для нескольких (многих) функций, посему адрес конкретной функции и передаётся как параметр Data.

VTT>3. Из этого пункта становится понятно, что вам по сути нужна библиотека, генерирующая фасад для прямой проброски C/C++ функций в javascript. Она, конечно, будет полезна для работы с простыми функциями, навроде int SomeFunc(bool arg1, int arg2). Но для чего-то более сложного все равно придется писать много дополнительного кода. Я пошел по пути написания вспомогательных объектов для проверки параметров вызова javascript функции конкретной сигнатуре. Каждый такой объект содержит общие директивы (например что вызов функции должен быть конструктором, или что this должен указывать на объект определенного класса), коллекцию проверок для каждого аргумента (тип, обязательность, диапазон допустимых значений и т.п.); умеет проверять параметры вызова на соответствие и формировать текст для сообщения об ошибке.

Дело в том, что мне нужно экспортировать пару сотен функций, и правило 80-20 здесь применяется во весь рост — 80% функций у меня простые по типу вышеуказанной, но при этом расброс возможных параметров (как их типов, как и количество) достаточно велик, чтобы следать рукопашную реализацию всех вариантов непрактичной. Посему мне хотелось бы сваять нечто, что смогло бы покрыть эти самые 80%. При этом я отдаю себе отчёт в том, что остальные 20% придётся педалить вручную, но 20% << 100%
[КУ] оккупировала армия.
Re[2]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 13:41
Оценка:
Здравствуйте, PM, Вы писали:

PM>Порекламируюсь: https://github.com/pmed/v8pp

Спасибо, посмотрю!

PM>Про детали реализации лень писать, даже документацию никак не сделаю Есть только немного примеров и тестов.

Надеюсь, что хоть по почте/чату/здесь вы доступны?

У меня дополнительная сложность в том, что нужна поддержка нескольких скриптовых языков, посему нужно будет делать дополнительный уровень абстракции между приложением и движком, и нельзя завязываться на детали специфичные именно для v8.
[КУ] оккупировала армия.
Re[2]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 13:50
Оценка:
Здравствуйте, bazis1, Вы писали:

B>Из контекста сложно понять на 100% всю решаемую задачу, но рискну предположить, что Вы хотите сохранить в неком объекте указатель на функцию + ее аргументы и потом ее унифицированно вызвать.


Нет, к сожалению, это совсем из другой оперы. v8 — это движок JavaScript (который используется в Хроме и прочих хромиумах).
[КУ] оккупировала армия.
Re[3]: Интегрируем v8
От: PM  
Дата: 09.04.15 14:07
Оценка: 6 (1) +1
Здравствуйте, koandrew, Вы писали:


PM>>Про детали реализации лень писать, даже документацию никак не сделаю Есть только немного примеров и тестов.

K>Надеюсь, что хоть по почте/чату/здесь вы доступны?

Да, почта/чат по адресу в профиле, что здесь, что на Github.

K>У меня дополнительная сложность в том, что нужна поддержка нескольких скриптовых языков, посему нужно будет делать дополнительный уровень абстракции между приложением и движком, и нельзя завязываться на детали специфичные именно для v8.


Не знаю, насколько абстрактно тут может получиться. Теоретически может, т.к. подобные обвязки для Python (boost.python) и Lua (https://github.com/jeremyong/Selene) сделаны примерно на тех же принципах.
Re[3]: Интегрируем v8
От: VTT http://vtt.to
Дата: 09.04.15 17:41
Оценка: 6 (1)
Здравствуйте, koandrew, Вы писали:

K>Дело в том, что мне нужно экспортировать пару сотен функций, и правило 80-20 здесь применяется во весь рост — 80% функций у меня простые по типу вышеуказанной


А вы не пробовали задействовать SWIG, там как раз в последних версиях добавили поддержку генерации оберток для v8? Конкретно для javascript мне его задействовать не приходилось, но для других языков (go, php) он дает вполне сносные результаты и берет на себя большую часть рутины.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Отредактировано 09.04.2015 17:49 VTT . Предыдущая версия .
Re[4]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 17:54
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>А вы не пробовали задействовать SWIG, там как раз в последних версиях добавили поддержку генерации оберток для v8? Конкретно для javascript мне его задействовать не приходилось, но для других языков (go, php) он дает вполне сносные результаты и берет на себя большую часть рутины.


Вот спасибо! Посмотрю обязательно!
[КУ] оккупировала армия.
Re[4]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 18:02
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>А вы не пробовали задействовать SWIG, там как раз в последних версиях добавили поддержку генерации оберток для v8? Конкретно для javascript мне его задействовать не приходилось, но для других языков (go, php) он дает вполне сносные результаты и берет на себя большую часть рутины.


Что-то меня напрягла вот эта строка:

SWIG also works perfectly well under all known 32 bit versions of Windows including 95/98/NT/2000/XP.

У меня проект 64bit only и Win7+ only...
[КУ] оккупировала армия.
Re[3]: Интегрируем v8
От: bazis1 Канада  
Дата: 09.04.15 18:10
Оценка:
Здравствуйте, koandrew, Вы писали:

K>Здравствуйте, bazis1, Вы писали:


B>>Из контекста сложно понять на 100% всю решаемую задачу, но рискну предположить, что Вы хотите сохранить в неком объекте указатель на функцию + ее аргументы и потом ее унифицированно вызвать.


K>Нет, к сожалению, это совсем из другой оперы. v8 — это движок JavaScript (который используется в Хроме и прочих хромиумах).

Т.е. после того, как Вы зарегистрировали функцию в движке, вам будет приходить массив элементов некого универсального типа (типа OLEVARIANT из Windows), и Вы хотите это автоматически сконвертировать в нормальный вызов нативной функции?
Re[5]: Интегрируем v8
От: VTT http://vtt.to
Дата: 09.04.15 18:25
Оценка:
Здравствуйте, koandrew, Вы писали:

K>

SWIG also works perfectly well under all known 32 bit versions of Windows including 95/98/NT/2000/XP.

K>У меня проект 64bit only и Win7+ only...

Видно у них некому обновить страницу. В загрузках для windows исполняемый файл SWIG действительно только 32 битный, но это не важно. Собственно SWIG занимается только генерацией кода-обертки. Собирается она затем обычным компилятором. Особых проблем со сгенерированным кодом на 64-битном линуксе не было.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[4]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 18:53
Оценка:
Здравствуйте, bazis1, Вы писали:

B>Т.е. после того, как Вы зарегистрировали функцию в движке, вам будет приходить массив элементов некого универсального типа (типа OLEVARIANT из Windows), и Вы хотите это автоматически сконвертировать в нормальный вызов нативной функции?


Да, именно так. Я на вход получаю некую структуру данных, из которой я могу вытянуть входные параметры (в виде v8::Value, которое по сути своей аналог комовского варианта), и туда же мне нужно будет положить возвращаемое значение (упавовав его соответствующим образом ), которое передастся в скрипт.
То есть проблема в том, что тут очень много boilerplate кода по распаковке-упаковке (который, как вы понимаете, разный для разных кобминаций входных параметров и их типов). Вот эту часть и хотелось бы автоматизировать. Как я уже отметил, инстинкт подсказывает мне, что тут нужно юзать variadic templates, но как их готовить, я не знаю, ибо не имею практического опыта их применения.
[КУ] оккупировала армия.
Re[6]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 09.04.15 19:12
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Видно у них некому обновить страницу. В загрузках для windows исполняемый файл SWIG действительно только 32 битный, но это не важно. Собственно SWIG занимается только генерацией кода-обертки. Собирается она затем обычным компилятором. Особых проблем со сгенерированным кодом на 64-битном линуксе не было.


А скажите — этот самый сгенерированный код нужно потом самому линковать с соответствующим(и) бэкендами для каждого языка, или у них есть некие — возможно уже допиленные ими напильником — версии?
[КУ] оккупировала армия.
Re[7]: Интегрируем v8
От: VTT http://vtt.to
Дата: 09.04.15 19:55
Оценка:
Здравствуйте, koandrew, Вы писали:

K>А скажите — этот самый сгенерированный код нужно потом самому линковать с соответствующим(и) бэкендами для каждого языка, или у них есть некие — возможно уже допиленные ими напильником — версии?


В том же php расширения были в виде динамических библиотек, и php к ним не линковался. В вашем случае наверное v8 и так встраивается в ваше приложение. Так или иначе, каких-то специальных версий для бэкэндов у SWIG я не видел.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[5]: Интегрируем v8
От: bazis1 Канада  
Дата: 09.04.15 19:57
Оценка: 9 (1)
Здравствуйте, koandrew, Вы писали:

K>Да, именно так. Я на вход получаю некую структуру данных, из которой я могу вытянуть входные параметры (в виде v8::Value, которое по сути своей аналог комовского варианта), и туда же мне нужно будет положить возвращаемое значение (упавовав его соответствующим образом ), которое передастся в скрипт.

Тогда это делается так:
Возьмем для простоты dummy Variant, который конвертируется только в int
struct Variant
{
    int Val;

    Variant(int val)
    {
        Val = val;
    }
};

template <typename _Ty> struct VariantConverter
{
};

template<> struct VariantConverter<int>
{
    static int FromVariant(const Variant &var)
    {
        return var.Val;
    }
};

задача нашего Invoker-класса будет в том, чтобы взять вектор Variant-ов и вызвать внутреннюю функцию, распаковав каждый Variant, вызвав нужную версию ConvertVariant():
class IInvokable
{
public:
    virtual void Invoke(const std::vector<Variant> &variants) = 0;
    virtual ~IInvokable(){}
};

template<typename _Result, typename... _Args> class Invoker : public IInvokable
{
private:
    _Result (*m_pFunc)(_Args...);

private:
    template <typename _Ty, size_t _Index> int GetArg(const std::vector<Variant> &variants)
    {
        return VariantConverter<_Ty>::FromVariant(variants[_Index]);
    }

    template <size_t... _Indicies> void DoInvoke(const std::vector<Variant> &variants, std::_Arg_idx<_Indicies...>)
    {
        m_pFunc((GetArg<_Args, _Indicies>(variants))...);
    }

public:
    Invoker(_Result(*pFunc)(_Args...))
        : m_pFunc(pFunc)
    {
    }

    void Invoke(const std::vector<Variant> &variants)
    {
        DoInvoke(variants, std::_Make_arg_idx<_Args...>::type());
    }
};

template<typename _Result, typename... _Args> IInvokable *CreateInvoker(_Result(*pFunc)(_Args...))
{
    return new Invoker<_Result, _Args...>(pFunc);
}

Пример использования:
int sum(int a, int b)
{
    return a + b;
}

int main()
{
    std::vector<Variant> vec = { 1, 2 };
    IInvokable *pInvoker = CreateInvoker(sum);
    pInvoker->Invoke(vec);
}


DoInvoke() с std::_Arg_idx — это калька с std::bind() и, скорее всего, при желании ее можно упростить раза в 2.
Re[6]: Интегрируем v8
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 10.04.15 02:02
Оценка:
Здравствуйте, bazis1, Вы писали:

B>Тогда это делается так:

Огромное спасибо! Это уже намного ближе к тому, что мне нужно. Теперь осталось решить только одну проблему. Дело в том, что движок требует следующую сигнатуру для метода-колбека:
void Callback(const FunctionCallbackInfo<Value>& info)

Я пока вот что придумал (заодно модифицировал ваш код под реальные типы):
  Код
template <typename _Ty> struct VariantConverter
{
};

template<> struct VariantConverter<int>
{
    static int FromVariant(const Local<Value> &val)
    {
        auto arg = val->ToInt32();
        return arg->Value();
    }

    static Local<Value> ToVariant(Isolate * isolate, int val)
    {
        return Int32::New(isolate, val);
    }
};

template<> struct VariantConverter<bool>
{
    static bool FromVariant(const Local<Value> &val)
    {
        auto arg = val->ToBoolean();
        return arg->Value();
    }

    static Local<Value> ToVariant(Isolate * isolate, bool val)
    {
        return Boolean::New(isolate, val);
    }
};

template<> struct VariantConverter<std::wstring>
{
    static std::wstring FromVariant(const Local<Value> &val)
    {
        Local<String> str = val->ToString();
        auto len = str->Length();
        auto buf = new wchar_t[len + 1];
        str->Write(reinterpret_cast<uint16_t*>(buf));
        std::wstring result = buf;
        delete[]buf;
        return result;
    }

    static Local<Value> ToVariant(Isolate * isolate, std::wstring val)
    {
        return String::NewFromTwoByte(isolate, (const uint16_t*)val.c_str());
    }
};



class IInvokable
{
public:
    virtual Local<Value> Invoke(Isolate * isolate, const std::vector<Local<Value>> &variants) = 0;
    virtual void (*GetShim())(const FunctionCallbackInfo<Value>& info) = 0;
    virtual ~IInvokable(){}
};

template<typename _Result, typename... _Args> class Invoker : public IInvokable
{
private:
    _Result(*m_pFunc)(_Args...);

private:
    template <typename _Ty, size_t _Index> int GetArg(const std::vector<Local<Value>> &variants)
    {
        return VariantConverter<_Ty>::FromVariant(variants[_Index]);
    }

    template <size_t... _Indicies> Local<Value> DoInvoke(Isolate * isolate, const std::vector<Local<Value>> &variants, std::_Arg_idx<_Indicies...>)
    {
        auto result = m_pFunc((GetArg<_Args, _Indicies>(variants))...);
        return VariantConverter<_Result>::ToVariant(isolate, result);
    }

public:
    Invoker(_Result(*pFunc)(_Args...))
        : m_pFunc(pFunc)
    {
    }

    Local<Value> Invoke(Isolate * isolate, const std::vector<Local<Value>> &variants)
    {
        return DoInvoke(isolate, variants, std::_Make_arg_idx<_Args...>::type());
    }

    static void ShimFunc(const FunctionCallbackInfo<Value>& info)
    {
        auto argsCountRequired = sizeof...(_Args);
        auto argsCountPassed = info.Length();
        if (argsCountRequired != argsCountPassed)
        {
            info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Bad parameters"));
            return;
        }
        std::vector<Local<Value>> args;
        for (auto i = 0U; i < argsCountRequired; ++i)
        {
            args.push_back(info[i]);
        }
        
        auto invokerData = info.Data();
        auto wrap = Local<External>::Cast(invokerData);
        void* invokerVoid = wrap->Value();
        auto invoker = static_cast<IInvokable*>(invokerVoid);
        auto result = invoker->Invoke(info.GetIsolate(), args);
        info.GetReturnValue().Set(result);
    }

    void (*GetShim())(const FunctionCallbackInfo<Value>& info)
    {
        return ShimFunc;
    }
};

template<typename _Result, typename... _Args> Invoker<_Result, _Args...> *CreateInvoker(_Result(*pFunc)(_Args...))
{
    return new Invoker<_Result, _Args...>(pFunc);
}

Использую так:
auto pInvoker = CreateInvoker(SomeFunc); //это ваша обёртка для инкапсуляции вызова функции
auto shim = pInvoker->GetShim(); //это адрес "переходника", который надо скормить движку. Внутри функция распаковывает указатель на инвокер, и использует его для вызова функции


На тестовой функции всё работает как должно. При компиляции выдаётся варнинг:
  Скрытый текст

1>Scripting\v8\V8ScriptingEngine.cpp(210): warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)
1> Scripting\v8\V8ScriptingEngine.cpp(222) : see reference to function template instantiation 'v8::Local<v8::Value> Invoker<int,bool,int>::DoInvoke<0x00,0x01>(v8::Isolate *,const std::vector<v8::Local<v8::Value>,std::allocator<_Ty>> &,std::_Arg_idx<0x00,0x01>)' being compiled
1> with
1> [
1> _Ty=v8::Local<v8::Value>
1> ]
1> Scripting\v8\V8ScriptingEngine.cpp(222) : see reference to function template instantiation 'v8::Local<v8::Value> Invoker<int,bool,int>::DoInvoke<0x00,0x01>(v8::Isolate *,const std::vector<v8::Local<v8::Value>,std::allocator<_Ty>> &,std::_Arg_idx<0x00,0x01>)' being compiled
1> with
1> [
1> _Ty=v8::Local<v8::Value>
1> ]
1> Scripting\v8\V8ScriptingEngine.cpp(221) : while compiling class template member function 'v8::Local<v8::Value> Invoker<int,bool,int>::Invoke(v8::Isolate *,const std::vector<v8::Local<v8::Value>,std::allocator<_Ty>> &)'
1> with
1> [
1> _Ty=v8::Local<v8::Value>
1> ]
1> Scripting\v8\V8ScriptingEngine.cpp(390) : see reference to class template instantiation 'Invoker<int,bool,int>' being compiled


Но с этим, я думаю, как-нить разберусь.

Ещё раз огромное спасибо за помощь и за урок вариадиковедения тоже!
[КУ] оккупировала армия.
Re[7]: упс...
От: bazis1 Канада  
Дата: 10.04.15 02:15
Оценка:
Здравствуйте, koandrew, Вы писали:

K>На тестовой функции всё работает как должно. При компиляции выдаётся варнинг:

K>
  Скрытый текст
K>

1>Scripting\v8\V8ScriptingEngine.cpp(210): warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)
1>> Scripting\v8\V8ScriptingEngine.cpp(222) : see reference to function template instantiation 'v8::Local<v8::Value> Invoker<int,bool,int>::DoInvoke<0x00,0x01>(v8::Isolate *,const std::vector<v8::Local<v8::Value>,std::allocator<_Ty>> &,std::_Arg_idx<0x00,0x01>)' being compiled
1>> with
1>> [
1>> _Ty=v8::Local<v8::Value>
1>> ]
1>> Scripting\v8\V8ScriptingEngine.cpp(222) : see reference to function template instantiation 'v8::Local<v8::Value> Invoker<int,bool,int>::DoInvoke<0x00,0x01>(v8::Isolate *,const std::vector<v8::Local<v8::Value>,std::allocator<_Ty>> &,std::_Arg_idx<0x00,0x01>)' being compiled
1>> with
1>> [
1>> _Ty=v8::Local<v8::Value>
1>> ]
1>> Scripting\v8\V8ScriptingEngine.cpp(221) : while compiling class template member function 'v8::Local<v8::Value> Invoker<int,bool,int>::Invoke(v8::Isolate *,const std::vector<v8::Local<v8::Value>,std::allocator<_Ty>> &)'
1>> with
1>> [
1>> _Ty=v8::Local<v8::Value>
1>> ]
1>> Scripting\v8\V8ScriptingEngine.cpp(390) : see reference to class template instantiation 'Invoker<int,bool,int>' being compiled


K>Но с этим, я думаю, как-нить разберусь.
Глюк, судя по-всему, здесь:
template <typename _Ty, size_t _Index> int GetArg(const std::vector<Local<Value>> &variants)

должно быть:
template <typename _Ty, size_t _Index> _Ty GetArg(const std::vector<Local<Value>> &variants)


K>Ещё раз огромное спасибо за помощь и за урок вариадиковедения тоже!

Меня самого любопытство одолело и я решил распотрошить std::bind()
P.S. При желании этот код можно упростить, выкинув:
1. Копирование в промежуточный vector
2. Создание Invokable-объекта. Вместо этого методы можно сделать static и передавать туда явно указатель на вызываемую функцию, которую shim вытащит из info.Data().
Отредактировано 10.04.2015 2:24 bazis1 . Предыдущая версия .
Re[8]: упс...
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 10.04.15 02:46
Оценка:
Здравствуйте, bazis1, Вы писали:


B>Глюк, судя по-всему, здесь:

Вы правы — поправил у себя. Теперь компилятор молчит, как партизанен

B>Меня самого любопытство одолело и я решил распотрошить std::bind()

B>P.S. При желании этот код можно упростить, выкинув:
B>1. Копирование в промежуточный vector
B>2. Создание Invokable-объекта. Вместо этого методы можно сделать static и передавать туда явно указатель на вызываемую функцию, которую shim вытащит из info.Data().
Это уже на завтра — мне ещё надо будет завтра как следует разобраться к текущем коде — а то в 10:30 вечера голова что-то туго соображает %)

У меня есть ещё бонусное задание — придумать, как обернуть похожим образом нестатические методы классов this класса (указатель на нативное представление JS-объекта) можно вытянуть из const FunctionCallbackInfo<Value>& info, которая передаётся в переходник. Есть какие-нить мысли на этот счёт?

P.S. Здесь правда есть ещё один очень тонкий момент, связанный с особенностью поведения this в JavaScript (он в принципе может указывать на что угодно) — но с этим в принципе несложно справиться, проверив имя класса объекта, который к нам попал.
[КУ] оккупировала армия.
Re[9]: упс...
От: bazis1 Канада  
Дата: 10.04.15 02:59
Оценка:
Здравствуйте, koandrew, Вы писали:

K>У меня есть ещё бонусное задание — придумать, как обернуть похожим образом нестатические методы классов this класса (указатель на нативное представление JS-объекта) можно вытянуть из const FunctionCallbackInfo<Value>& info, которая передаётся в переходник. Есть какие-нить мысли на этот счёт?

Практически так же. Гуглите C++ method pointers. Там используется какой-то зубодробительный синтаксис, но сути — это тот же indirect call, только сбоку: добавляем шаблонный аргумент для типа класса и передаем в объект 2 вещи: указатель на this и указатель на метод.

K>P.S. Здесь правда есть ещё один очень тонкий момент, связанный с особенностью поведения this в JavaScript (он в принципе может указывать на что угодно) — но с этим в принципе несложно справиться, проверив имя класса объекта, который к нам попал.

тут можно воспользоваться RTTI, думаю.
Re[9]: упс...
От: PM  
Дата: 10.04.15 07:40
Оценка: 6 (1)
Здравствуйте, koandrew, Вы писали:

Эх, не выдержу, влезу, поделюсь списком граблей по которым прошел

B>>Глюк, судя по-всему, здесь:

K>Вы правы — поправил у себя. Теперь компилятор молчит, как партизанен

VariantConverter должен уметь сообщать из/в какой тип он конвертирует, чтобы знать какой тип возвращать из GetArg(). См v8pp/convert.hpp

B>>Меня самого любопытство одолело и я решил распотрошить std::bind()

B>>P.S. При желании этот код можно упростить, выкинув:
B>>1. Копирование в промежуточный vector
B>>2. Создание Invokable-объекта. Вместо этого методы можно сделать static и передавать туда явно указатель на вызываемую функцию, которую shim вытащит из info.Data().
K>Это уже на завтра — мне ещё надо будет завтра как следует разобраться к текущем коде — а то в 10:30 вечера голова что-то туго соображает %)

Вероятно придешь к нескольким перегрузкам DoInvoke(..), т.к. Invokable-объект с одним аргументом FunctionCallbackInfo будет полезен, если на JS стороне требуется вызывать функцию с разным кол-вом или типами аргументов. Или иметь в С++ функции v8::Isolate* (который есть в FunctionCallbackInfo). Для этого нужно выбрать на этапе компиляции какую из перегруженных DoInvoke() вызывать. См. v8pp/call_from_v8.hpp

K>У меня есть ещё бонусное задание — придумать, как обернуть похожим образом нестатические методы классов this класса (указатель на нативное представление JS-объекта) можно вытянуть из const FunctionCallbackInfo<Value>& info, которая передаётся в переходник. Есть какие-нить мысли на этот счёт?


Главное помнить, что указатель на class member function может быть не указателем, а смещением в VMT и его размер в военное время достигает до 4* sizeof(void*) в зависимости от компилятора и виртуальности базовых классов.

Есть v8::Object::SetAlignedPointerInInternalField(), с его помощью в JS объекте хранят указатель на С++ объект. При создании только функции-конструкора класса нужно указать кол-во внутренних полей для этого типа объектов используя SetInternalFieldCount()

См. wrap_external_object() unwrap_object().

Если хочешь использовать свои классы как аргументы/результат оборачиваемых функций, не забудь добавить поддержу для них в VariantConverter. Чтобы работал VariantConverter::ToVariant() я использую unordered_map&lt;object*, v8::Handle&gt;

Еще более тонкий момент — использование полиморфных классов, если в JS объекте хранить укзатель на С++ объект. Для этого я ничего лучше н епридумал, как сделать небольшой аналог RTTI чтобы знать про типы базовых и производных классов и уметь преобразовывать указатель на объект между этими типами для корректного вызова функции-члена, см. v8pp/class.hpp
Re[10]: упс...
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 10.04.15 15:06
Оценка:
Здравствуйте, bazis1, Вы писали:

B>Практически так же. Гуглите C++ method pointers. Там используется какой-то зубодробительный синтаксис, но сути — это тот же indirect call, только сбоку: добавляем шаблонный аргумент для типа класса и передаем в объект 2 вещи: указатель на this и указатель на метод.

Вообще в С/С++ синтаксис указателей на функцию явно придумывали под тяжёлыми наркотиками — сколько я не работаю с языком, так до сих пор не могу запомнить, как писать сигнатуру, каждый раз ищу какой-нить пример, дабы напомнить себе, как это надо правильно писать

B>тут можно воспользоваться RTTI, думаю.

Возможно.
[КУ] оккупировала армия.
Re: Интегрируем v8
От: IROV..  
Дата: 17.04.15 07:44
Оценка:
Здравствуйте, koandrew, Вы писали:

https://sourceforge.net/projects/pybind/
там можно посмотреть как решается данная проблема
если что спрашуй
я не волшебник, я только учусь!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.