简体   繁体   English

如何使用 SWIG 包装 std::function 对象?

[英]How to use SWIG to wrap std::function objects?

I have seen quite a few similar questions, but have not found a solution to my particular problem.我看过很多类似的问题,但还没有找到解决我的特定问题的方法。 I am attempting to SWIGify some C++11 code that uses std::function, so I can use it in my Java application.我正在尝试 SWIGify 一些使用 std::function 的 C++11 代码,因此我可以在我的 Java 应用程序中使用它。

I have encountered shared pointers like this:我遇到过这样的共享指针:

virtual std::shared_ptr<some::ns::TheThing> getTheThing(unsigned short thingID);

and successfully handled them with the shared_ptr directive like so:并使用 shared_ptr 指令成功处理它们,如下所示:

%shared_ptr(some::ns::TheThing);

I have encountered vectors of shared pointers like this:我遇到过这样的共享指针向量:

virtual std::vector<std::shared_ptr<some::ns::TheThing>> getAllTheThings() const = 0;

and successfully handled them with a template like so:并使用如下模板成功处理它们:

%template(ThingVector) std::vector<std::shared_ptr<some::ns::TheThing>>;

Now I have a method like this:现在我有一个这样的方法:

 void registerThingCallback(std::function<void(std::shared_ptr<some::ns::TheThing>) > func);

and I cannot get SWIG to wrap it properly.我无法让 SWIG 正确包装它。 I have tried using %callback, directors, %template, and %inline functional code, as I have seen examples with all of these things, but have not been able to get anything that seems close to working.我已经尝试使用 %callback、directors、%template 和 %inline 函数代码,因为我已经看到了所有这些东西的例子,但没有得到任何看起来接近工作的东西。 Here is a little more context around the function call if that helps (sanitized and reduced):如果有帮助(清理和减少),这里有更多关于函数调用的上下文:

thing_callback.h thing_callback.h

#include <functional>

namespace some {
  namespace ns {

    /**
     * Hold some callbacks.
     */
    class ThingCallbacks {
    public:

        /**
         * Registers a callback 
         * @param func The callback function
         */
        void registerThingCallback(std::function<void(std::shared_ptr<some::ns::TheThing>) > func);

    };

  }
}

Update更新

Based on Flexo's great answer below, I am much closer to a solution.根据下面 Flexo 的精彩回答,我离解决方案更近了。 I was able to get the examples below working exactly as advertised.我能够让下面的示例完全按照宣传的方式工作。 I tried incorporating it into my actual code, but ran into issues.我尝试将它合并到我的实际代码中,但遇到了问题。 To expand on my earlier simplified example, here is my definition of TheThing:为了扩展我之前的简化示例,这是我对 TheThing 的定义:

test_thing.h test_thing.h

#ifndef THE_THING_H
#define THE_THING_H

#include <string>

namespace some {
  namespace ns {

    class TheThing {
    public:

        virtual ~TheThing() {};

        virtual unsigned long longThing() const = 0;

        virtual std::string stringThing() const = 0;
    };
  }
}

#endif  /* THE_THING_H */

And here is my .i file.这是我的 .i 文件。 To have as few moving parts as possible, I've basically just taken the int and double from the code provided in the answer below, and replaced them with a shared pointer to my object.为了尽可能少地移动部件,我基本上只是从下面的答案中提供的代码中取出 int 和 double ,并将它们替换为指向我的对象的共享指针。

func_thing_test.i func_thing_test.i

%module(directors="1") Thing
%include "stl.i"
%include "std_function.i"
%include "std_shared_ptr.i"

%shared_ptr(some::ns::TheThing);


%typemap(javadirectorin) std::shared_ptr<some::ns::TheThing> "new $typemap(jstype, some::ns::TheThing)($1,false)";
%typemap(directorin,descriptor="Lsome.ns.typemap(jstype, some::ns::TheThing);") std::shared_ptr<some::ns::TheThing> %{
     *($&1_type*)&j$1 = &$1;
%}


