简体   繁体   English

如何将指向对象的指针传递给 Squeak/Cuis 中的 FFI 调用?

[英]How do I pass a pointer to an Object to an FFI call in Squeak/Cuis?

I need to pass an array of Strings to an FFI call, I'd like to just do it as:我需要将一个字符串数组传递给 FFI 调用,我想这样做:

library passArray: {'hola' 'manola'} size: 2.

where passArray:size: is something like:其中passArray:size:类似于:

passArray: anArray size: anInteger
   <cdecl: void 'someFunction' (void* size_t)>
   ^ self externalCallFailed

But it fails with "Could not coerce arguments", no matter what I try.但无论我尝试什么,它都以“无法强制论证”而失败。

Any ideas?有任何想法吗? (Yes, I could "externalize" all strings, and then build also an array of the pointers, but I don't think I need it. (是的,我可以“外部化”所有字符串,然后还构建一个指针数组,但我认为我不需要它。

I prefer using a shared memory approach where data is to be shared between Smalltalk and C. The nice thing about shared memory is that you do not have to worry about moving the data between Smalltalk and C because the data is accessible from C and Smalltalk at the same time.我更喜欢使用共享内存方法,其中数据在 Smalltalk 和 C 之间共享。共享内存的好处是您不必担心在 Smalltalk 和 C 之间移动数据,因为数据可以从 C 和 Smalltalk 访问同时。 Also because shared memory operates outside the VM and GC boundaries you do not have to worry about your data being garbage collected and ending up with memory leaks.此外,由于共享内存在 VM 和 GC 边界之外运行,因此您不必担心数据被垃圾收集并最终导致内存泄漏。

I do not know how to do this on Squeak because I am a Pharo user but must be something similar.我不知道如何在 Squeak 上执行此操作,因为我是 Pharo 用户,但必须是类似的。

On C side在 C 侧

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <iostream>
#include <string>

#define FILEPATH "mmapped.bin"
#define NUMINTS  (1000)
#define FILESIZE (NUMINTS * sizeof(int))

int main(int argc, char *argv[])
{
    int i;
    int fd;
    std::string* map;
    std::string map_contents;

    fd = open(FILEPATH, O_RDONLY);
    if (fd == -1) {
    perror("Error opening file for reading");
    exit(EXIT_FAILURE);
    }
    map = (std::string*)mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED) {
    close(fd);
    perror("Error mmapping the file");
    exit(EXIT_FAILURE);
    }

    /* Read the file int-by-int from the mmap
     */
    map_contents = std::string(*map);
    std::cout<<"type of map is : "<< typeid(map).name()<<"\n";
    std::cout<<"I am reading from mmap : "<< map_contents <<" \n";

    if (munmap(map, FILESIZE) == -1) {
    perror("Error un-mmapping the file");
    }
    close(fd);
    return 0;
}

On Pharo side在法罗一侧

examples
retrieveSharedValueStep1
<example>
"This method is an example that retrieves a struct from a shared memory section, in order for this example to work you need you first copy paste the contents of the Example Souce Code of the C++ file in the comment section  (you can also find the cpp file in the same directory where the git repo has been downloaded) of this class to a C++ source code file and compile it a run then replace the path of in this code of CPPBridge openFile: with the correct path of the bin that the C++ files has created , in order for this to work also you need to execute the C++ example first so it creates and file and share the memory.
After executing this method you can execute retrieveSharedValueStep2 to unmap and close the memory mapped file (keeps sharing the memory it just does not store it to the file)"

|instance fdNumber lseek mmapPointer data struct|

"Let's create an instance just an an example but we wont use it because we can use either class method or intance methods. You would want to use instance method if you want to open multiple memory mapped files meaning multiple areas of shared memory. Class methods for using just one"

instance := CPPBridge new.

"Warning !!! You must change the path to the file that is located in your hard drive. The file should be at the same location you built atlas-server.cpp which is responsible for creating the file. The number returned is a number that OS uses to identify the image , flag O_RDWR is just a number that states that we want to write and read the file"

fdNumber := CPPBridge openFile: '/Users/kilon/git/Pharo/CPPBridge/mmapped.bin' flags: (O_RDWR) . 

"lseek is used to stretch the file to a new size"
lseek := CPPBridge lSeek_fd: fdNumber range:3999  value:0.

