从本机库读取流到C# 我有以下的本机C++函数: // Decode binary format from file 'filename' into stream 'output' bool read_private_format(const char * filename, std::ostringstream & output);

从本机库读取流到C# 我有以下的本机C++函数: // Decode binary format from file 'filename' into stream 'output' bool read_private_format(const char * filename, std::ostringstream & output);,c#,linux,.net-core,stream,pinvoke,C#,Linux,.net Core,Stream,Pinvoke,在阅读上一篇文章之后,我创建了一个中间C函数,将其公开给C层,如下所示: extern "C" { typedef char *(*StringBuilderCallback)(int len); __attribute__ ((visibility ("default"))) bool c_read_private_format(const char * filename, StringBuilderCallback ensureCapaci

在阅读上一篇文章之后,我创建了一个中间C函数,将其公开给C层,如下所示:

extern "C" {
  typedef char *(*StringBuilderCallback)(int len);
  __attribute__ ((visibility ("default")))
  bool c_read_private_format(const char * filename, StringBuilderCallback ensureCapacity, char *out, int len) {
    std::ostringstream oss;
    if( read_private_format(filename, oss) ) {
      const std::string str = oss.str();
      if( str.size() > len )
        out = ensureCapacity(str.size());
      strcpy(out, str.c_str());
      return true;
    }
    return false;
  }
}
而在C侧:

由于某些原因,这无法按预期工作,当打印
回调返回的
char*
的地址时,它就像返回的指针是调用
EnsureCapacity
之前的指针一样(我可以通过执行第二次调用进行验证,在这种情况下,C层中的
char*
是不同的)

我的问题是:

  • 如何在.NETSDK(5.0.202)中有效地从C检索UTF-8字符串

我事先不知道绳子会有多长。从技术上讲,我可以高估StringBuilder的容量,这样我就可以在我的文件中重复使用,但感觉似乎有更好的方法将不断增长的数据流传递到c层。

尝试优化发布的代码没有意义,因为根据定义,pinvoke层缺少最重要的一点:

❌ 避免使用StringBuilder参数。StringBuilder始终编组 创建本机缓冲区副本。因此,这可能是非常危险的 效率低下


在被调用者中分配内存,在调用者中取消分配内存可能是最好的选择。使用共享分配器,或导出本机释放。在我看来,回调的想法是笨拙的。我确实同意回调看起来笨拙,但我不同意内存被叫方/调用方模式在这里是最好的。为什么不在C中实现一个映射到a的OFstream接口呢?显然,这取决于您如何实现。对于这种互操作,我倾向于使用低级C风格的接口,并以更惯用的方式将其封装在使用本机库的任何语言中。关键是pinvoke只是一块垫脚石,高级代码看不到它。但是你想怎么做就怎么做。
private delegate System.Text.StringBuilder StringBuilderEnsureCapacity(int capacity);
[System.Runtime.InteropServices.DllImport(NativeLibraryName, EntryPoint="c_read_private_format")]
private static extern bool c_read_private_format(string filename, System.IntPtr aCallback, System.Text.StringBuilder data, int size);

private static System.Text.StringBuilder callback(int capacity)
{
    buffer.EnsureCapacity( capacity );
    return buffer;
}

public static string readIntoString(string filename) {
  StringBuilderEnsureCapacity del = new StringBuilderEnsureCapacity(callback);
  System.IntPtr ptr = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(del)
  if( c_read_private_format( ptr, buffer, buffer.Capacity ) ) {
    string str = buffer.ToString();
    return str;
  }
  return null;
}