通过sfinae检测API版本

时间:2018-07-03 14:01:12

标签: c++ c++11 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没有附带要选择的预处理器变量

#if WIDGET_VERSION == OLD
  return (*w.widgets().begin())->i(); // asterisk will need to go
#else
  return (w.widgets().begin())->i(); // asterisk gone
#endif

我正尝试使用sfinae检测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在此处启用正确的代码?

我还尝试使用std::enable_if作为返回码或函数参数,但我的理解是,对于这两个版本,编译器始终会看到签名int single_op(BigWidget&)

2 个答案:

答案 0 :(得分:0)

我可以提出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));

但是我怀疑那里有更好的解决方案。

答案 1 :(得分:0)

创建一个小辅助函数,将使用两种类型的任意一种并返回所需的类型。

SmallWidget &getWidget(SmallWidget *w) { return *w; }
SmallWidget &getWidget(SmallWidget &w) { return w; }

int single_op(BigWidget &w) {
    return getWidget(*w.widgets().begin()).i();
}

您还可以为const版本提供重载。