首页 > 解决方案 > 避免警告:对“计数”的操作可能未定义 [-Wsequence-point]

问题描述

我正在使用 Node.js N-API,并且正在制作一个小包装器,这将使导出 C++ 函数变得容易。

template<class T, class... Targs> napi_value Api::create(const char* name, T (* const cb)(Targs...))
{
    // creates JavaScript function that will call cbProxy<> when called
    return create(name, cbProxy<T, Targs...>, cb);
}

template<class T, class... Targs> napi_value Api::cbProxy(const napi_env env, const napi_callback_info info)
{
    // number of arguments
    size_t count = sizeof...(Targs);
    ApiValue args[count];
    T (* cb)(Targs...);
    // retrieve arguments and callback
    if (!Api::getParams(env, info, args, count, &cb))
        return nullptr;

    T ret = cb(Api::getValue<Targs>(&args[--count])...);    
    return Api(env).create(ret);
}

template<> bool Api::getValue(ApiValue* value)
{
    return value->toBool();
}

template<> double Api::getValue(ApiValue* value)
{
    return value->toDouble();
}

template<> int32_t Api::getValue(ApiValue* value)
{
    return value->toInt32();
}

这个想法是调用api.create("TestFunction", testFn);which 将返回一个 JS 函数。
当它被调用时,它将调用Api::cbProxy<>将 JS 参数转换为等效的 C++ 类型,Api::getValue<T>()并调用testFn(cb)。

假设testFn具有以下签名:int testFn(bool bVal, double dVal)
该行将T ret = cb(Api::getValue<Targs>(&args[--count])...);扩展到

int ret = cb(Api::getValue<bool>(&args[--count]), 
             Api::getValue<double>(&args[--count]));

可以 100% 正常工作,但会触发编译器警告。我对 C++ 有点陌生,所以我正在寻找一种更好的方法来编写这个。

基本上我想一次遍历数组args和类型参数列表Targs

只是为了澄清:--count扩展函数调用中的 触发以下警告:
warning: operation on ‘count’ may be undefined [-Wsequence-point]

标签: c++c++11templatesvariadic-templates

解决方案


我想问题在于

int ret = cb(Api::getValue<bool>(&args[--count]), 
             Api::getValue<double>(&args[--count]));

参数参数的评估顺序是依赖于实现的未定义行为(MM 校正),因此,count从 开始2,可以是

int ret = cb(Api::getValue<bool>(&args[1]), 
             Api::getValue<double>(&args[0]));

或者

int ret = cb(Api::getValue<bool>(&args[0]), 
             Api::getValue<double>(&args[1]));

为了确保 (the one) 的第一个索引argsbool并且1第二个 (the doubleone) is 0,一种可能的方法是使用可变参数索引。

如果可以使用C++14,使用cbProxyHelper()方法,可以尝试如下(注意:代码未测试)

template <typename T, typename ... Targs, std::size_t ... Is>
napi_value Api::cbProxyHelper (const napi_env env,
                               const napi_callback_info info,
                               std::index_sequence<Is...> const &)
 {
   // number of arguments
   constexpr std::size_t count = sizeof...(Targs);
   ApiValue args[count];
   T (* cb)(Targs...);
   // retrieve arguments and callback
   if (!Api::getParams(env, info, args, count, &cb))
      return nullptr;

   T ret = cb(Api::getValue<Targs>(&args[count-1U-Is])...);    
   return Api(env).create(ret);
 }

template <typename T, typename ... Targs>
napi_value Api::cbProxy (const napi_env env, const napi_callback_info info)
 { return cbProxyHelper(env, info, std::index_sequence_for<Targs...>{}); }

如果您使用的是 C++11,请进行模拟std::index_sequence并且std::make_index_sequence并不难。


推荐阅读