"this is the most importan method, this method maps the file to memmory , which means it loads its contents into memory and associates the memory with the file. PROT_READ means we want to write the memory , PROT_WRITE to write the memory and MAP_SHARED is the most importan because it defines the memory area as shared so we can access it from other application"

mmapPointer := CPPBridge  mmap_adress: 0 fileSize:4000  flag1: (PROT_READ | PROT_WRITE )flag2: MAP_SHARED  fd: fdNumber  offset: 0  .

"This assigns the pointer to our Pharo structure so we can use it to get the contents of the C structure located in the shared memory"
struct := CPPStruct pointTo: (mmapPointer getHandle ).

"data here serves as a convenience array its not necessary we use it just to collect information about the instance, the fd number of the file, the streched size of the file, the adress (point) where the file is mapped to in memory and struct that contains the values of the C struct that we received"
data :={ instance.  fdNumber . lseek. mmapPointer  .  struct}.
data inspect.

"Store data to the class so we can use it in the second method"
ExampleDATA := data.
^data 

"
Its also possible to write to the shared memory , in this case we use once again the C struct which has the following members (variables) : 
1) data = char[3000]  this is where we store the string
2) count = int this is where we store the size of the string
struct := {(mmapPointer getHandle  copyFrom: 1 to:3000 )asString . (mmapPointer getHandle integerAt: 3001 size:4 signed: false)}.
mmapPointer is the pointer that points to the first byte of the shared memory.
getHandle gives us the memory adress that the pointer points to
copyFrom:1 to:3000 copies byte from byte 0 (remember C counts from 0 , Pharo counts from 1) to byte 3000 because the string we store is stored as a char array of 3000 elements, each element is a char, each char is 1 byte in leght and represents a single character of the string. This gets the value of the first struct member.
on the other hand integerAt: 3001 size: 4 signed: false returns us the value count memeber of the C struct . its an integer in position 3001 because our string is a char[3000] and the size is 4 bytes because its an C int, signed false because we use no negative values because it does not make sense for a string to have negative length. This gets the value of the second struct member"

You can find more info by visiting my github repo because I have packaged all this into a library I call CPP (main intention was to use C++ but it works with C as well)您可以通过访问我的 github repo 找到更多信息,因为我已将所有这些打包到一个我称为 CPP 的库中(主要目的是使用 C++,但它也适用于 C)

https://github.com/kilon/CPP https://github.com/kilon/CPP

The advantages of my approach are:我的方法的优点是:

  1. you do not have to worry about GC你不必担心GC

  2. you do not need to copy data around你不需要复制数据

  3. because shared memory using the memory mapped file system of the OS kernel you get a ton of speed plus your shared memory is always stored to a file automagically so you do not need to worry about losing your data in case of crash因为共享内存使用操作系统内核的内存映射文件系统,您可以获得大量速度,而且您的共享内存始终自动存储到文件中,因此您无需担心在崩溃时丢失数据

  4. the mmap file works in similar way to squeak image, storing live state mmap 文件的工作方式类似于吱吱声图像,存储实时状态

  5. mmap because is an OS kernel function its supported in all OSes but also most programming languages , that means you can use this with any programming language you want mmap 因为它是一个操作系统内核函数,它支持所有操作系统以及大多数编程语言,这意味着您可以将它与您想要的任何编程语言一起使用。

Disadvantages缺点

  1. Because this works inside a manual memory management region you lose the advantages of GC so you need to handle that memory yourself manually因为这在手动内存管理区域内工作,所以您失去了 GC 的优势,因此您需要自己手动处理该内存
  2. Because its outside GC you also lose many of the dynamic capabilites of Smalltalk objects and thus you have to abide by C rules.因为它是外部 GC,你也失去了 Smalltalk 对象的许多动态能力,因此你必须遵守 C 规则。 Of course none stopping you from making a copy of the data as Smalltalk objects if you so wish or passing the data to existing Smalltalk objects当然,如果您愿意,也不会阻止您将数据复制为 Smalltalk 对象或将数据传递给现有的 Smalltalk 对象
  3. If you mess up you will crash Squeak VM easily as with any usual memory leak如果你搞砸了,你会像任何通常的内存泄漏一样轻松地使 Squeak VM 崩溃

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM