SWIG:结构字段的“输出”类型图需要访问相同结构的另一个字段

时间:2018-12-24 16:34:16

标签: c swig

我正在尝试使用SWIG包装一个C库(我没有写过,并且其接口无法更改)。这通常很简单,但是一个struct的一个字段给我带来了麻烦。相关的struct定义如下:

struct Token {
    const char *buffer;
    const char *word;
    unsigned short wordlen;
    // ... other fields ...
};

buffer是普通的C字符串,应正常显示(但不可变)。 word是问题字段。它是指向buffer字符串内 中某处的指针,并且应理解为长度为wordlen的字符串。我想以普通的只读字符串的形式向高级语言公开此内容,因此它们不必总是要切成薄片。

认为处理此问题的方法是使用专门用于Token::word的“出”字型映射,如下所示:

struct Token {
    %typemap (out) const char *word {
        $result = SWIG_FromCharPtrAndSize($1, ?wordlen?);
    }
}

这就是我遇到的问题:如何从此类型映射访问父结构的wordlen字段?

或者,如果有更好的方法来处理整个问题,请告诉我。

2 个答案:

答案 0 :(得分:0)

不幸的是,SWIG似乎不支持同时映射多个结构成员。检查生成的输出,我们了解到(arg1)指向输入结构。因此,我们必须:

  1. 使word不可变,以便不生成set包装器。
  2. 导入SWIG_FromCharPtrAndSize片段-默认情况下不可用。
  3. 根据需要使用word映射SWIG_FromCharPtrAndSize,指的是(arg1)->wordlen
  4. 跳过wordlen,以便不进行映射(通过%ignore对其进行映射,或者不以SWIG可见的struct形式提供)。

以下是一个完整的示例。首先,标题:

// main.h
#pragma once

struct Token {
  const char *word;
  unsigned short wordlen;
};

struct Token *make_token(void);
extern char *word_check;

还有SWIG模块-请注意,我们逐字使用标题,仅覆盖struct Token的定义:

// token_mod.i
%module token_mod
%{#include "main.h"%}
%ignore Token;
%include "main.h"
%rename("%s") Token;

struct Token {
  %immutable word;
  %typemap (out, fragment="SWIG_FromCharPtrAndSize") const char *word {
    $result = SWIG_FromCharPtrAndSize($1, (arg1)->wordlen);
  }
  const char *word;
  %typemap (out) const char *word;
};

使用Python检查事情是否正常的演示代码:

// https://github.com/KubaO/stackoverflown/tree/master/questions/swig-pair-53915787
#include <assert.h>
#include <stdlib.h>
#include <Python.h>
#include "main.h"

struct Token *make_token(void) {
  struct Token *r = malloc(sizeof(struct Token));
  r->word = "1234";
  r->wordlen = 2;
  return r;
}

char *word_check;

#if PY_VERSION_HEX >= 0x03000000
#  define SWIG_init    PyInit__token_mod
PyObject*
#else
#  define SWIG_init    init_token_mod
void
#endif
SWIG_init(void);

int main()
{
   PyImport_AppendInittab("_token_mod", SWIG_init);
   Py_Initialize();
   PyRun_SimpleString(
            "import sys\n"
            "sys.path.append('.')\n"
            "import token_mod\n"
            "from token_mod import *\n"
            "token = make_token()\n"
            "cvar.word_check = token.word\n");
   assert(word_check && strcmp(word_check, "12") == 0);
   Py_Finalize();
   return 0;
}

最后,制作演示的CMakeLists.txt-可以与Python 2.7或3.x一起使用。注意:要切换Python版本,必须擦除构建目录(或至少必须擦除其中的cmake缓存)。

cmake_minimum_required(VERSION 3.2)

set(Python_ADDITIONAL_VERSIONS 3.6)
project(swig-pair)
find_package(SWIG 3.0 REQUIRED)
find_package(PythonLibs 3.6 REQUIRED)
include(UseSwig)

SWIG_MODULE_INITIALIZE(${PROJECT_NAME} python)
SWIG_ADD_SOURCE_TO_MODULE(${PROJECT_NAME} swig_generated_sources "token_mod.i")
add_executable(${PROJECT_NAME} "main.c" ${swig_generated_sources})
target_include_directories(${PROJECT_NAME} PRIVATE ${PYTHON_INCLUDE_DIRS} ".")
target_link_libraries(${PROJECT_NAME} PRIVATE ${PYTHON_LIBRARIES})

答案 1 :(得分:-1)

高级语言并不关心wordlen是单词的大小。只有C可以。如果您不能更改正在痛饮的C,那么您必须将其保留为原样,并记住在用高级语言编写字符时要注意大小。 Swig和const也不喜欢彼此。 Here是否有关于const的文档