mardi 3 juillet 2018

detect API version through sfinae

I work on a code base where the C++ API is about to change (master branch and feature branch) and I want to protect my code against the change, such that my code builds against both versions. As minimum working example godbolt/compiler-explorer link:

common:

class SmallWidget {
 private:
  int m_i;

 public:
  int i() { return m_i; }
};

master:

class BigWidget {
 private:
  std::vector<SmallWidget*> m_v;

 public:
  auto& widgets() { return m_v; }
};

feature branch:

class BigWidget {
 private:
  std::vector<SmallWidget> m_v;

 public:
  auto& widgets() { return m_v; }
};
/**/

For the master version I have some call code:

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;
}

The API does not come with a preprocessor variable to select with

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

I was trying to detect the return type of widgets with sfinae:

// 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(); }

But the compilers are not happy about that. clang says:

<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

template

Is there a way to use sfinae to enable the right code here?

I also tried using std::enable_if for the return code or function arguments, but my understanding is that the compiler always sees the signature int single_op(BigWidget&) for both versions.

Aucun commentaire:

Enregistrer un commentaire