C#中的Swig Director异常 - 在C ++中获取异常文本

时间:2015-01-09 00:27:24

标签: c# swig

我们正在使用SWIG 3.0.3在Python,Java和C#中创建C ++库的接口。

我们还提供C ++中的Stream接口,并且使用SWIG Director功能,我们允许用户在他们选择的那些支持的语言中实现此接口。

问题在于,当用户的C#流实现中抛出异常时,C#的实际错误消息将丢失,C ++无法检索它。

我想知道是否有任何解决方案可以解决这个问题。

其他信息:
1。 SWIG Java的文档说这个功能(在导向器中传递异常数据)是SWIG 3的新功能。
2。我必须稍微修改SWIG Python代码才能获得所需的结果。

// C++ Stream Interface
class Stream 
{
    virtual uint Seek(long offset, int origin);
    // ...
};


// C# Implementation of a Stream
class CustomStream : Stream 
{
    public override uint Seek(long offset, int origin)
    {
        throw new Exception("This message should be seen in C++ caller");
        return 0;
    }
}


// C++ calling code
try 
{
    pStream->Seek(0, 0);
}
catch(std::exception e)
{
    // Here's where I want to see the exception text.
    std::cout << e.what();
}

1 个答案:

答案 0 :(得分:1)

事实证明这对C#和SWIG来说真的很难。我能提供的最好的是粗略的解决方法。

我为我们做了以下标题来展示:

#include <iostream>

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

  virtual void Bar() = 0;
};

inline void test_catch(Foo& f) {
  try {
    f.Bar();
  }
  catch (const std::exception& e) {
    std::cerr << "Caught: " << e.what() << "\n";
  }
}

这个Foo的C#实现:

public class CSharpDerived : Foo
{
  public override void Bar()
  {
    System.Console.WriteLine("In director method");
    throw new System.Exception("This is a special message");
  }
}

如果您使用SWIG定位Python,则可以%feature("director:except")使用std::exception。 C#SWIG语言模块似乎并不支持这一点。

我们需要采用的策略来捕获托管异常,然后将其重新抛出为继承自%typemap(csdirectorout) void %{ try { $cscall; } catch(System.Exception e) { // pass e.ToString() somewhere now } %} 的东西。我们需要解决两个问题来为C#模拟这个:

  1. 我们如何从托管代码中捕获异常?
  2. 我们如何注入代码以重新投入SWIG生成的输出的正确位?
  3. 从托管代码中捕获异常:

    看起来有两种解决方法。

    首先,如果你不在Linux上使用mono,我认为my example能够捕捉到这一点,但可能不是vectored exception handlers

    第二种方法是捕获托管代码中的异常。这是可以解决的,但有点像黑客。我们可以使用csdirectorout typemap插入我们的代码来执行此操作:

    csdirectorout

    这里的bodge是我们必须指定导演方法的类型 - 我上面的例子仅适用于不返回任何内容的C ++函数。显然你可以写更多这些类型图(SWIGTYPE会很好),但是那里有很多重复。

    注入代码以作为C ++本机异常重新抛出

    这变得更加丑陋。我没有找到任何可靠的方法将代码注入到生成的Director调用的C ++端。我希望将directorout作为返回类型与csdirectorout类型映射一起工作,但是使用SWIG 3.0.2我无法实现这一点(并在运行SWIG时使用`-debug-tmsearch确认)。我们甚至无法在任何地方使用预处理器播放技巧,并调用一个宏,让我们有机会添加代码。

    我接下来尝试的是让我的%module(directors="1") test %{ #include "test.hh" %} %include <std_string.i> %{ #include <exception> struct wrapped_exception : std::exception { wrapped_exception(const std::string& msg) : msg(msg) {} private: virtual const char * what () const noexcept { return msg.c_str(); } std::string msg; }; %} %inline %{ void throw_native(const std::string& msg) { throw wrapped_exception(msg); } %} %typemap(csdirectorout) void %{ try { $cscall; } catch(System.Exception e) { test.throw_native(e.ToString()); } %} %feature("director") Foo; %include "test.hh" 再次调用一个C ++函数进行重新抛出,然后再调用C#实现完全返回。作为参考,我的尝试看起来像:

    public class runme {
      static void Main() 
      {
        System.Console.WriteLine("Running");
        using (Foo myFoo = new CSharpDerived())
        {
          test.test_catch(myFoo);
        }
      }
    }
    

    我测试了它:

    ---------------------------
    | throw_native()    | C++ |
    | SwigDirectorBar() | C#  |
    | Foo::Bar()        | C++ |
    | test_catch()      | C++ |
    | Main()            | C#  |
    | pre-main()        | ??? |
    ---------------------------
    

    因此,当我们在C#中捕获异常时,我们将字符串传递给另一个C ++函数,最后我们将它作为C ++异常抛出,其中包含如下堆栈:

    %inline %{
    char *exception_pending
    %} 
    %typemap(csdirectorout) void %{
      try {
        $cscall;
      }
      catch(System.Exception e) {
        test.exception_pending = e.ToString();
      }
    %}
    

    但是当异常被抛出时,运行时会爆炸 - 它被我使用Mono的C#实现捕获并被阻止作为C ++异常进一步传播。

    所以目前我得到的最佳解决方案是解决方法:让C#代码设置一个全局变量并在C ++中手动检查它,你可能会发现它已被设置,即在SWIG中接口:

    test_catch()

    并且对inline void test_catch(Foo& f) { try { f.Bar(); check_for_csharp_exception_and_raise(); } catch (const std::exception& e) { std::cerr << "Caught: " << e.what() << "\n"; } } 进行了侵入式更改:

    check_for_csharp_exception_and_raise

    void check_for_csharp_excception_and_raise() { if (exception_pending) { std::string msg = exception_pending; delete[] exception_pending; exception_pending = NULL; throw wrapped_exception(msg); } } 类似于:

    {{1}}

    我真的不喜欢这个解决方案,但似乎是现在提供的最好的东西,至少在没有破坏单声道兼容性和修补SWIG的情况下。