通过 sfinae 检测 API 版本
detect API version through sfinae
我在 C++ API 即将更改的代码库上工作(主分支和功能分支),我想保护我的代码免受更改,以便我的代码针对两个版本构建。作为最小工作示例 godbolt/compiler-explorer link:
常见:
class SmallWidget {
private:
int m_i;
public:
int i() { return m_i; }
};
师傅:
class BigWidget {
private:
std::vector<SmallWidget*> m_v;
public:
auto& widgets() { return m_v; }
};
功能分支:
class BigWidget {
private:
std::vector<SmallWidget> m_v;
public:
auto& widgets() { return m_v; }
};
/**/
大师版我有一些调用代码:
int single_op(BigWidget& w) {
return (*w.widgets().begin())->i(); // asterisk will need to go
}
std::vector<int> do_stuff(std::vector<BigWidget> inputs) {
std::vector<int> retval;
for (auto w : inputs) {
retval.push_back(single_op(w));
}
return retval;
}
API 没有预处理器变量 select 和
#if WIDGET_VERSION == OLD
return (*w.widgets().begin())->i(); // asterisk will need to go
#else
return (w.widgets().begin())->i(); // asterisk gone
#endif
我试图用 sfinae 检测 return 类型的 widgets
:
// version for the master branch API
template <typename = typename std::enable_if<std::is_same<
std::remove_const<std::remove_reference<decltype(**(
std::declval<BigWidget>().widgets().begin()))>::type>::type,
SmallWidget>::value>::type>
int single_op(BigWidget& w) {
return (*w.widgets().begin())->i();
}
// version for the feature branch API
template <typename = typename std::enable_if<std::is_same<
std::remove_const<std::remove_reference<decltype(*(
std::declval<BigWidget>().widgets().begin()))>::type>::type,
SmallWidget>::value>::type>
int single_op(BigWidget& w) { return w.widgets().begin()->i(); }
但是编译器对此并不满意。铿锵声说:
<source>:43:46: error: failed requirement 'std::is_same<std::remove_const<std::remove_reference<decltype(* (std::declval<BigWidget>().widgets().begin()))>::type>::type, SmallWidget>::value'; 'enable_if' cannot be used to disable this declaration
模板
有没有办法在此处使用 sfinae 启用正确的代码?
我也尝试对 return 代码或函数参数使用 std::enable_if
,但我的理解是编译器总是看到两个版本的签名 int single_op(BigWidget&)
。
我可以想出 this 版本:
// version for the feature branch API
template <typename bigtype>
int single_op(
typename std::enable_if<
std::is_same<
std::remove_const<std::remove_reference<decltype(
*(std::declval<BigWidget>().widgets().begin()))>::type>::type,
SmallWidget>::value,
bigtype>::type& w) {
return w.widgets().begin()->i();
}
我为 BigWidget 类型添加了一个额外的模板参数(尽管已知)并将其用于 single_op
的函数参数。现在编译器可以遍历两个模板定义,为重载集提供一种可能的解决方案并禁用未使用的代码。
作为缺点,我现在必须在调用端拼出模板参数
retval.push_back(single_op<decltype(w)>(w));
但我怀疑有更好的解决方案。
创建一个小的辅助函数,它将接受任何一种类型和 return 您想要的类型。
SmallWidget &getWidget(SmallWidget *w) { return *w; }
SmallWidget &getWidget(SmallWidget &w) { return w; }
int single_op(BigWidget &w) {
return getWidget(*w.widgets().begin()).i();
}
您还可以为 const
版本提供重载。
我在 C++ API 即将更改的代码库上工作(主分支和功能分支),我想保护我的代码免受更改,以便我的代码针对两个版本构建。作为最小工作示例 godbolt/compiler-explorer link:
常见:
class SmallWidget {
private:
int m_i;
public:
int i() { return m_i; }
};
师傅:
class BigWidget {
private:
std::vector<SmallWidget*> m_v;
public:
auto& widgets() { return m_v; }
};
功能分支:
class BigWidget {
private:
std::vector<SmallWidget> m_v;
public:
auto& widgets() { return m_v; }
};
/**/
大师版我有一些调用代码:
int single_op(BigWidget& w) {
return (*w.widgets().begin())->i(); // asterisk will need to go
}
std::vector<int> do_stuff(std::vector<BigWidget> inputs) {
std::vector<int> retval;
for (auto w : inputs) {
retval.push_back(single_op(w));
}
return retval;
}
API 没有预处理器变量 select 和
#if WIDGET_VERSION == OLD
return (*w.widgets().begin())->i(); // asterisk will need to go
#else
return (w.widgets().begin())->i(); // asterisk gone
#endif
我试图用 sfinae 检测 return 类型的 widgets
:
// version for the master branch API
template <typename = typename std::enable_if<std::is_same<
std::remove_const<std::remove_reference<decltype(**(
std::declval<BigWidget>().widgets().begin()))>::type>::type,
SmallWidget>::value>::type>
int single_op(BigWidget& w) {
return (*w.widgets().begin())->i();
}
// version for the feature branch API
template <typename = typename std::enable_if<std::is_same<
std::remove_const<std::remove_reference<decltype(*(
std::declval<BigWidget>().widgets().begin()))>::type>::type,
SmallWidget>::value>::type>
int single_op(BigWidget& w) { return w.widgets().begin()->i(); }
但是编译器对此并不满意。铿锵声说:
<source>:43:46: error: failed requirement 'std::is_same<std::remove_const<std::remove_reference<decltype(* (std::declval<BigWidget>().widgets().begin()))>::type>::type, SmallWidget>::value'; 'enable_if' cannot be used to disable this declaration
模板
有没有办法在此处使用 sfinae 启用正确的代码?
我也尝试对 return 代码或函数参数使用 std::enable_if
,但我的理解是编译器总是看到两个版本的签名 int single_op(BigWidget&)
。
我可以想出 this 版本:
// version for the feature branch API
template <typename bigtype>
int single_op(
typename std::enable_if<
std::is_same<
std::remove_const<std::remove_reference<decltype(
*(std::declval<BigWidget>().widgets().begin()))>::type>::type,
SmallWidget>::value,
bigtype>::type& w) {
return w.widgets().begin()->i();
}
我为 BigWidget 类型添加了一个额外的模板参数(尽管已知)并将其用于 single_op
的函数参数。现在编译器可以遍历两个模板定义,为重载集提供一种可能的解决方案并禁用未使用的代码。
作为缺点,我现在必须在调用端拼出模板参数
retval.push_back(single_op<decltype(w)>(w));
但我怀疑有更好的解决方案。
创建一个小的辅助函数,它将接受任何一种类型和 return 您想要的类型。
SmallWidget &getWidget(SmallWidget *w) { return *w; }
SmallWidget &getWidget(SmallWidget &w) { return w; }
int single_op(BigWidget &w) {
return getWidget(*w.widgets().begin()).i();
}
您还可以为 const
版本提供重载。