很抱歉这个长标题,这是我想要实现的:我有一个带有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
。例如使用AttributeError: 'NonConstHandle' object has no attribute 'set'
进行以下Python测试失败:
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没有发出任何错误/警告。 。
任何帮助赞赏!问候
这里的问题是,每次在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现在点击“这不应该打印”,因为我们已经生成了一个(完全合法的)set()
函数,即使在const情况下通过明确拼写模板参数而不是让他们推断出来。
所以我们生成了代码来调用:
Handle<true>::set<false, void>(int);
哪个呃在这个例子中起作用,因为它不能以直观的方式编译。
我不知道有什么方法可以在这里进行演绎(这是一种耻辱,因为它们是默认的,所以它应该是正确的吗? - 也许是一个补丁进入SWIG主干,尽管同时进行默认和SFINAE会很棘手)
幸运的是,有一个简单的解决方法,使用%ignore
删除我们不想要的版本:
%模块测试
%{
#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
。