Шаблон C ++: Как динамически выбирать между классами и примитивными типами - PullRequest
1 голос
/ 07 октября 2011

Можно ли реализовать идею, показанную ниже, с помощью шаблонов:

// please excuse any syntax errors.

template<typename KEY, typename VALUE>
class Container {
public: 
    VALUE calculate(vector<KEY> searchFors)
    {
        KEY searchFor = searchFors[0];
        pair<KEY,VALUE> lower = getLower(searchFor);
        pair<KEY,VALUE> upper = getUpper(searchFor);
        // calculateImpl uses + - * /
        VALUE value = calculateImpl( 
            lower.first, lower.second(searchFor), 
            upper.first, upper.second(searchFor) );   
        return value;
    }
    // an example of calculateImpl
    VALUE calculateImpl( KEY key1, VALUE value1, KEY key2, VALUE value2 )
    {
         return value1 * value2;
    }
    // an example of getLower getUpper, assuming there're more than 2 elements in _data
    pair<KEY,VALUE> getLower(KEY key)
    {
         return *(_data.begin());
    }        
    pair<KEY,VALUE> getUpper(KEY key)
    {
         return *(_data.begin()+1);
    }        

private:
    vector<pair<KEY,VALUE>> _data;
};

Обратите внимание, что VALUE требует реализации operator ().Как я могу сделать так, чтобы он мог выбирать между примитивами или функторами?Например, если searchFors.size () == 0, использовать VALUE в качестве примитивов, в противном случае использовать VALUE в качестве функторов?

Другими словами, VALUE может быть примитивом (тип VALUE) или функтором.(тип VALUE (*) (KEY)) в зависимости от переключателя, который зависит от размера вектора.

Использование такого Контейнера будет

vector<double> keys;
keys.push_back(2);  
keys.push_back(3);

// usage as primitive 
// Note Container<double,double> where VALUE=double
Container<double,Container<double,double>> c1;
double result1 = c1.calculate(keys);

Container<double,Container<double,Array<double>>> c2;
Array<double> result2 = c1.calculate(keys);

Я хочу иметь возможность вкладывать Контейнеры, рекурсивно вызывать вычисления, останавливается, когда VALUE больше не является Контейнером.

РЕДАКТИРОВАТЬ: Добавлен код

template<typename KEY, typename VALUE>
class Container {
public: 
Container(vector<KEY> x, vector<VALUE> y)
    :_x(x),
    _y(y) {}

    VALUE calculate(vector<KEY> searchFors)
{
    if( searchFors.size() == 0 )
    {
        throw exception("no search keys");
    }
    KEY key = searchFors[0];

    if( key >= *(_x.end()-1) )
    {
        return *(_y.end()-1);
    }
    if( key <= *(_x.begin()) )
    {
        return *(_y.begin());
    }
    vector<KEY>::const_iterator iSearchKey;
    iSearchKey = upper_bound( _x.begin(), _x.end(), key );
    size_t pos = iSearchKey - _x.begin();
    return (_y[pos]-_y[pos-1])/(_x[pos]-_x[pos-1]) * (key-_x[pos-1]) + _y[pos-1];
}
private:
vector<KEY> _x;
vector<VALUE> _y;
};


template<typename KEY, typename VALUE>
class Container<KEY, Container<KEY, VALUE> > {
public: 
Container(vector<KEY> x, vector<Container<KEY, VALUE> > y)
    :_x(x),
    _y(y) {}

VALUE calculate(vector<KEY> searchFors)
{
    if( searchFors.size() == 0 )
    {
        throw exception("no search keys");
    }
    KEY key = searchFors[0];
    vector<KEY> remainingKeys( searchFors.begin()+1, searchFors.end() );

    if( key >= *(_x.end()-1) )
    {
        return (_y.end()-1)->calculate(remainingKeys);
    }
    if( key <= *(_x.begin()) )
    {
        return _y.begin()->calculate(remainingKeys);
    }

    vector<KEY>::const_iterator iSearchKey;
    iSearchKey = upper_bound( _x.begin(), _x.end(), key );
    size_t pos = iSearchKey - _x.begin();

    VALUE upperY = _y[pos].calculate(remainingKeys);
    VALUE lowerY = _y[pos-1].calculate(remainingKeys);

    return (upperY-lowerY)/(_x[pos]-_x[pos-1]) * (key-_x[pos-1]) + lowerY;
}
private:
vector<KEY> _x;
vector<Container<KEY, VALUE> > _y;
};

