草庐IT

python - 带有来自 boost 预处理器的预处理器宏的 SWIG

coder 2024-02-22 原文

我正在使用此处建议的带有 ToString 实现的枚举: How to convert an enum type variable to a string? 据我所知,它可以很好地利用和工作。

当我尝试将宏打包并导出到用 SWIG 打包的 Python 库时,我的问题就出现了。类似问题:SWIG errors because of preprocessor directive 在那里,解决方案是向 SWIG 接口(interface)添加 header /声明。到目前为止,我还没有成功。很可能我只是不知道我必须添加什么。

尝试过:

%include <boost/preprocessor/config/config.hpp>
%include <boost/preprocessor/stringize.hpp>
%include <boost/preprocessor/seq/for_each.hpp>
%include <boost/preprocessor/seq/enum.hpp>

MWE:

最小.h

#ifndef MINIMAL_H
#define MINIMAL_H
#include <boost/preprocessor.hpp>

//Found this here: https://stackoverflow.com/questions/5093460/how-to-convert-an-enum-type-variable-to-a-string

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data, elem)    \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                \
    enum name {                                                               \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    inline const char* ToString(name v)                                       \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,          \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }

DEFINE_ENUM_WITH_STRING_CONVERSIONS(my_enum, (A)(B))
#endif

最小.cpp

#include <iostream> 
#include "minimal.h"

int main(){
    using namespace std;
    cout << A << ": " << ToString(A) << endl;
    cout << B << ": " << ToString(B) << endl;

}

最小的.i

%module minimal
%{
#include "minimal.h"
%}
%include "minimal.h"

错误的指示性不强。第 29 行是 my_enum 的实际定义。

matthias@rp3deb:~/dvl/swig_boost_minimal$ swig minimal.i
minimal.h:29: Error: Syntax error in input(1).

关于如何包装它有什么建议吗?

最佳答案

如果你想让 SWIG 读取 boost/preprocessor.hpp,你可以这样做:

%module minimal
%{
#include "minimal.h"
%}
%include <boost/preprocessor.hpp>
%include "minimal.h"

因为默认情况下 SWIG 不遵循 #include指令。 (您也可以使用 -includeall 使其跟随它们)。在这种情况下,虽然我认为让 SWIG 预处理器对 Boost 预处理器库使用的疯狂魔法有任何意义是一个失败的原因。

相反,尽管我们可以尝试使用同样不错但“Pythonic”语法来代替。本质上我们要做的是写一个完全不同的版本 DEFINE_ENUM_WITH_STRING_CONVERSIONS仅适用于 SWIG 包装器。不过,它将与 C++ 所见的定义兼容。

为此,我首先将您的文件 minimal.h 分成两个文件。一种具有宏定义,另一种使用它。 (我们可以用不同的方式来做到这一点,例如用 #ifndef DEFINE_ENUM_WITH_STRING_CONVERSIONS#ifndef SWIG 包装宏定义,这将是同样有效的解决方案)。

因此我们现在有 enum.hh:

#ifndef ENUM_H
#define ENUM_H
#include <boost/preprocessor.hpp>

//Found this here: https://stackoverflow.com/questions/5093460/how-to-convert-an-enum-type-variable-to-a-string
#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data, elem)    \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                \
    enum name {                                                               \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    inline const char* ToString(name v)                                       \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,          \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }
#endif

和 minimal.h:

#ifndef MINIMAL_H
#define MINIMAL_H
#include "enum.h"

DEFINE_ENUM_WITH_STRING_CONVERSIONS(my_enum, (A)(B))
#endif

因此您的 minimal.cpp 继续像以前一样工作,但现在我们可以编写一个至少可以编译的 SWIG 模块,即使它还没有做任何有用的事情:

%module minimal
%{
#include "minimal.h"
%}
%define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name,enumerators)
%enddef
%include "minimal.h"

目前有一个 stub ,我们将要填写的 SWIG 特定宏。我这样做的方式有点难看,仅仅是因为我试图完全避免改变现有宏的定义/使用方式。

我作为起点生成的是另一个文件,enum.i:

%include <std_vector.i>
%include <std_string.i>

%{
#include <vector>
#include <string>
#include <tuple>
%}

%define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name,enumerators)
%{
  typedef std::tuple<name,std::string> name ## _entry;
  struct name ## _helper {
    std::vector<name ## _entry> list;
    name ## _helper(const name value) {
      list.push_back(std::make_tuple(value,ToString(value)));
    }
    name ## _helper operator()(const name value) {
      list.push_back(std::make_tuple(value,ToString(value)));
      return *this;
    }
  };

  static const std::vector<name ## _entry> name ## _list = name ## _helper enumerators . list;
%}

struct name ## _entry {
  %extend {
    const unsigned long value {
      return std::get<0>(*$self);
    }
    const std::string& label {
      return std::get<1>(*$self);
    }
  }
};

%template(name ## vec) std::vector<name ## _entry>;

const std::vector<name ## _entry> name ## _list;

%enddef

这样 minimal.i 只需要变成:

%module minimal

%{
#include "minimal.h"
%}

%include "enum.i"
%include "minimal.h"

该宏所做的只是获取 enumerators 的值。 , 这将类似于 (A)(B)并生成一些完全标准(如果古怪)的 C++ 代码,将其扩展为 std::vector<std::tuple<my_enum,std::string>> .这是通过将第一个枚举成员映射到构造函数调用,并将其余成员映射到重载的 operator() 来完成的。 .我们使用 ToString()由 enum.h 提供以查找字符串表示形式。最后,我们的宏有足够的信息以一种在 Python 中有意义的方式包装元组 vector 。

有了这个,我们可以做类似的事情:

import minimal
print ", ".join(("%s(%d)" % (x.label,x.value) for x in minimal.my_enum_list))

其中,编译和运行时给出:

A(0), B(1)

即足以开始编写能够识别 C++ 枚举的标签和值的 Python 代码。

但我们不要就此止步!为什么我故意称结果 vector 为my_enum_list而不仅仅是 my_enum ?因为我们现在可以做的更多。

Python 2.7 没有任何默认的“enum-ish”,但这并不妨碍我们将其包装为对了解枚举的人来说既 Pythonic 又自然的东西。我通过阅读 this other answer 获得了 Python 2.7 枚举支持.首先,我使用 %pythoncode 在文件中添加了一些通用枚举支持例程。 ,(在最终源代码中标记为#1)但在 SWIG 宏之外,因为不需要改变它。我还添加了一个 %pythoncode在每个实际枚举调用一次的 SWIG 宏(标记为#2)中。为了完成这项工作,我必须转换 const std::vector从以前的版本转换为一个函数,以便可以在生成的 Python 的正确部分访问它。最后,我不得不向 SWIG 展示真实枚举的前向声明,以说服它实际接受它作为函数的参数。最终结果是:

%include <std_vector.i>
%include <std_string.i>

%{
#include <vector>
#include <string>
#include <tuple>
%}

// #1
%pythoncode %{
class EnumValue(int):
  def __new__(cls,v,l):
    result = super(EnumValue,cls).__new__(cls,v)
    result._value = l
    return result
  def __str__(self):
    return self._value

def make_enum(name,enums):
    return type(name, (), enums)
%}

%define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name,enumerators)
%{
  typedef std::tuple<name,std::string> name ## _entry;
  struct name ## _helper {
    std::vector<name ## _entry> list;
    name ## _helper(const name value) {
      list.push_back(std::make_tuple(value,ToString(value)));
    }
    name ## _helper operator()(const name value) {
      list.push_back(std::make_tuple(value,ToString(value)));
      return *this;
    }
  };

  static const std::vector<name ## _entry> name ## _list() {
    return name ## _helper enumerators . list;
  }
%}

struct name ## _entry {
  %extend {
    const unsigned long value {
      return std::get<0>(*$self);
    }
    const std::string& label {
      return std::get<1>(*$self);
    }
  }
};

%template(name ## vec) std::vector<name ## _entry>;

const std::vector<name ## _entry> name ## _list();

// #2
%pythoncode %{
  name = make_enum('name', {x.label: EnumValue(x.value, x.label) for x in name ## _list()})
%}

enum name;

%enddef

我在 minimal.i 中添加了一个函数来证明它确实有效:

%module minimal

%{
#include "minimal.h"
%}

%include "enum.i"
%include "minimal.h"

%inline %{
  void foo(const my_enum& v) {
    std::cerr << "GOT: " << v << "\n";
  }
%}

最后测试它:

import minimal
print minimal.my_enum
print minimal.my_enum.A
print minimal.my_enum.B

minimal.foo(minimal.my_enum.B)

您会很高兴看到它起作用并产生了:

<class 'minimal.my_enum'>
A
B
GOT: 1

如果您使用的是 Python 3,则可能有更好的方法来 represent enums ,但我现在将其作为练习留给读者。您显然也可以根据自己的喜好调整 Python 2.7 假枚举。

关于python - 带有来自 boost 预处理器的预处理器宏的 SWIG,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25197380/

有关python - 带有来自 boost 预处理器的预处理器宏的 SWIG的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  7. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  8. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  9. Python 刷Leetcode题库,顺带学英语单词(31) - 2

    ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

  10. python - 是否可以使用 Ruby 或 Python 禁用 anchor /引用来发出有效的 YAML? - 2

    是否可以在PyYAML或Ruby的Psych引擎中禁用创建anchor和引用(并有效地显式列出冗余数据)?也许我在网上搜索时遗漏了一些东西,但在Psych中似乎没有太多可用的选项,而且我也无法确定PyYAML是否允许这样做.基本原理是我必须序列化一些数据并将其以可读的形式传递给一个不是真正的技术同事进行手动验证。有些数据是多余的,但我需要以最明确的方式列出它们以提高可读性(anchor和引用是提高效率的好概念,但不是人类可读性)。Ruby和Python是我选择的工具,但如果有其他一些相当简单的方法来“展开”YAML文档,它可能就可以了。 最佳答案

随机推荐