从本机库读取流到C# 我有以下的本机C++函数: // Decode binary format from file 'filename' into stream 'output' bool read_private_format(const char * filename, std::ostringstream & output);
在阅读上一篇文章之后,我创建了一个中间C函数,将其公开给C层,如下所示:从本机库读取流到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
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;
}