void main()
{
using namespace boost::assign;

vector<double> y;
vector<double> z;

y += 1,2,3,4;
z += 1,4,9,16;
Container<double,double> yz1(y,z);

z.clear();
z += 1,8,27,64;
Container<double,double> yz2(y,z);

vector<double> x;   
x += 1,4;
vector<Container<double,double> > ys;
ys += yz1,yz2;

Container<double,Container<double,double> > xy(x,ys);

    vector<double> keys;
keys += 2.5,3.5;
double value = xy.calculate(keys);

    // prints 29
cout << value << endl;

}

РЕДАКТИРОВАТЬ: более эффективная версия с хранением итераторов вместо данных

template<typename KEY, typename VALUE>
class Container;

template<typename KEY, typename VALUE>
class Container_helper{
public:
typedef typename std::vector<KEY>::const_iterator key_iterator_type;
typedef VALUE value_type;
typedef typename vector<VALUE>::const_iterator value_iterator_type;

static value_type getValue(const value_iterator_type& iValue, 
    const key_iterator_type&, const key_iterator_type&){
          return *iValue;
     }
};

template<typename KEY, typename VALUE>
class Container_helper<KEY, Container<KEY, VALUE> >{
public:
typedef typename std::vector<KEY>::const_iterator key_iterator_type;
     typedef typename Container_helper<KEY, VALUE>::value_type value_type;
typedef typename std::vector<Container<KEY, VALUE> >::const_iterator value_iterator_type;

static value_type getValue(const value_iterator_type& iValue, 
    const key_iterator_type& xBegin, const key_iterator_type& xEnd)
{
           return (*iValue)(xBegin,xEnd);
     }
};

template<typename KEY, typename VALUE>
class Container {

typedef typename std::vector<KEY>::const_iterator key_iterator_type;
typedef typename std::vector<VALUE>::const_iterator value_iterator_type;
typedef typename Container_helper<KEY, VALUE>::value_type value_type;

public:   
Container(const key_iterator_type& xBegin, 
          const key_iterator_type& xEnd,        
          const value_iterator_type& yBegin, 
          const value_iterator_type& yEnd)
    :_xBegin(xBegin),_xEnd(xEnd),_yBegin(yBegin),_yEnd(yEnd) {}
Container(const Container& source)
    :_xBegin(source._xBegin),_xEnd(source._xEnd),
     _yBegin(source._yBegin),_yEnd(source._yEnd) {}
     Container& operator=(const Container& source)
{
    _xBegin = source._xBegin;
    _xEnd = source._xEnd;
    _yBegin = source._yBegin;
    _yEnd = source._yEnd; 
    return *this;
}


public:
     value_type 
operator()( const key_iterator_type& searchBegin, const key_iterator_type& searchEnd ) const
{
    if( searchBegin == searchEnd )
    {
        throw exception("no search keys");
    }
    KEY key = *searchBegin;
    key_iterator_type searchNext = searchBegin + 1;

              if( key >= *(_xEnd-1) )
              {
                  return Container_helper<KEY,VALUE>::getValue(_yEnd-1, searchNext, searchEnd);
              }
              if( key <= *_xBegin )
              {
                  return Container_helper<KEY,VALUE>::getValue(_yBegin, searchNext, searchEnd);
              }

              key_iterator_type iSearchKey = upper_bound( _xBegin, _xEnd, key );
              size_t pos = iSearchKey - _xBegin;

    KEY lowerX = *(_xBegin+pos-1);
    KEY upperX = *(_xBegin+pos);

              value_type upperY = Container_helper<KEY,VALUE>::
        getValue(_yBegin+pos, searchNext, searchEnd);
              value_type lowerY = Container_helper<KEY,VALUE>::
        getValue(_yBegin+pos-1, searchNext, searchEnd);

              return (upperY-lowerY)/(upperX-lowerX) * (key-lowerX) + lowerY;
         }

  private:
     key_iterator_type _xBegin; 
     key_iterator_type _xEnd;       
     value_iterator_type _yBegin; 
     value_iterator_type _yEnd; 
  };

Ответы [ 2 ]

0 голосов
/ 07 октября 2011

Если я вас правильно понял, я думаю, что это может быть полезно для вас

template<typename KEY, typename VALUE>
class Container {
public: 
    VALUE calculate(vector<KEY> searchFors);
    .
    .
    .
private:
    vector<pair<KEY,VALUE> > _data;

};

template<typename KEY, typename VALUE>
VALUE Container<KEY, VALUE>::calculate(vector<KEY> searchFors)
{
     // operate on non-container (simple) values.
}

