【问题标题】:SWIG Python C++ struct as in/out parameterSWIG Python C++ 结构作为输入/输出参数
【发布时间】:2021-12-30 16:10:14
【问题描述】:

老实说,我在这个网站上阅读了很多关于 struct 主题的帖子。但我需要你的帮助。

我有 C 风格的结构

   struct Time
   {
      uint16_t  year; //    year with four digits like 2016
      uint8_t   month; // 1 .. 12
      uint8_t   day; // 1 .. 31
      uint8_t   hour; // 0 .. 23, 24 hour representation
      uint8_t   minute; // 0 .. 59
      uint8_t   second; // 0 .. 59
   };

以及具有成员函数的类,其实现在 DLL 中。

class DeviceInterface {
virtual uint32_t getTime(Time&  time) = 0;
};

其中 uint32_t 值是状态码。

这里是自动生成的 SWIG C++ 代码:

SWIGINTERN PyObject *_wrap_getTime(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  DeviceInterface *arg1 = (DeviceInterface *) 0 ;
  Time *arg2 = 0 ;
  void *argp1 = 0 ;
  int res1 = 0 ;
  void *argp2 = 0 ;
  int res2 = 0 ;
  PyObject *swig_obj[2] ;
  uint32_t result;
  
  if (!SWIG_Python_UnpackTuple(args, "getTime", 2, 2, swig_obj)) SWIG_fail;
  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_DeviceInterface, 0 |  0 );
  if (!SWIG_IsOK(res1)) {
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "getTime" "', argument " "1"" of type '" "DeviceInterface *""'"); 
  }
  arg1 = reinterpret_cast< DeviceInterface * >(argp1);
  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_Time,  0 );
  if (!SWIG_IsOK(res2)) {
    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "getTime" "', argument " "2"" of type '" "Time &""'"); 
  }
  if (!argp2) {
    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "getTime" "', argument " "2"" of type '" "Time &""'"); 
  }
  arg2 = reinterpret_cast< Time * >(argp2);
  result = (uint32_t)(arg1)->getTime(*arg2);
  resultobj = SWIG_From_unsigned_SS_int(static_cast< unsigned int >(result));
  return resultobj;
fail:
  return NULL;
}

从上面的代码中,我看不到返回了什么时间值,即 arg2 变量。那么我需要在 SWIG 接口文件中编写什么来获取状态码和时间

【问题讨论】:

    标签: python c++ struct swig


    【解决方案1】:

    您可以编写一个类型映射来附加Time 输出参数。 SWIG 为可以根据需要通过SWIG_NewPointerObj() 生成的结构生成代理。完整示例如下:

    DeviceInterface.h(最小实现)

    #include <stdint.h>
    
    struct Time
    {
        uint16_t year;   // year with four digits like 2016
        uint8_t  month;  // 1 .. 12
        uint8_t  day;    // 1 .. 31
        uint8_t  hour;   // 0 .. 23, 24 hour representation
        uint8_t  minute; // 0 .. 59
        uint8_t  second; // 0 .. 59
    };
    
    class DeviceInterface {
    public:
        virtual uint32_t getTime(Time& time) {
            time.year = 2021;
            time.month = 12;
            time.day = 30;
            time.hour = 9;
            time.minute = 47;
            time.second = 30;
            return 0;
        }
    };
    

    test.i

    %module test
    
    %{
    #include "DeviceInterface.h"
    %}
    
    %include <stdint.i>
    
    // Do not require an input parameter for Time.
    // Instead, SWIG will allocate one.
    %typemap(in,numinputs=0) Time& %{
        $1 = new Time;
    %}
    
    // After calling the function, process Time as an output.
    // Convert the allocated pointer to a SWIG Python wrapper
    // and pass ownership to Python.  Python will free the object
    // when it goes out of scope.
    %typemap(argout) Time& (PyObject* tmp) %{
        // Convert C pointer to SWIG Python wrapper
        tmp = SWIG_NewPointerObj($1, $1_descriptor, SWIG_POINTER_OWN);
        // Append to existing uint32_t return value
        $result = SWIG_Python_AppendOutput($result, tmp);
    %}
    
    %include "DeviceInterface.h"
    

    演示:

    >>> import test
    >>> d=test.DeviceInterface()
    >>> r=d.getTime()
    >>> r
    [0, <test.Time; proxy of <Swig Object of type 'Time *' at 0x000001ED4F866090> >]
    >>> r[1].year
    2021
    >>> r[1].month
    12
    

    如果您想自定义 SWIG 包装器的显示以使 Time 更易于阅读,您可以使用以下方法扩展 Time 对象:

    %module test
    
    %{
    // Added to support the __repr__ implementation
    #include <string>
    #include <sstream>
    
    #include "DeviceInterface.h"
    %}
    
    %include <stdint.i>
    %include <std_string.i> // SWIG support for std::string.
    
    %typemap(in,numinputs=0) Time& %{
        $1 = new Time;
    %}
    
    %typemap(argout) Time& (PyObject* tmp) %{
        tmp = SWIG_NewPointerObj($1, $1_descriptor, SWIG_POINTER_OWN);
        $result = SWIG_Python_AppendOutput($result, tmp);
    %}
    
    // Extend time to suport Python's __repr__.
    // It must return a string representing how to display the object in Python.
    %extend Time {
        std::string __repr__()
        {
            std::ostringstream ss;
            ss << "Time(year=" << $self->year << ", month=" << (unsigned)$self->month
               << ", day=" << (unsigned)$self->day << ", hour=" << (unsigned)$self->hour
               << ", minute=" << (unsigned)$self->minute << ", second=" << (unsigned)$self->second << ")";
            return ss.str();
        }
    }
    
    %include "DeviceInterface.h"
    

    演示:

    >>> import test
    >>> d=test.DeviceInterface()
    >>> r=d.getTime()
    >>> r
    [0, Time(year=2021, month=12, day=30, hour=9, minute=47, second=30)]
    

    【讨论】:

    • +100500 太棒了,谢谢。如果我返回由Py_BuildValue 打包的元组而不是由SWIG_Python_AppendOutput 创建的列表,可以吗?
    • @Den SWIG_Python_AppendOutput 很聪明。如果正确处理 void 返回值和类型图的多个实例,例如您之前的问题,它有两个输出参数。如果您正在考虑构建时间元组而不是代理 Time 对象,请注意 Time 代理可以传递给采用 Time 对象的其他函数,而元组必须再次转换回 Time 对象。
    • 我的意思不是返回状态码和时间对象的列表,而是返回一个元组,以便获得以下status_code, time = d.getTime()
    • @Den 你也可以用一个列表来做到这一点。它不需要是一个元组
    猜你喜欢
    • 2019-04-22
    • 2021-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多