SoFunction
Updated on 2025-03-01

How to call functions in C++ modules in JavaScript

Scenario 1: JS side calls C++ side functions and passes parameters

JS side call C++ side function

import testNapi from ''

const TAG = "beixiang";

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        Text('Call')
          .fontSize(50)
          .fontWeight()
          .onClick(() => {
            const result = (3,4);
            (`${TAG} The result of the call is${result}`);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

C++ side method implementation

static napi_value Add(napi_env env, napi_callback_info info)
{
    // Get 2 parameters, napi_value is an encapsulation of JS type    size_t argc = 2;
    napi_value argv[2] = {nullptr};
    
    // Call the napi_get_cb_info method, read the passed parameters from info and put them into argv    napi_get_cb_info(env, info, &argc, argv , nullptr, nullptr);

    // Get parameters and verify the type    napi_valuetype valuetype0;
    napi_typeof(env, argv[0], &valuetype0);
    napi_valuetype valuetype1;
    napi_typeof(env, argv[1], &valuetype1);
    
    // Call napi_get_value_double to convert the napi_value type into the double type of C++    double value0;
    napi_get_value_double(env, argv[0], &value0);
    double value1;
    napi_get_value_double(env, argv[1], &value1);
    
    // Call the napi_create_double method to convert the C++ type to the napi_value type    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);
    
    // Return the napi_value type    return sum;
}
  • napi_get_cb_info (napi_env env, napi_callback_info cbinfo, size_t *argc, napi_value *argv, napi_value *this_arg, void **data)

    • env: The environment that calls the API
    • cbinfo: callback information passed to callback function
    • argc: Specifies the size of the provided argv array and receives the actual count of the parameters
    • argv: The buffer to which the napi_value representing the parameter is copied
    • this_arg: Receive the call to this parameter
    • data: receive the data pointer of the callback

    For example, the Add method code,napi_get_cb_info(env, info, &argc, argv , nullptr, nullptr);, read the passed in &argc parameters from info and put them into argv.

  • napi_get_value_double(napi_env env, napi_value value, double *result)

    Convert the napi_value type to the double type of C++ for use on the C++ side

  • napi_create_double(napi_env env, double value, napi_value *result)

    Convert C++ double type to napi_value type for use on the JS side

  • napi_create_function(napi_env env, const char *utf8name, size_t length, napi_callback cb, void *data, napi_value *result)

Different from the above defined function, andInit()Method declarationnapi_property_descriptorThe way to export functions in structures is to use napi_create_function to allow the C++ side function to be created as a function object for call on the JS side, and then use napi_set_named_property to export the created function object so that the function can be accessed from the JS side, as follows:

//     
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_value fn;
    // Create function fn according to C++ side function Add    napi_create_function(env, nullptr, 0, Add, nullptr, &fn);
    // Export the created function fn, the function name is newAdd    napi_set_named_property(env, exports, "newAdd", fn);
    
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END
// 
export const newAdd: (a: number, b: number) => number;

Scenario 2: The JS side does not pass parameters to the callback function, the C++ side receives the JS side callback function and executes it

JS side call C++ side function

import testNapi from ''
const TAG = "beixiang";

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        Text('Call the callback function without arguments')
          .fontSize(50)
          .fontWeight()
          .onClick(() => {
            // Register the callback function without arguments first            (() => {
              const a = 2;
              const b = 3;
              return a + b;
            })
            // Call the callback function without arguments            const result = ();
            (`${TAG} The result of calling the callback function without arguments is${result}`);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

C++ side registration callback function

// js function callbackstatic napi_ref callback = nullptr;
/**
  * Register the callback function
  * @param env
  * @param info
  * @return
  */
static napi_value RegisterCallback(napi_env env, napi_callback_info info) 
{
    size_t argc = 1;
    napi_value argv[1] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    
    napi_create_reference(env, argv[0], 1, &callback);
    
    return nullptr;
}

C++ side executes the registered callback function

/**
  * Execute the callback function without parameters
  * @param env
  * @param info
  * @return
  */
static napi_value HandleCallbackWithoutParams(napi_env env, napi_callback_info info)
{
    
    napi_value global;
    napi_get_global(env, &global);
    
    napi_value cb = nullptr;
    napi_get_reference_value(env, callback, &cb);

    napi_value result;
    napi_status status = napi_call_function(env, global, cb, 0 , nullptr, &result);

    if (status != napi_ok) return nullptr;
    
    return result;
}
  • napi_create_reference(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref *result)

    This API creates a new reference for the passed object with a specified reference count, such as registering the napi_create_reference(env, argv[0], 1, &callback) in the callback function RegisterCallback, and save the object argv[0] passed on the JS side (for JS, the function is also an object) in the callback for C++ side method calls;

  • napi_get_reference_value(napi_env env, napi_ref ref, napi_value *result)

    Save the created reference ref to result

  • napi_call_function(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv, napi_value *result)

    • env: The environment that calls the API
    • recv: this object is passed to the called function, which is generally the global object of the current environment, and is obtained through napi_get_global.
    • func: represents the JavaScript function to be called
    • argc: count of elements in argv array.
    • argv: an array of napi_values ​​that represents the JavaScript value passed to a function as an argument
    • result: napi_value represents the returned JavaScript object

    In the env environment, the function func is called in the global object. The array of the function parameter is argv, and there are argc parameters. The function execution result is saved in result. This API allows calling JavaScript function objects from the C++ side, such as napi_call_function(env, global, cb, 0, nullptr, &result);

Scenario 3: Pass the parameter to the callback function on the JS side, and receive the JS side callback function on the C++ side and execute it

JS side call C++ side function

import testNapi from ''
const TAG = "beixiang";

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        Text('Call the callback function with argument')
          .fontSize(50)
          .fontWeight()
          .onClick(() => {
            // Register the callback function without arguments first            ((a: number, b: number) => {
              return a + b;
            })
            // Call the callback function without arguments            const result = ();
            (`${TAG} The result of calling the callback function with arguments is${result}`);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

C++ side registration callback function

/**
  * Register the callback function
  * @param env
  * @param info
  * @return
  */
static napi_value RegisterCallback(napi_env env, napi_callback_info info) 
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    napi_create_reference(env, args[0], 1, &callback);
    
    return nullptr;
}

C++ side executes the registered callback function

/**
  * Execute callback function with parameters
  * @param env
  * @param info
  * @return
  */
static napi_value HandleCallbackWithParams(napi_env env, napi_callback_info info)
{
    napi_value argv[2] = {nullptr};
    
    napi_valuetype valuetype0;
    napi_typeof(env, argv[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, argv[1], &valuetype1);
    
    double value1 = 2;
    double value2 = 3;

    // Create two doubles and call callback    napi_create_double(env, value1, &argv[0]);
    napi_create_double(env, value2, &argv[1]);
    
    
    napi_value global;

    napi_get_global(env, &global);
    
    napi_value cb = nullptr;
    napi_get_reference_value(env, callback, &cb);
    
    napi_valuetype type;
    napi_typeof(env, cb, &type);

    napi_value result;
    // Call callback function    napi_status status = napi_call_function(env, global, cb, 2, argv, &result);
    
    if (status != napi_ok) return nullptr;
    
    return result;
}

It is worth noting that the callback function passed to C++ on the JS side in this article is an anonymous function. The C++ side first registers the JS callback function on the C++ side, that is, use napi_create_reference to create the JS function as a ref. Ref will eventually be used as the third parameter of napi_call_function. You can rest assured that you did not directly retrieve the function reference in the global object.

Another way to implement it is that the callback function passed to C++ on the JS side is a non-anonymous function. Use napi_get_named_property to directly obtain the function reference in the global object:

The JS side defines a non-anonymous callback function:

function add(a: number, b:number) {
  return a + b;
}

The C++ side takes the add function from the global object and introduces it in napi_call_function:

napi_value global, add, arg;
napi_get_global(env, &global);
// Take the object/function name named "add" in the global object and save it in add.napi_get_named_property(env, global, "add", &add);
...
// Call the add functionnapi_call_function(env, global, add, 2 , argv, &result);

Summarize

This is the article about how JavaScript calls functions in C++ modules. For more related functions of JS calling C++ modules, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!