%include "test_thing.h"
%include "thing_callback.h"

%{
#include <memory>

#include "test_thing.h"
#include "thing_callback.h"

%}

%std_function(Functor, void, std::shared_ptr<some::ns::TheThing>);

%{
#include <iostream>
void add_and_print(std::shared_ptr<some::ns::TheThing> thing) {
  std::cout << "here\n";
}
%}

%callback("%s_cb");
void add_and_print(std::shared_ptr<some::ns::TheThing>);
%nocallback;

%inline %{
  std::function<void(std::shared_ptr<some::ns::TheThing>)> make_functor() {
    return [](std::shared_ptr<some::ns::TheThing>){
      std::cout << "make functor\n";
    };
  }

  void do_things(std::function<void(std::shared_ptr<some::ns::TheThing>)> in) {
      std::cout << "inside do things\n";
  }
%}

test_thing.h is what I just posted above, and thing_callback.h is the code I posted in my original question. test_thing.h 是我刚刚在上面发布的内容,而 thing_callback.h 是我在原始问题中发布的代码。 This all compiles through the swig, c++, and Java chain without error, but it looks like swig is having a little trouble connecting the dots between the c++ and Java.这一切都通过 swig、c++ 和 Java 链编译而没有错误,但看起来 swig 在连接 c++ 和 Java 之间的点时有点麻烦。 It creates these three classes:它创建了这三个类:

SWIGTYPE_p_f_std__function__f_std__shared_ptr__some__ns__TheThing____void____void
SWIGTYPE_p_f_std__shared_ptr__some__ns__TheThing____void
SWIGTYPE_p_std__functionT_void_fstd__shared_ptrT_some__ns__TheThing_tF_t

And unfortunately, most of the methods from the simple Java main code now take or return these objects, which make them fairly unusable.不幸的是,来自简单 Java 主代码的大多数方法现在都接受或返回这些对象,这使得它们相当不可用。 Any idea how to fix this?知道如何解决这个问题吗? Thank you!谢谢!

A little more detail for completeness: I am using the following three scripts to compile and run the code.关于完整性的更多细节:我使用以下三个脚本来编译和运行代码。 The parameters are slightly different, but I don't think it matters.参数略有不同,但我认为这无关紧要。 On my end it is set up as an Eclipse maven project.在我的最后,它被设置为一个 Eclipse maven 项目。 These scripts reside in the root of my project, headers and swig files reside in src/main/resources, java source files reside in src/main/java, and java compiled classes reside in target/classes.这些脚本驻留在我的项目的根目录中,头文件和 swig 文件驻留在 src/main/resources 中,java 源文件驻留在 src/main/java 中,java 编译的类驻留在 target/classes 中。 Eclipse performs the java compilation. Eclipse 执行java 编译。

swigthing.sh swigthing.sh

MODULE_NAME=Thing
PACKAGE=some.ns
OUTDIR=./src/main/java/some/ns
I_FILE=./src/main/resources/func_thing_test.i

mvn clean

