抱歉,长标题是我要实现的目标:我有一个带有bool模板参数的小型C ++类,当设置为true时,它将使用std::enable_if
禁用其setter方法。这是一个简化的示例:
template< bool IS_CONST >
class Handle
{
public:
Handle(void) : m_value(0) { }
Handle(int value) : m_value(value) { }
template < bool T = IS_CONST, typename COMPILED = typename std::enable_if< T == false >::type >
void set(int value)
{
m_value = value;
}
int get(void) const
{
return m_value;
}
private:
int m_value;
};
此代码按预期编译了作品:Handle< true >
没有set
方法,Handle< false >
有。
现在我正在尝试使用SWIG将其绑定到Python。我正在使用以下文件来生成绑定:
%module core
%include "Handle.h"
%template(NonConstHandle) Handle< false >;
%template(ConstHandle) Handle< false >;
%{
#include "Test.h"
%}
SWIG生成模块时不会抱怨,它可以很好地编译,但是set
方法从未绑定,即使在专门的NonConstHandle
中也是如此。例如以下Python测试失败,并显示AttributeError: 'NonConstHandle' object has no attribute 'set'
:
import core
handle = core.NonConstHandle()
assert(handle.get() == 0)
handle.set(1)
assert(handle.get() == 1)
const_handle = core.ConstHandle()
assert(const_handle .get() == 0)
try:
const_handle .set(1)
print("this should not print")
except:
pass
print("all good")
在搜索该主题时,我发现了很多与enable_if和SWIG相关的内容,这使我认为它受支持,但我不知道为什么没有生成set
,尽管没有错误。 / SWIG发出的警告...
任何帮助表示赞赏! 问候
答案 0 :(得分:2)
这里的问题是,每次在C ++中创建模板时,您都需要在SWIG接口中至少有一个%template
指令,以使其对生成的包装程序有影响。
当人们模糊地暗示std::enable_if
有效时,通常意味着两件事。首先,它可以解析,其次,%template
为他们工作。这两件事在这里都是真的。
由于您已经在模板类中使用了SFINAE和模板函数,因此每个模板都需要一个%template
。否则,如您所见,set
成员将被完全忽略。绕开问题的SFINAE / enable_if位,example of template functions inside template classes是一个很好的起点。
因此,我们可以将您的.i文件更改为如下所示:
%module test
%{
#include "test.h"
%}
%include "test.h"
// Be explicit about what 'versions' of set to instantiate in our wrapper
%template(set) Handle::set<false, void>;
%template(NonConstHandle) Handle<false>;
%template(ConstHandle) Handle<true>;
问题在于,(已修复了一些小错误)您的测试python现在会命中“这不应该打印”,因为即使在const情况下,我们也已经生成了(完全合法的)set()
函数通过显式地拼写模板参数而不是推导它们。
因此,我们生成了要调用的代码:
Handle<true>::set<false, void>(int);
在这种情况下可以使用它,因为它不能以直观的方式进行编译。
我不知道在这种情况下进行扣除的方法(这很可惜,因为默认情况下,所以应该是对的吗?-也许是一个SWIG补丁程序之一主干,尽管同时进行默认设置和SFINAE都会很棘手)
幸运的是,有一个使用%ignore
删除我们也不想要的版本的简单解决方法:
%module测试
%{
#include "test.h"
%}
%include "test.h"
%template(set) Handle::set<false, void>;
%ignore Handle<true>::set;
%template(NonConstHandle) Handle<false>;
%template(ConstHandle) Handle<true>;
然后会生成您期望的代码。
值得注意的是,在生成包装器时,通常通常更容易明确地说明复杂模板代码的工作方式-您通常需要额外的帮助程序或调整接口,以使其以希望的方式在Python中工作。因此,您还可以通过执行以下操作来解决您的示例:
%module test
%{
#include "test.h"
%}
template <bool>
class Handle {
public:
Handle(void);
Handle(int value);
int get(void) const;
};
template<>
class Handle<false>
{
public:
Handle(void);
Handle(int value);
void set(int value);
int get(void) const;
};
%template(NonConstHandle) Handle<false>;
%template(ConstHandle) Handle<true>;
或类似的技巧:
%module test
%{
#include "test.h"
typedef Handle<true> ConstHandle;
typedef Handle<false> NonConstHandle;
%}
struct ConstHandle {
ConstHandle(void);
ConstHandle(int value);
int get(void) const;
};
struct NonConstHandle
{
NonConstHandle(void);
NonConstHandle(int value);
void set(int value);
int get(void) const;
};
尽管请注意,在后一种情况下,如果要将模板用作函数内/函数外的参数,则也需要使用%apply
。