当前位置: 首页 > 工具软件 > Duktape > 使用案例 >

用C++对Duktape JavaScript引擎的简单封装。

许毅
2023-12-01

Duktape是款比较小巧的JavaScript引擎,适用于嵌入式系统,不过API使用不太方便,研究了两天决定放弃。下面把测试代码上来,做个备忘吧。

duktape_helper.hpp文件

#ifndef DUKTAPE_HELPER_HPP
#define DUKTAPE_HELPER_HPP

#include <memory>
#include <string>
#include <type_traits>
#include "duktape.h"

namespace duktape {

    class StackAssert {
    private:
        duk_context *m_context;
        unsigned m_expected;
        unsigned m_begin;
    public:
        /**
         * Create the stack checker.
         *
         * No-op if NDEBUG is set.
         *
         * \param ctx the context
         * \param expected the size expected relative to the already existing values
         */
        inline StackAssert(duk_context *ctx, unsigned expected = 0) noexcept
            : m_context(ctx)
            , m_expected(expected)
            , m_begin(static_cast<unsigned>(duk_get_top(ctx)))
        {

        }

        /**
         * Verify the expected size.
         *
         * No-op if NDEBUG is set.
         */
        inline ~StackAssert() noexcept
        {
            if (static_cast<unsigned>(duk_get_top(m_context)) - m_begin != m_expected) {
                std::fprintf(stderr, "Corrupt stack detection in StackAssert:\n");
                std::fprintf(stderr, "  Size at start:            %u\n", m_begin);
                std::fprintf(stderr, "  Size at end:              %d\n", duk_get_top(m_context));
                std::fprintf(stderr, "  Expected (user):          %u\n", m_expected);
                std::fprintf(stderr, "  Expected (adjusted):      %u\n", m_expected + m_begin);
                std::fprintf(stderr, "  Number of stale values:   %u\n", duk_get_top(m_context) - m_begin - m_expected);
                std::abort();
            }
        }
    };

    template< typename T >
    struct duk_result
    {
        static T get(duk_context *ctx, duk_idx_t idx)
        {
            return duk_to_null(ctx,idx);
        }
    };
    template<>
    struct duk_result<int>
    {
        static int get(duk_context *ctx, duk_idx_t idx)
        {
            return duk_to_int(ctx,idx);
        }
    };

    template<>
    struct duk_result<double>
    {
        static double get(duk_context *ctx, duk_idx_t idx)
        {
            return duk_to_number(ctx,idx);
        }
    };

    template<>
    struct duk_result<const char*>
    {
        static const char* get(duk_context *ctx, duk_idx_t idx)
        {
            return duk_to_string(ctx,idx);
        }
    };

    class SharedPtr
    {
        using Deleter = void (*)(duk_context *);
        std::unique_ptr<duk_context,Deleter> ptr_;
        SharedPtr( const SharedPtr &) = delete;
        SharedPtr& operator = ( const SharedPtr&) = delete;
    public:
        SharedPtr(duk_context *p ):ptr_(p,duk_destroy_heap)
        {}
        inline operator duk_context *() noexcept
        {
            return ptr_.get();
        }
        inline operator duk_context *() const noexcept
        {
            return ptr_.get();
        }
    };

    using deleter = void(*)(duk_context*);
    typedef std::unique_ptr<duk_context,deleter> stack_pop;

    class script
    {

    public:
        script():m_ctx(duk_create_heap_default())
        {

        }

        duk_context * context() const
        {
            return m_ctx;
        }
        inline operator duk_context *() noexcept
        {
            return m_ctx;
        }
        inline operator duk_context *() const noexcept
        {
            return m_ctx;
        }

        void evaluate( const std::string & script )
        {
            StackAssert sa(m_ctx);
            stack_pop pop(m_ctx,duk_pop);
            duk_eval_string(m_ctx,script.c_str());
        }

        template<typename R , typename ...Args>
        R evaluate(const char* functionName ,Args ...args )
        {
            StackAssert sa(m_ctx);
            stack_pop pop(m_ctx,duk_pop);
            duk_get_global_string(m_ctx,functionName);
            duk_push_args(m_ctx,args...);
            duk_call(m_ctx,sizeof...(args));

           return duk_result<R>::get(m_ctx,-1);
        }

        void defineFunctions(duk_function_list_entry * fs )
        {
            for( ; fs->key ; fs++ ){
                duk_push_c_function(m_ctx, fs->value, fs->nargs);
                duk_put_global_string(m_ctx, fs->key);
            }
        }
        void defineFunction(const char* key , duk_c_function func, duk_idx_t nargs)
        {
            duk_push_c_function(m_ctx, func, nargs);
            duk_put_global_string(m_ctx, key);
        }

    private:
        SharedPtr m_ctx;
        script( const script &) = delete;
        script& operator = ( const script&) = delete;

    };

    //help template function for duk_push_
    template<typename T> void duk_push_arg(duk_context *ctx,T t) {}
    template<> void duk_push_arg(duk_context *ctx,int t)
    {
        duk_push_int(ctx,t);
    }
    template<> void duk_push_arg(duk_context *ctx,double t)
    {
        duk_push_number(ctx,t);
    }
    template<> void duk_push_arg(duk_context *ctx,const char* t)
    {
        duk_push_string(ctx,t);
    }
    template<> void duk_push_arg(duk_context *ctx,bool t)
    {
        duk_push_boolean(ctx,t);
    }
    template<> void duk_push_arg(duk_context *ctx,void* t)
    {
        duk_push_pointer(ctx,t);
    }
    template <typename ...Args>
    void duk_push_args(duk_context *ctx,Args... args)
    {
        std::initializer_list<int> lst{(duk_push_arg(ctx,std::forward< Args>(args)),0)...};
    }

}//namespace Duktape

#endif // DUKTAPE_HELPER_HPP

main.cpp文件 

#include <iostream>
#include <memory>

#include "duktape_helper.hpp"

static duk_ret_t native_print(duk_context *ctx) {
    duk_push_string(ctx, " ");
    duk_insert(ctx, 0);
    duk_join(ctx, duk_get_top(ctx) - 1);
    std::cout<<duk_safe_to_string(ctx, -1)<<std::endl;
    return 0;
}

static duk_ret_t native_adder(duk_context *ctx) {
    int i;
    int n = duk_get_top(ctx);  /* #args */
    double res = 0.0;

    for (i = 0; i < n; i++) {
        res += duk_to_number(ctx, i);
    }
    duk_push_number(ctx, res);
    return 1;  /* one return value */
}


int main()
{
    std::cout << "Run duktape JavaScript !" << std::endl;

    std::shared_ptr<duktape::script> duk = std::make_shared<duktape::script>();

    duk_function_list_entry functions[] = {
        {"print",native_print,DUK_VARARGS},
        {"adder",native_adder,DUK_VARARGS},
        {0}
    };

    duk->defineFunctions(functions);

    duk->evaluate("var a = 'Hello world'; print(a + ' ' + Duktape.version);");
    duk->evaluate("print('2.2+3.3=' + adder(2.2, 3.3));");
    duk->evaluate("function sum(a,b){ return a+b; }");
    duk->evaluate("var printVersion = function (msg,a,b){"
                  "var disp = msg+(a+b) ; "
                  "print ('Duktape Version: '+ Duktape.version); "
                  "return disp;}");

    const char* result;
    result = duk->evaluate<const char*>("printVersion","call javascript function ",56,44);
    std::cout<<result<<std::endl;
    double ret;
    ret = duk->evaluate<double>("sum",56,44);
    std::cout<<ret<<std::endl;

    return 0;
}

CMakeLists.txt文件 

cmake_minimum_required(VERSION 2.8)

set(CMAKE_VERBOSE_MAKEFILE ON)

project(duk_jse)

set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_C_FLAGS -std=c99)
set(CMAKE_CXX_FLAGS "-std=c++11")

set(LIST_SRC main.cpp
    duktape.c)

add_executable(${PROJECT_NAME} ${LIST_SRC})

代码挺简单,就不注释了。还测试过v7,sipdermonkey1.8,如果是做嵌入式系统v7就够用了,sipdermonkey的api和文档都很好,就是大了些,不过1.8版本编译后也可以。

代码下载

 类似资料: