【问题标题】:Recommended approaches for making my code swiggable?使我的代码可挥霍的推荐方法?
【发布时间】:2011-03-11 00:34:42
【问题描述】:

我目前正在重构一个用 C++ 编写的 Tcl 插件库。最初的代码是手写的。存在第二个库,它为 Java 做同样的事情。

重构后的库将是一个单一的 C++ 库,可用于创建与不同语言的绑定。

我对 SWIG 的第一次测试很有希望。但是,也会产生很多垃圾。各种基类和实用程序都被导出。从脚本的角度来看,这些没有意义,只会增加混乱。

我能想到的可能解决方案是:

  1. 在原始代码库中使用#ifndef SWIG 过滤掉不需要的代码
  2. 围绕 API 类创建一个与 SWIG 兼容的包装器。
  3. 区分公共标头和专用标头。公共标头是不包含任何实现的纯抽象基类。私有标头继承并实现它们。仅 SWIG 公开标头。
  4. 与上述解决方案相反:为每个 API 类继承一个与 SWIG 兼容的类。

我目前倾向于解决方案 3。但是,我不太确定,所以我想知道 SO 社区对此的看法。欢迎分享您的想法。

更新

我忘了列出一个解决方案:

  • 不应由 SWIG 导出的代码可能不应该出现在您班级的公共部分中。

也许这就是答案。星期一我再看看。

更新

我找到了一个解决方案。看我的回答。

【问题讨论】:

    标签: c++ swig


    【解决方案1】:

    任何意味着 C++ 库对 C++ 用户变得不那么有用的方法都不是理想的解决方案。

    1. .hpp 文件中间的#ifdef SWIG:用不必要的杂物弄乱你的 C++,所以它并不理想
    2. SWIG 特定接口:这是一个可行的选项,但仅当您要向 SWIG 公开的代码比基本 C++ API 更高级别时才有意义。
    3. Public 与 Private 接口:可能有道理,但您必须再次询问 API 的 C++ 用户的成本是多少?您是否过多地限制了公共接口?谁有权访问私有接口?是否应该考虑使用pImpl idiom
    4. 每个接口的 SWIG 兼容类:可能比必要的工作更多。

    首先,将您的 SWIG 相关代码与 API 的其余部分分开。

    您可能不想将 .hpp 文件直接导入 SWIG(如果在库的初始设计期间未考虑 SWIG),但如果您这样做,您希望使用 SWIG .i 文件来提供帮助你收拾烂摊子。我们使用三种基本方法,每种方法都有不同的用例。

    首先,直接包含。如果您知道自己的 API 很好、很干净并且非常适合 SWIG 解析,这将非常有用:

    // MyClass.i
    %{
      #include "MyClass.hpp" // included for the generated .cxx file
    %}
    
    %include "MyClass.hpp" // included and parsed directly by SWIG
    

    第二种情况是大多数情况下的代码。这是考虑了 SWIG 的代码,但确实需要一些我们不想暴露给 SWIG 的 C++ 用户的东西:

    // MyClass.i
    %{
      #include "MyClass.hpp" // included for the generated .cxx file
    %}
    
    %ignore MyClass::someFunction(); // This function really just causes us problems
    %include "MyClass.hpp" // included and parsed directly by SWIG
    

    第三种情况,也可能是您要使用的情况,是直接选择要向 SWIG 公开的函数。

    // MyClass.i
    %{
      #include "MyClass.hpp" // included for the generated .cxx file
    %}
    
    // With this example we provide exactly as much information to SWIG as we want
    // it to have. Want it to know the base class? Add it. Don't want it to know about
    // a function? Leave it out. want to add a new function? %extend it.
    class MyClass
    {
      void importantFunction();
      void importantFunction2();
    }
    

    【讨论】:

    • 我同意 SWIG 相关代码应该与 API 分离(库应该不知道 SWIG 的存在)。我没有考虑在接口文件中编写类声明。感谢您的信息。
    • 我“接受”了您的回答,因为它很有帮助。如果您有兴趣,可以在我自己的答案中阅读我的解决方案。
    【解决方案2】:

    我也会使用方法 #3。我在我的项目中使用了类似的方法,它也被 COM 使用(接口由私有实现类继承)。

    以这种方式检测错误和维护代码真的很容易!不幸的是,您将最终将所有功能都实现为虚拟功能,但这应该不是大问题...

    分离界面将保持它非常干净和易于理解!

    【讨论】:

      【解决方案3】:

      我的最终解决方案:只需 SWIG 原始代码库。为了避免生成不相关的代码,我使用以下技术。按优先顺序:

      1. 将非 swig 代码设为私有或受保护。如果不需要 swigged,那么它可能不需要公开。

      2. 如果可能,更改原始代码以使其与 SWIG 更兼容。我用抽象基类替换了curiously recurring template pattern。我愿意为 SWIG 做出牺牲 :)

      3. 在接口文件中添加%ignore语句。

      4. 使用#ifndef SWIG 将其过滤掉。我不喜欢污染我的原始代码,所以我只把它作为最后的手段。

      关于我之前的想法:

      • 围绕 API 类创建一个与 SWIG 兼容的包装器。
      • 区分公共标头和专用标头。公共标头是 纯抽象基类 不包含任何实现。私人的 标头继承并实现它们。 仅 SWIG 公开标头。
      • 与上述解决方案相反:继承与 SWIG 兼容的类 每个 API 类。

      所有这些解决方案都需要编写与 SWIG 兼容的包装器代码。这有点傻,因为您放弃了 SWIG 的最强卖点:自动生成包装器代码。如果我为 SWIG 编写自己的包装器代码,那么我不妨编写常规的 JNI 代码。

      也就是说,我意识到对于某些项目而言,编写包装代码可能是最具成本效益的解决方案。但是,我的情况并非如此。

      【讨论】:

        猜你喜欢
        • 2021-08-30
        • 2022-06-16
        • 1970-01-01
        • 2019-05-09
        • 2010-09-26
        • 2013-10-16
        • 1970-01-01
        • 2012-11-29
        • 2019-09-15
        相关资源
        最近更新 更多