template<typename KEY, typename VALUE>
class Container<KEY, Container<KEY, VALUE> > {
public: 
    VALUE calculate(vector<KEY> searchFors);
    .
    .
    .
private:
    vector<pair<KEY, Container<KEY, VALUE> > > _data;
};

template<typename KEY, typename VALUE>
VALUE Container<KEY, Container<KEY, VALUE> >::calculate(vector<KEY> searchFors)
{
     // operate on container values
}

Эти функции должны красиво инкапсулировать базовый тип и контейнер.Единственная проблема заключается в том, что он будет работать только для контейнерного класса - если у вас есть более одного типа контейнера, вы можете использовать некоторые типы черт.

РЕДАКТИРОВАТЬ:

После просмотра вашего точного примераЯ вижу, что это возможно при разделении функциональности на вспомогательный класс, но он уничтожает пространство имен дополнительным классом.Если не возражаете, тогда код здесь:

#include <iostream>
#include <vector>

#include <boost/assign.hpp> 

using namespace std;

template<typename KEY, typename VALUE>
class Container;

template<typename KEY, typename VALUE>
class Container_helper{
public:
    typedef VALUE value_type;

    static value_type calculate(VALUE& val, vector<KEY> /*remainingKeys*/){
        return val;
    }
};

template<typename KEY, typename VALUE>
class Container_helper<KEY, Container<KEY, VALUE> >{
public:
    typedef typename Container_helper<KEY, VALUE>::value_type value_type;

    static value_type calculate(Container<KEY, VALUE>& val, vector<KEY> remainingKeys){
        return val.calculate(remainingKeys);
    }
};

template<typename KEY, typename VALUE>
class Container {

public: 

Container(vector<KEY> x, vector<VALUE> y)
    :_x(x),
    _y(y) {}

public:

    typename Container_helper<KEY, VALUE>::value_type calculate(vector<KEY> searchFors){
          if( searchFors.size() == 0 )
        {
            throw exception(/*"no search keys"*/);
        }
        KEY key = searchFors[0];
        vector<KEY> remainingKeys( searchFors.begin()+1, searchFors.end() );

        if( key >= *(_x.end()-1) )
        {
            return Container_helper<KEY, VALUE>::calculate(*(_y.end()-1), remainingKeys);
        }
        if( key <= *(_x.begin()) )
        {
            return Container_helper<KEY, VALUE>::calculate((*_y.begin()), remainingKeys);
        }

        typename vector<KEY>::const_iterator iSearchKey;
        iSearchKey = upper_bound( _x.begin(), _x.end(), key );
        size_t pos = iSearchKey - _x.begin();

        typename Container_helper<KEY, VALUE>::value_type upperY = Container_helper<KEY, VALUE>::calculate(_y[pos], remainingKeys);
        typename Container_helper<KEY, VALUE>::value_type lowerY = Container_helper<KEY, VALUE>::calculate(_y[pos-1], remainingKeys);

        return (upperY-lowerY)/(_x[pos]-_x[pos-1]) * (key-_x[pos-1]) + lowerY;
    }

private:
vector<KEY> _x;
vector<VALUE> _y;
};


int main(int argc, char* argv[])
{
using namespace boost::assign;

vector<double> y;
vector<double> z;

y += 1,2,3,4;
z += 1,4,9,16;
Container<double,double> yz1(y,z);

z.clear();
z += 1,8,27,64;
Container<double,double> yz2(y,z);

vector<double> x;   
x += 1,4;
vector<Container<double,double> > ys;
ys += yz1,yz2;

Container<double,Container<double,double> > xy(x,ys);

    vector<double> keys;
keys += 2.5,3.5;
double value = xy.calculate(keys);

    // prints 29
cout << value << endl;

}
0 голосов
/ 07 октября 2011

Я думаю, что вы также можете определить метод в вашем классе Container, который получит VALUE и KEY и вернет VALUE.Затем вы можете указать эту функцию, чтобы либо вернуть значение, либо вызвать функтор.Или (что я, вероятно, сделал бы) - это написать класс Wrapper, который оборачивает примитив в Functor, поэтому, когда вам нужен примитив, просто создайте контейнер с помощью Wrapper.Это будет выглядеть так (это, вероятно, не компилируется)

 template<typename KEY, typename VALUE>
 class PrimitiveToFunctor {
   public:
    PrimitiveToFunctor(VALUE v) {
       value = v;
    }
    VALUE function_to_wrap(KEY key) {
       return value;
    } 
  private:
  VALUE value;
 }

Container<double, Container<double, PrimitiveToFunction<double>>> c1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...