rm $OUTDIR/*.*
mkdir -p $OUTDIR

swig -java -c++ -module $MODULE_NAME -package $PACKAGE -outdir $OUTDIR $I_FILE

./compileThingSwigTest.sh

compileThingSwigTest.sh compileThingSwigTest.sh

#!/bin/bash

pushd src/main/resources
g++ -c -std=gnu++11 -fpic \
func_thing_test_wrap.cxx \
-I/usr/lib/jvm/java/include \
-I/usr/lib/jvm/java/include/linux

g++ -shared func_thing_test_wrap.o -o libFunc.so
popd

runThingTest.sh运行测试文件

pushd target/classes
java -Xmx512M -Xms512M -Djava.library.path=. some.ns.test.RunThingTest
popd

Last Update最后更新

Fixed the code above to pass the right parameters to std_function.修复了上面的代码以将正确的参数传递给 std_function。 Now between the question and answer there is a complete working example of what I was after.现在在问题和答案之间有一个完整的工作示例,说明我所追求的。

Although SWIG doesn't provide a std_function.i natively we can build one ourselves this with a bit of work.尽管 SWIG 本身不提供 std_function.i,但我们可以通过一些工作自己构建一个。 My answer here is a more generalised version of my a previous of mine answer , looking at this problem for a specific instance and targeting Python.我在这里的答案是我之前的答案的更通用版本,针对特定实例查看此问题并针对 Python。 I'll go through several iterations of it, which define a %std_function macro for generic std::function wrapping.我将经历它的几次迭代,它为通用std::function包装定义了一个%std_function宏。

I'm assuming there are four things you want to achieve from a wrapping of std::function , which become our main requirements:我假设您希望通过包装std::function实现四件事,这成为我们的主要要求:

  1. We want to be able to call std::function objects from within Java code.我们希望能够从 Java 代码中调用std::function对象。
  2. The wrapped std::function objects need to get passed around like any other object, including crossing the language boundaries in either direction.包装的std::function对象需要像任何其他对象一样传递,包括在任一方向跨越语言边界。
  3. It should be possible to write std::function objects inside of Java, which can be passed back to C++ without having to modify existing C++ code that works on std::function objects (ie maintaining type erasure of std::function cross language)应该可以在 Java 内部编写std::function对象,这些对象可以传递回 C++,而无需修改现有的适用于std::function对象的 C++ 代码(即维护std::function跨语言的类型擦除)
  4. We should be able to construct std::function objects in Java using C++ pointer to function types.我们应该能够使用指向函数类型的 C++ 指针在 Java 中构造std::function对象。

I'm going to work through these and show how we can achieve this.我将解决这些问题并展示我们如何实现这一目标。 Where possible I'll keep the solution language agnostic too.在可能的情况下,我也会使解决方案语言不可知。

For the purposes of discussion I'm glossing over the shared_ptr part of your question, it doesn't actually change things because as you've got shared_ptr working that's actually sufficient to use it in this scenario too, it would just make my examples more verbose needlessly.出于讨论的目的,我掩盖了您问题的shared_ptr部分,它实际上并没有改变事情,因为当您让shared_ptr工作时,实际上也足以在这种情况下使用它,它只会使我的示例更多不必要的冗长。

The solution I'm working towards is actually modelled after the existing shared_ptr support in SWIG.我正在努力的解决方案实际上是在 SWIG 中现有的shared_ptr支持之后建模的。 I've put together a test interface to illustrate how it will be used:我已经整理了一个测试界面来说明它将如何使用:

%module test
%include "std_function.i"

%std_function(Functor, void, int, double);

%{
#include <iostream>
%}

%inline %{
  std::function<void(int,double)> make_functor() {
    return [](int x, double y){
      std::cout << x << ", " << y << "\n";
    };
  }
%}

Basically to use this all you need do is include the file "std_function.i" and then use the macro %std_function which takes arguments as:基本上要使用这个,你需要做的就是包含文件“std_function.i”,然后使用宏%std_function ,它接受参数为:

%std_function(Name, Ret, ...)

You call this once per instantiation of the std::function template you want to wrap, where Name is what you want to call the type in Java, Ret is the return type and then the remaining (variadic) arguments are the inputs to your function.您每次要包装的std::function模板的实例化都调用一次,其中Name是您要在 Java 中调用的类型,Ret 是返回类型,然后剩余的(可变参数)参数是您的函数的输入. So in my test interface above I'm basically looking to wrap std::function<void(int,double)> .所以在我上面的测试界面中,我基本上希望包装std::function<void(int,double)>

Writing a first version of "std_function.i" isn't actually too tricky.编写“std_function.i”的第一个版本实际上并不太棘手。 All you need to get basic working requirements #1 and #2 is:获得基本工作要求 #1 和 #2 所需的一切是:

%{
  #include <functional>
%}

%define %std_function(Name, Ret, ...)
%rename(Name) std::function<Ret(__VA_ARGS__)>;
%rename(call) std::function<Ret(__VA_ARGS__)>::operator();
namespace std {
  struct function<Ret(__VA_ARGS__)> {
    // Copy constructor
    function<Ret(__VA_ARGS__)>(const std::function<Ret(__VA_ARGS__)>&);

    // Call operator
    Ret operator()(__VA_ARGS__) const;
  };
}

%enddef

This includes the C++ header file in the generated wrapper code once and then sets up the macro for usage in interfaces.这包括一次生成的包装器代码中的 C++ 头文件,然后设置宏以在接口中使用。 SWIG's support for C++11 variadic templates isn't actually very helpful for us in this usage scenario, so the macro I wrote basically re-implements the default template expansion functionality using C99 variadic macro arguments (which are much better supported). SWIG 对C++11 可变参数模板的支持在这个使用场景中实际上对我们没有太大帮助,因此我编写的宏基本上使用 C99 可变参数宏参数(更好地支持)重新实现了默认模板扩展功能 Coincidentally this means the SWIG code we're writing will work with 2.x or even some 1.3.x releases.巧合的是,这意味着我们正在编写的 SWIG 代码将适用于 2.x 甚至某些 1.3.x 版本。 (I tested with 2.x). (我用 2.x 测试过)。 Even if/when your version of SWIG does have %template support that works with std::function retaining this macro is still useful for the rest of the glue that makes it actually callable.即使/当您的 SWIG 版本确实具有与std::function一起使用的%template支持时,保留此宏对于使其实际可调用的其余胶水仍然有用。

The manual expansion of the std:function template is limited to just the bits we care about for our usage: the actual operator() and a copy constructor that might come in handy. std:function模板的手动扩展仅限于我们在使用中关心的部分:实际的operator()和可能派上用场的复制构造函数。

The only other thing to be done is renaming operator() to something that matches the target language, eg for Java renaming it to be just a regular function called "call", or if you were targeting Python to __call__ or using tp_slots if required.唯一要做的另一件事是将operator()重命名为与目标语言匹配的内容,例如,对于 Java,将其重命名为一个名为“call”的常规函数​​,或者如果您将 Python 定位为__call__或在需要时使用 tp_slots。

This is now sufficient to let our interface work, to demonstrate it I wrote a little bit of Java:现在这足以让我们的界面工作,为了演示它,我写了一点 Java:

public class run {
    public static void main(String[] argv) {
        System.loadLibrary("test");
        test.make_functor().call(1,2.5);
    }
}

Which I compiled with:我编译的:

swig2.0 -Wall -c++ -java  test.i
g++ -Wall -Wextra -std=c++11 test_wrap.cxx -o libtest.so -I/usr/lib/jvm/default-java/include/ -I/usr/lib/jvm/default-java/include/linux -shared -fPIC
javac run.java
LD_LIBRARY_PATH=. java run

and it worked.它奏效了。


Requirement #4 is pretty easy to cross off the list now at this point.此时,要求 #4 很容易从列表中划掉。 All we need to do is tell SWIG there's another constructor in std::function which accepts compatible function pointers:我们需要做的就是告诉 SWIG 在std::function有另一个接受兼容函数指针的构造函数:

// Conversion constructor from function pointer
function<Ret(__VA_ARGS__)>(Ret(*const)(__VA_ARGS__));

And then we can use this with the %callback mechanism in SWIG, our test interface file becomes:然后我们可以将它与 SWIG 中的%callback机制一起使用,我们的测试接口文件变为:

%module test
%include "std_function.i"

%std_function(Functor, void, int, double);

%{
#include <iostream>
void add_and_print(int a, double b) {
  std::cout << a+b << "\n";
}
%}

%callback("%s_cb");
void add_and_print(int a, double b);
%nocallback;

%inline %{
  std::function<void(int,double)> make_functor() {
    return [](int x, double y){
      std::cout << x << ", " << y << "\n";
    };
  }
%}

and the Java we use to call this is then:我们用来调用它的 Java 是:

public class run {
    public static void main(String[] argv) {
    System.loadLibrary("test");
    test.make_functor().call(1,2.5);
    new Functor(test.add_and_print_cb).call(3,4.5);
    }
}

Which we compile and run identically successfully at this point.我们在这一点上编译和运行完全相同。

(Note that it's normal and desirable to see some Java classes created at this point that start with the name "SWIGTYPE_p_f_..." - these wrap the "pointer to function" types that are used by the pointer to function constructor and callback constants) (请注意,此时创建的一些 Java 类以名称“SWIGTYPE_p_f_...”开头是正常且可取的——这些类包装了“函数指针”类型,这些类型由指向函数构造函数和回调常量的指针使用)


Requirement #3 is where things start to get trickier.要求 #3 是事情开始变得棘手的地方。 Essentially we've hit the same problem as I answered on making SWIG generate an interface in Java previously, except now we're looking to do it within a macro more generically.本质上,我们遇到了与我之前在 Java 中使 SWIG 生成接口时回答的问题相同的问题,但现在我们希望在宏中更通用地执行此操作。

It turns out that in this instance because the interface we want to generate is fairly simple we can use some tricks inside our macro to make SWIG generate it for us.事实证明,在这种情况下,因为我们想要生成的界面相当简单,所以我们可以在宏中使用一些技巧让 SWIG 为我们生成它。

The main thing that we need to do in order to make this work is to setup SWIG directors to provide cross-language polymorphism and allow something written in Java to implement a C++ interface.为了完成这项工作,我们需要做的主要事情是设置 SWIG 控制器以提供跨语言多态性,并允许用 Java 编写的东西实现 C++ 接口。 This is the class generated with the suffix "Impl" in my code.这是在我的代码中使用后缀“Impl”生成的类。

To make things "feel right" to Java developers we want to still use the same type for both C++ and Java implemented std::function objects.为了让 Java 开发人员“感觉正确”,我们希望对 C++ 和 Java 实现的std::function对象仍然使用相同的类型。 Even if std::function::operator() were virtual we still don't want to have SWIG directors use that type directly as it's pretty common to pass std::function by value which would lead to type slicing problems .即使std::function::operator()是虚拟的,我们仍然不希望 SWIG 董事直接使用该类型,因为按值传递std::function很常见,这会导致类型切片问题 So when a Java developer extends our std::function objects and overrides call we need to do some extra work to make it so that C++ which uses that object actually calls the Java implementation given that we can't just use directors to handle this automatically.因此,当 Java 开发人员扩展我们的std::function对象并覆盖call我们需要做一些额外的工作来使使用该对象的 C++ 实际调用 Java 实现,因为我们不能仅使用控制器来自动处理它.

So what we do looks a little bit weird.所以我们所做的看起来有点奇怪。 If you construct a Java object that is meant to implement std::function then there's a special, protected constructor for that.如果您构造了一个旨在实现std::function的 Java 对象,那么就有一个特殊的、受保护的构造函数。 This constructor leaves the swigCPtr member variable, which normally points to a real C++ object as 0 and instead creates an anonymous wrapper object that implements the "Impl" interface and simply proxies everything back to the call member of the Java object.此构造函数将swigCPtr成员变量swigCPtr为 0,该变量通常指向一个真实的 C++ 对象,而是创建一个匿名包装对象,该对象实现“Impl”接口并简单地将所有内容代理回 Java 对象的call成员。

We have another typemap too that gets applied, in Java, everywhere we pass a std::function object to C++.我们还有另一种类型映射,它在 Java 中应用到我们将std::function对象传递给 C++ 的任何地方。 Its role is to detect which case we have - a C++ implemented std::function object, or a Java one.它的作用是检测我们遇到的是哪种情况std::function ++ 实现的std::function对象,还是 Java 对象。 In the C++ case it does nothing special and everything proceeds as normal.在 C++ 的情况下,它没有什么特别的,一切都照常进行。 In the Java case it takes the proxy object and asks C++ to convert it back to another, separate std::function instance which gets substituted instead.在 Java 的情况下,它获取代理对象并要求 C++ 将其转换回另一个单独的std::function实例,该实例被替换。

This is enough to get us the behaviour we want in both languages without anything that feels weird on either side (other than a lot of mechanical lifting that happens transparently).这足以让我们在两种语言中都能获得我们想要的行为,而不会在任何一方都感到奇怪(除了大量透明发生的机械提升)。

The catch here is that it's non-trivial to automatically construct the proxy object.这里的问题是自动构造代理对象并非易事。 Java has dynamic proxy classes as part of the reflection API, but these only implement interfaces, not extend abstract classes. Java 有动态代理类作为反射 API 的一部分,但这些只实现接口,不扩展抽象类。 One possibility I did try to use was void call(Object ...args) on the Java side, which is a variadic function argument.我尝试使用的一种可能性是 Java 端的void call(Object ...args) ,这是一个可变参数函数参数。 Whilst legal this didn't seem to actually override any cases in the super class as would be needed.虽然合法,但这似乎并没有真正覆盖超类中需要的任何情况。

What I ended up doing was adapting some macros to iterate over variadic macro arguments in the way I wanted.我最终做的是调整一些宏以按照我想要的方式迭代可变参数宏参数。 This is a fairly sensible solution given that we already decided to use variadic C99 macro arguments for other reasons.鉴于出于其他原因我们已经决定使用可变参数 C99 宏参数,这是一个相当明智的解决方案。 This mechanism gets used four times in total in my solution, once in the function declaration and once in the delgated call for both Java and C++.这种机制在我的解决方案中总共使用了四次,一次在函数声明中,一次在 Java 和 C++ 的委托调用中。 (C++ has perfect forwarding properties retained and Java needs a typemap lookup to be performed, so they are distinct in each and every case). (C++ 保留了完美的转发属性,Java 需要执行类型映射查找,因此它们在每种情况下都是不同的)。

There's also a custom typemap to simplify some of the Java code - in a void function it's not legal to write return other_void_function();还有一个自定义类型映射来简化一些 Java 代码 - 在 void 函数中编写return other_void_function();是不合法的return other_void_function(); , so we would need to special case void functions if it weren't for that. ,所以如果不是这样,我们将需要特殊情况 void 函数。

So let's see what that looks like in reality.那么让我们看看现实中的样子。 First up is the run.java I used for testing, it's only slightly modified from previous examples to add a Java implementation of the std::function object.首先是我用于测试的 run.java,它只是对前面的示例稍作修改,以添加std::function对象的 Java 实现。

public class run extends Functor {
    public static void main(String[] argv) {
        System.loadLibrary("test");
        test.make_functor().call(1,2.5);

        new Functor(test.add_and_print_cb).call(3,4.5);

        Functor f = new run();
        test.do_things(f);
    }

    @Override
    public void call(int a, double b) {
        System.out.println("Java: " + a + ", " + b);
    }
}

The std_function.i is now substantially larger with all the changes outlined above: std_function.i 现在大大增加了上述所有更改:

%{
  #include <functional>
  #include <iostream>

  #ifndef SWIG_DIRECTORS
  #error "Directors must be enabled in your SWIG module for std_function.i to work correctly"
  #endif
%}

// These are the things we actually use
#define param(num,type) $typemap(jstype,type) arg ## num
#define unpack(num,type) arg##num
#define lvalref(num,type) type&& arg##num
#define forward(num,type) std::forward<type>(arg##num)

// This is the mechanics
#define FE_0(...)
#define FE_1(action,a1) action(0,a1)
#define FE_2(action,a1,a2) action(0,a1), action(1,a2)
#define FE_3(action,a1,a2,a3) action(0,a1), action(1,a2), action(2,a3)
#define FE_4(action,a1,a2,a3,a4) action(0,a1), action(1,a2), action(2,a3), action(3,a4)
#define FE_5(action,a1,a2,a3,a4,a5) action(0,a1), action(1,a2), action(2,a3), action(3,a4), action(4,a5)

#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME
%define FOR_EACH(action,...) 
  GET_MACRO(__VA_ARGS__, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0)(action,__VA_ARGS__)
%enddef

%define %std_function(Name, Ret, ...)

%feature("director") Name##Impl;
%typemap(javaclassmodifiers) Name##Impl "abstract class";

%{
  struct Name##Impl {
    virtual ~Name##Impl() {}
    virtual Ret call(__VA_ARGS__) = 0;
  };
%}

%javamethodmodifiers Name##Impl::call "abstract protected";
%typemap(javaout) Ret Name##Impl::call ";" // Suppress the body of the abstract method

struct Name##Impl {
  virtual ~Name##Impl();
protected:
  virtual Ret call(__VA_ARGS__) = 0;
};

%typemap(maybereturn) SWIGTYPE "return ";
%typemap(maybereturn) void "";

%typemap(javain) std::function<Ret(__VA_ARGS__)> "$javaclassname.getCPtr($javaclassname.makeNative($javainput))"
%typemap(javacode) std::function<Ret(__VA_ARGS__)> %{
  protected Name() {
    wrapper = new Name##Impl(){
      public $typemap(jstype, Ret) call(FOR_EACH(param, __VA_ARGS__)) {
    $typemap(maybereturn, Ret)Name.this.call(FOR_EACH(unpack, __VA_ARGS__));
      }
    };
    proxy = new $javaclassname(wrapper);
  }

  static $javaclassname makeNative($javaclassname in) {
    if (null == in.wrapper) return in;
    return in.proxy;
  }

  // Bot of these are retained to prevent garbage collection from happenign to early
  private Name##Impl wrapper;
  private $javaclassname proxy;
%}

%rename(Name) std::function<Ret(__VA_ARGS__)>;
%rename(call) std::function<Ret(__VA_ARGS__)>::operator();

namespace std {
  struct function<Ret(__VA_ARGS__)> {
    // Copy constructor
    function<Ret(__VA_ARGS__)>(const std::function<Ret(__VA_ARGS__)>&);

    // Call operator
    Ret operator()(__VA_ARGS__) const;

    // Conversion constructor from function pointer
    function<Ret(__VA_ARGS__)>(Ret(*const)(__VA_ARGS__));

    %extend {
      function<Ret(__VA_ARGS__)>(Name##Impl *in) {
    return new std::function<Ret(__VA_ARGS__)>([=](FOR_EACH(lvalref,__VA_ARGS__)){
          return in->call(FOR_EACH(forward,__VA_ARGS__));
    });
      }
    }
  };
}

%enddef

And test.i is slightly expanded to validate the Java -> C++ passing of std::function objects and enable directors:并且 test.i 稍微扩展以验证std::function对象的 Java -> C++ 传递并启用导向器:

%module(directors="1") test
%include "std_function.i"

%std_function(Functor, void, int, double);

%{
#include <iostream>
void add_and_print(int a, double b) {
  std::cout << a+b << "\n";
}
%}

%callback("%s_cb");
void add_and_print(int a, double b);
%nocallback;

%inline %{
  std::function<void(int,double)> make_functor() {
    return [](int x, double y){
      std::cout << x << ", " << y << "\n";
    };
  }

  void do_things(std::function<void(int,double)> in) {
    in(-1,666.6);
  }
%}

This compiled and ran as with the previous examples.这与前面的示例一样编译和运行。 It's worth noting that we've crossed into writing a lot of Java specific code - although the design would work for other languages if you were targeting Python it's a lot simpler to fix some of these issues using Python specific features.值得注意的是,我们已经编写了许多特定于 Java 的代码——尽管该设计适用于其他语言,如果您的目标是 Python,使用 Python 特定功能修复其中一些问题要简单得多。

There are two things I'd like to improve:我想改进的有两点:

  1. Use C++14 variadic lambdas to avoid the macro preprocessor magic I've used to keep them compatible with C++11.使用 C++14 可变参数 lambda 来避免我用来保持它们与 C++11 兼容的宏预处理器魔法。 If you have C++ 14 the %extend constructor becomes:如果你有 C++ 14 %extend构造函数变成:

     %extend { function<Ret(__VA_ARGS__)>(Name##Impl *in) { return new std::function<Ret(__VA_ARGS__)>([=](auto&& ...param){ return in->call(std::forward<decltype(param)>(param)...); }); } }

When it comes to using this macro with std::shared_ptr as expected the macro itself needs no changes.当按预期将此宏与std::shared_ptr一起使用时,宏本身不需要更改。 There is however a problem with the implementation of the javadirectorin and directorin typemaps that get applied, which do prevent things from "just working".然而,应用的javadirectorin 和directorin 类型映射的实现存在问题,这确实阻止了事情“正常工作”。 This is true even with a build of SWIG from "trunk".即使从“trunk”构建 SWIG,也是如此。 (There's an outstanding question on combining directors and shared_ptr ) (关于结合董事和 shared_ptr有一个悬而未决的问题)

We can work around that though, by adding two additional typemaps in the main .i file of our module right after the call to %shared_ptr :不过,我们可以通过在调用%shared_ptr之后立即在模块的主 .i 文件中添加两个额外的类型映射来解决这个问题:

%shared_ptr(some::ns::TheThing);
%typemap(javadirectorin) std::shared_ptr<some::ns::TheThing> "new $typemap(jstype, some::ns::TheThing)($1,false)";
%typemap(directorin,descriptor="L$typemap(jstype, some::ns::TheThing);") std::shared_ptr<some::ns::TheThing> %{ 
  *($&1_type*)&j$1 = &$1;
%}

The first of these two typemaps is actually dead code because we forced the "call" method to be abstract in our abstract class, but it's easier to fix the compilation of this method than it is to suppress it.这两个类型映射中的第一个实际上是死代码,因为我们在抽象类中强制“调用”方法是抽象的,但是修复此方法的编译比抑制它更容易。 The second typemap is important.第二个类型图很重要。 It's substantially similar to the normal "out" typemap in that it creates a jlong which is really just a representation of a C++ pointer, ie it prepares an object to go from C++, to Java.它与普通的“out”类型jlong基本相似,因为它创建了一个jlong ,它实际上只是一个 C++ 指针的表示,即它准备了一个从 C++ 到 Java 的对象。

Note that you might need to modify the descriptor attribute of the directorin typemap if you use packages in your module, either to "L$packagepath/$typemap(...);"请注意,如果您在模块中使用包,您可能需要修改 directorin 类型映射的描述符属性,或者修改为"L$packagepath/$typemap(...);" or simply write it by hand.或者简单地手写。

This should remove the spurious "SWIGTYPE_p_sstd__shared_ptr..." type generated now as well.这也应该删除现在生成的虚假“SWIGTYPE_p_sstd__shared_ptr...”类型。 If you have virtual functions that return shared_ptr objects you'll need to write directorout and javadirectorout typemaps for them too.如果您有返回 shared_ptr 对象的虚函数,您还需要为它们编写directorout 和javadirectorout 类型映射。 These can be base on the normal "in" typemap.这些可以基于正常的“in”类型映射。

This was sufficient for my own simple testing with a modified Functor to work, at least with my version of SWIG checked out from the trunk today.这足以让我自己使用修改过的Functor进行简单的测试,至少在我今天从后备箱检出的 SWIG 版本的情况下。 (My test with 2.0.x failed and I didn't put much effort into making it work since this is a known work in progress area). (我对 2.0.x 的测试失败了,我没有花太多精力让它工作,因为这是一个已知的正在进行的工作区域)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM