如何使用SWIG

如何解决如何使用SWIG

我正在尝试为System.Text.Json.Utf8JsonWriter和System.Text.Json.Utf8JsonReader构建C ++包装器(使用SWIG),以使C ++代码可以将JSON读写到同一读者。客户端C#代码。为此,我要在C ++中创建与Utf8JsonWriter和Utf8JsonReader类的接口匹配的抽象类,然后使用SWIG Director功能使我能够在C#中创建派生类,该派生类实现此接口并充当调用相应接口的代理实际读取器/写入器上的方法。例如,在C ++中,JsonWriter类定义为:

class JsonWriter
{
public:
    virtual ~JsonWriter() {}

    virtual void WriteStartObject() = 0;
    virtual void WriteStartObject(const char* propertyName) = 0;
    virtual void WriteEndObject() = 0;

    // .. etc
};

SWIG(启用了导演功能)会在C#中生成一个匹配的类,并通过将委托(回调)传递给它在C ++中自动生成的派生类来连接方法。这使我能够在C#中实现一个派生类,该类重写每个方法并充当实际Utf8JsonWriter的代理,如下所示:

public class JsonWriterProxy : JsonWriter
{
    private readonly Utf8JsonWriter _writer;

    public JsonWriterProxy(Utf8JsonWriter writer)
        : base()
    {
        _writer = writer;
    }

    public override void WriteStartObject() => _writer.WriteStartObject();
    public override void WriteStartObject(string propertyName) => _writer.WriteStartObject(propertyName);
    public override void WriteEndObject() => _writer.WriteEndObject();
    // ... etc
}

这对于Writer来说效果很好,而且考虑到所有幕后打来的电话,其性能令人惊讶地好。问题出在System.Text.Json.Utf8JsonReader实现上。在这种情况下,Microsoft选择将Utf8JsonReader实现为“引用结构”而不是类。这意味着我不能简单地在代理类中存储对原始Utf8JsonReader对象的引用(因为该语言禁止这样做)。我可以通过将Utf8JsonReader与代理对象一起传递给C ++反序列化方法来解决此问题。 C ++反序列化方法如下:

 typedef void* RefJsonReader;
 void Deserialize(cylite::io::JsonReader* reader,RefJsonReader refReader)
 {
        reader->SetRefReader(refReader);
        if (reader->TokenType() != JsonTokenType::StartObject) throw new std::exception();
        while (reader->Read())
        {
        // .. etc
        }
}

以及相关的C#pinvoke定义:

[DllImport("IO",EntryPoint="CSharp_fIO_Exam_Deserialize___")]

public static extern void Deserialize(global::System.Runtime.InteropServices.HandleRef jarg1,global::System.Runtime.InteropServices.HandleRef jarg2,ref System.Text.Json.Utf8JsonReader jarg3);

C ++反序列化代码在C ++ JsonReader上调用SetRefReader方法以保存该引用,然后在每次调用时将其传递回C#委托,如下所示:

typedef void* RefJsonReader;

class JsonReader
{
private:
    
    RefJsonReader _refReader = nullptr;

public:

    virtual ~JsonReader() {}

    inline void SetRefReader(RefJsonReader reader)
    {
        _refReader = reader;
    }

    inline bool Read()
    {
        return Read(_refReader);
    }

    inline void Skip()
    {
        return Skip(_refReader);
    }
    // .. etc


protected:

    virtual bool Read(RefJsonReader reader) = 0;
    virtual void Skip(RefJsonReader reader) = 0;
    virtual unsigned char TokenType(RefJsonReader reader) = 0;
    // .. etc
} 

以及相应的C#Reader代理类(在每个方法中均采用ref Utf8JsonReader参数):

   public class JsonReaderProxy : JsonReader
    {
        protected override bool Read(ref Utf8JsonReader reader) => reader.Read();
        protected override void Skip(ref Utf8JsonReader reader) =>  reader.Skip();
        protected override byte TokenType(ref Utf8JsonReader reader) => (byte)reader.TokenType;
        // .. etc
    }

此代码实际上有效。但是必须同时将代理类和ref Utf8JsonReader传递给每个反序列化方法(使它与编写不对称)有点混乱。我想做的是从代理类的构造函数中有效地调用SetRefReader方法,这意味着我不必将其作为附加参数传递给每个反序列化方法。但是,这不起作用(由于内存错误而崩溃)。起初我以为仅仅是因为通过ref传递时传递的实际上是本地堆栈变量的地址,而该地址又将地址保存到实际的struct中。因此,我更改了SetReader调用,以获取参数的地址,并将取消引用的地址保存在_refReader变量中,然后将该变量的地址传递回Proxy的调用中,如下所示:

inline void SetRefReader(RefJsonReader* reader)
{
     _refReader = *reader;
}

inline bool Read()
{
    return Read(&_refReader);
}

这看起来很有希望,因为我可以看到reader参数的值不同,这取决于在调用堆栈中从何处调用的,被取消引用的* reader值始终是相同的地址。但是,当通过带有ref Utf8JsonReader参数的委托方法回调到代理时,这也会崩溃。

对于C#如何传递ref struct参数,似乎有些我不完全了解。如果有人有见识,将不胜感激。对于这个问题的长度,我深表歉意,但我认为值得为我要实现的目标提供一些背景信息。

编辑: 一些进一步的信息。为了更好地理解这一点,我创建了自己的ref结构,如下所示:

public ref struct TestStruct
{
    public int value;
    public Span<byte> data;
}

我只更改了C#导入定义以通过它,而不是Utf8JsonReader引用结构。这完全没有问题,完全可以设置构造函数的引用。通过在结构中填充数据值,很明显,传递的实际上只是指向数据的指针(而不是指针的地址)。

奇怪的是,如果我随后更改结构定义以嵌入Utf8JsonReader,例如:

public ref struct TestStruct
{
    public int value;
    Utf8JsonReader reader;
    public Span<byte> data;
}

如果我从构造函数设置值,它将再次失败。如果我从Serialize函数内部设置引用,则它会工作(就像以前一样),尽管有趣的是由于某种原因,当重复调用回调时,它会慢很多(大约x20),那么如果Utf8JsonReader不是该结构的一部分。这一切都很奇怪。为什么要在引用中传递的结构中包含其他结构,却根本改变了行为-为何会使它变慢。

编辑2:好,所以我想我知道可能会发生什么。我猜测出于某种原因,当敲打Utf8JsonReader(或包含它的结构)时,运行时决定通过在调用的途中将结构的副本复制到缓冲区中来编组参数。地址并在出站时从该缓冲区复制回来。由于在调用第二个函数时此缓冲区超出范围,因此该地址不再有效。委托调用可能发生相同的事情-这就是为什么它们这么慢。如果是这种情况,问题仍然存在,为什么不为自己的引用结构执行此操作,并且是否有更改它的条件?

编辑3:好,现在我知道了为什么Utf8JsonReader的行为有所不同。它是不可复制的类型,根据https://docs.microsoft.com/en-us/dotnet/framework/interop/copying-and-pinning,这意味着它将被复制和编组,因为我的简单结构可被复制并因此被固定。我想我真正需要的只是某种以不透明的方式通过C ++传递托管引用的方法-因为C ++确实需要知道类型是什么。理想情况下,我只是将ref转换为指针(使用一些不安全的代码)。但是,似乎无法创建指向引用结构的指针。也许是另一个问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-