通过 Emscripten 在 Javascript 中进行结构操作
Struct operations in Javascript through Emscripten
我在 C 和 Javascript 之间的 emscripten 互操作方面遇到了很多问题。
更具体地说,我无法访问在 javascript 中用 C 创建的结构,因为指向该结构的指针作为 external library 传递到 javascript。
看看下面的代码:
C:
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
struct test_st;
extern void read_struct(struct test_st *mys, int siz);
struct test_st{
uint32_t my_number;
uint8_t my_char_array[32];
};
int main(){
struct test_st *teststr = malloc(sizeof(struct test_st));
teststr->my_number = 500;
for(int i = 0; i < 32; i++){
teststr->my_char_array[i] = 120 + i;
}
for(int i = 0; i < 32; i++){
printf("%d\n",teststr->my_char_array[i]);
}
read_struct(teststr,sizeof(teststr));
return 0;
}
Javascript:
mergeInto(LibraryManager.library,
{
read_struct: function(mys,siz){
var read_ptr = 0;
console.log("my_number: " + getValue(mys + read_ptr, 'i32'));
read_ptr += 4;
for(var i = 0; i < 32; i++){
console.log("my_char[" + i + "]: " + getValue(mys + read_ptr, 'i8'));
read_ptr += 1;
};
},
});
然后使用emcc cfile.c --js-library jsfile.js
编译。
这里的问题是你不能真正读取javascript中的struct,你必须根据struct字段的大小从各自的地址中获取内存(所以从[=中读取4个字节32=] 和 uint8_t 中的 1 个字节)。好的,这不是问题,除了你还必须声明 getValue
的 LLVM IR 类型才能工作,并且它不包括无符号类型,所以在数组的情况下,它将到达127 并溢出到 -128,当预期行为是继续上升时,因为变量是无符号的。
我到处寻找答案,但显然这种特定的预期行为并不常见。在我应用它的程序中更改结构是不可能的(不是上面的示例)。
一种方法是使用 Emscripten 公开的 HEAP*
类型数组,它确实有未签名的视图:
mergeInto(LibraryManager.library, {
read_struct: function(myStructPointer, size) {
// Assumes the struct starts on a 4-byte boundary
var myNumber = HEAPU32[myStructPointer/4];
console.log(myNumber);
// Assumes my_char_array is immediately after my_number with no padding
var myCharArray = HEAPU8.subarray(myStructPointer+4, myStructPointer+4+32);
console.log(myCharArray);
}
});
这在我的测试中有效,运行 Emscripten 1.29.0-64 位,但如前所述,它对 alignment/padding 做了假设。我测试的案例似乎表明结构似乎总是从 4 字节边界开始,并且结构内的 32 位无符号整数也总是在 4 字节边界上对齐,因此可以通过 HEAPU32 访问。
但是,我不知道您是否可以依赖 Emscripten 中的这种行为。据我了解,您不能在通常的 C/C++ 世界中。
我在 C 和 Javascript 之间的 emscripten 互操作方面遇到了很多问题。
更具体地说,我无法访问在 javascript 中用 C 创建的结构,因为指向该结构的指针作为 external library 传递到 javascript。
看看下面的代码:
C:
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
struct test_st;
extern void read_struct(struct test_st *mys, int siz);
struct test_st{
uint32_t my_number;
uint8_t my_char_array[32];
};
int main(){
struct test_st *teststr = malloc(sizeof(struct test_st));
teststr->my_number = 500;
for(int i = 0; i < 32; i++){
teststr->my_char_array[i] = 120 + i;
}
for(int i = 0; i < 32; i++){
printf("%d\n",teststr->my_char_array[i]);
}
read_struct(teststr,sizeof(teststr));
return 0;
}
Javascript:
mergeInto(LibraryManager.library,
{
read_struct: function(mys,siz){
var read_ptr = 0;
console.log("my_number: " + getValue(mys + read_ptr, 'i32'));
read_ptr += 4;
for(var i = 0; i < 32; i++){
console.log("my_char[" + i + "]: " + getValue(mys + read_ptr, 'i8'));
read_ptr += 1;
};
},
});
然后使用emcc cfile.c --js-library jsfile.js
编译。
这里的问题是你不能真正读取javascript中的struct,你必须根据struct字段的大小从各自的地址中获取内存(所以从[=中读取4个字节32=] 和 uint8_t 中的 1 个字节)。好的,这不是问题,除了你还必须声明 getValue
的 LLVM IR 类型才能工作,并且它不包括无符号类型,所以在数组的情况下,它将到达127 并溢出到 -128,当预期行为是继续上升时,因为变量是无符号的。
我到处寻找答案,但显然这种特定的预期行为并不常见。在我应用它的程序中更改结构是不可能的(不是上面的示例)。
一种方法是使用 Emscripten 公开的 HEAP*
类型数组,它确实有未签名的视图:
mergeInto(LibraryManager.library, {
read_struct: function(myStructPointer, size) {
// Assumes the struct starts on a 4-byte boundary
var myNumber = HEAPU32[myStructPointer/4];
console.log(myNumber);
// Assumes my_char_array is immediately after my_number with no padding
var myCharArray = HEAPU8.subarray(myStructPointer+4, myStructPointer+4+32);
console.log(myCharArray);
}
});
这在我的测试中有效,运行 Emscripten 1.29.0-64 位,但如前所述,它对 alignment/padding 做了假设。我测试的案例似乎表明结构似乎总是从 4 字节边界开始,并且结构内的 32 位无符号整数也总是在 4 字节边界上对齐,因此可以通过 HEAPU32 访问。
但是,我不知道您是否可以依赖 Emscripten 中的这种行为。据我了解,您不能在通常的 C/C++ 世界中。