Tutorial :Is there any way to specify a generic argument to a function with the restriction that it should implement a given interface



Question:

Example:

I have a function that works with vectors:

double interpolate2d(const vector<double> & xvals, const vector<double> & yvals, double xv, double yv, const vector<vector<double> > &fvals) {      int xhi, xlo, yhi, ylo;      double xphi, yphi;      bracketval(xvals,xv,xhi,xlo,xphi);      bracketval(yvals,yv,yhi,ylo,yphi);      return (fvals[xhi][yhi]*xphi+fvals[xlo][yhi]*(1.-xphi))*yphi + (fvals[xhi][ylo]*xphi+fvals[xlo][ylo]*(1.-xphi))*(1.-yphi);  }  

But now I want to call it with boost::array elements for the first 2 arguments (same with bracketval()), if std::vector and boost::array were self-implemented, I would be able to derive both from a common base class (interface-like) enforcing an implementation of operator[], since both are library-provided, is there any way to cast/specify such a restriction?

I can always resort to plain c-arrays, but it's not very neat.

Edit: FWIW, here is the original bracketval implementation:

void bracketval(const vector<double> &vals, double v, int &hi, int &lo, double &prophi){      hi=vals.size()-1;      lo=0;      while(abs(hi-lo)>1) {          int md = (hi+lo)/2;          if(vals[md]>v) hi=md; else lo=md;      }      if(vals[hi]!=vals[lo])          prophi = (v-vals[lo])/(vals[hi]-vals[lo]);      else          prophi = 0.5;    }  


Solution:1

This works with std::vector, boost::array, built-in arrays an generally with anything that is indexable. I've also included a suggestion of how you should implement the bracketval function:

template<class Vec>  void bracketval(Vec const & xvals, double xv, int xhi, int xlo, double xphi)  {  }    template <class Vec, class VecOfVecs>  double interpolate2d(Vec const & xvals, Vec const & yvals,                        double xv, double yv,                       VecOfVecs const & fvals)  {      int xhi, xlo, yhi, ylo;      double xphi, yphi;      bracketval(xvals,xv,xhi,xlo,xphi);      bracketval(yvals,yv,yhi,ylo,yphi);      return (fvals[xhi][yhi]*xphi+fvals[xlo][yhi]*(1.-xphi))               *yphi + (fvals[xhi][ylo]*xphi+fvals[xlo][ylo]               *(1.-xphi))*(1.-yphi);  }    int main()  {      {          std::vector<double> v, w;          std::vector<std::vector<double> > vv;          interpolate2d(v, w, 1., 2., vv);      }      {          boost::array<double, 4> v, w;          boost::array<boost::array<double, 4>, 4> vv;          interpolate2d(v, w, 1., 2., vv);      }      {          double v[4], w[4];          double vv[4][4];          interpolate2d(v, w, 1., 2., vv);      }      }  

You can even add an extra template parameter if you envision the possibility that the second vector could be of a different type that the first (e.g., the first a vector and the second a boost::array):

template <class VecX, class VecY, class VecOfVecs>  double interpolate2d(VecX const & xvals, VecY const & yvals,                        double xv, double yv,                       VecOfVecs const & fvals)  


Solution:2

While this is probably overkill for your specific problem, in general you can check wether template parameters implement a certain interface by explicitly checking wether certain members are provided and some expressions are valid.
Boosts concept check library gives you a clean way to do that, but the container checking classes it provides don't help you here because Boost.Array only supports a certain subset of the sequence requirements.

What you could do though if you need a clean way to put restrictions is modelling the requirements yourself, either utilizing Boosts utilities or a similar custom approach.


Solution:3

There is no way to put type restrictions on template parameters. What you could is, is define your own interface and create adaptors for all types you want to support.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »