如何为我的类hirerachy定义一个公共迭代器

时间:2016-03-06 19:49:24

标签: c++ iterator

我的团队设计了一个用于存储来自不同“信号”的数据的库。信号是带时间戳的浮点值列表。我们有三种方式来存储信号(取决于它首先从硬件中记录的方式):

  • MarkerSignal:我们存储std::vector std::pair (boost::posix_time::ptime,float)RawSignal
  • boost::posix_time::ptime:我们存储了一个开始时间(boost::posix_time::time_duration),一个采样周期(std::vector),最后是float start time + period * value's index in the vector(每个值的时间戳)是NumericalSignal
  • boost::posix_time::ptime:我们存储了一个开始时间(boost::posix_time::time_duration),一个采样周期(float),一个比例(float),一个偏移量({{1}最后std::vector short {时间戳计算为RawSignalfloat值为short*scale+offset

这三个信号有一个共同的父类(SignalBase)存储信号的名称,描述,单位和类似的东西。我们使用访问者模式让人们很好地将SignalBase“投射”到MarkerSignal / RawSignal / NumericalSignal,然后访问其中包含的数据。

最后,我们每个类需要迭代所有元素,一个元素实际上是一对(boost::posix_time::ptime,float)(如MarkerSignal)。每次我们想要这样做时,都必须创建一个访问者。

将所有信号存储为std::vector<std::pair<boost::posix_time::ptime,float>>(或根据需要返回此类对象)会占用太多内存。

我们认为最好的可能是定义我们自己的迭代器对象。迭代器可以访问时间戳和值,如:

SignalBase* signal = <any signal>;
for ( SignalBase::iterator iter = signal->begin();
      iter != signal->end();
      ++iter )
{
    boost::posix_time::ptime timestamp = iter.time();
    float value = iter.value();
}

创建此类迭代器类的最佳方法/策略是什么?(具有size_t索引属性的简单类或MarkerSignal / RawSignal / NumericalSignal容器的特定迭代器作为属性,存储std::pair<boost::posix_time::ptime,float>并从++运算符更新它。)。

另外,我更倾向于使用解决方案rpoposed避免使用虚拟表(在迭代大量信号时让++time()value()更快。)

2 个答案:

答案 0 :(得分:0)

总结一下,如果你对效率有价值,我认为你能达到的最好效果可能就是这样:

template <typename SignalType, typename Functor = function<void(typename SignalType::value_type&&) > >
void iterateThroughSignal(SignalBase *signal, Functor foo) {
    SignalType *specificSignal = dynamic_cast<SignalType *>(signal);
    if (!specificSignal)
        return;
    for (typename SignalType::iterator it = specificSignal->begin();
         it != specificSignal->end();
         it++) {
       foo(*it); // retrieving value from iterator...
    }
}

然后拨打电话:

iterateThroughSignal<MarkerSignal>(signal, [](MarkerSignal::value_type&& msv){ 
   /*doing something with value...*/ 
});

我不确定你是否在使用C ++ 11,所以lambda可以被函数指针替换,使用左值引用的rvalue引用和带函数签名的std :: function ...

修改 为了在foo签名的类型与SignalType::value_type匹配时进行编译,需要与sfinae一起玩一点:

template <typename SignalType> 
class IterateHelper {
    template <typename Functor>
    static typename enable_if<first_param_is<Functor, typename SignalType::value_type>::value >::type iterateThroughSignal(SignalBase *signal, Functor foo) {
        SignalType *specificSignal = dynamic_cast<SignalType *>(signal);
        if (!specificSignal)
            return;
        for (typename SignalType::iterator it = specificSignal->begin();
            it != specificSignal->end();
            it++) {
            foo(*it); // retrieving value from iterator...
       }
    }
    template <typename Functor>
    static typename enable_if<!first_param_is<Functor, typename SignalType::value_type>::value >::type iterateThroughSignal(SignalBase *signal, Functor foo) {
    }
};

我将first_param_is helper struct的实现留给你......调用将改为:

IteratorHelper<MarkerSignal>::iterateThroughSignal(signal, [](MarkerSignal::value_type&& msv){ 
   /*doing something with value...*/ 
});

答案 1 :(得分:0)

因为我想要一些易于使用我的库的人(能够做一个for循环)我终于实现了我自己的迭代器:

SignalBase中添加了两个虚拟函数(除此之外别无选择,运行时将使用虚拟表):

virtual size_t floatDataCount() const = 0;
virtual bool loadFloatInfoAt( size_t pos, SignalFloatIter::ValueInfo& info ) const = 0;

SignalBase中添加了函数以获取开始/结束迭代器:

inline BDL::SignalFloatIter beginFloatIter() const { return BDL::SignalFloatIter::beginIter( *this ); }
inline BDL::SignalFloatIter endFloatIter() const { return BDL::SignalFloatIter::endIter( *this ); }

声明了迭代器类:

class SignalFloatIter
{
public:
    SignalFloatIter( const SignalBase* signal = NULL, size_t pos = 0 );
    SignalFloatIter( const SignalFloatIter& iter );

    static SignalFloatIter beginIter( const SignalBase& signal );
    static SignalFloatIter endIter( const SignalBase& signal );

    SignalFloatIter& operator=( const SignalFloatIter& iter );

    bool operator==( const SignalFloatIter& iter ) const;
    bool operator!=( const SignalFloatIter& iter ) const;

    /** Pre-increment operator */
    SignalFloatIter& operator++();

    /** Post-increment operator */
    SignalFloatIter operator++(int unused);

    inline const BDL::time& when() const { assert( m_valid ); return m_info.first.first; }
    inline const BDL::duration& duration() const { assert( m_valid ); return m_info.first.second; }
    inline const float& value() const { assert( m_valid ); return m_info.second; }
    inline size_t index() const { assert( m_valid ); return m_pos; }
    inline BDL::MarkerKey markerKey() const { assert( m_valid ); return std::make_pair( when(), duration() ); }
    inline bool valid() const { return m_valid; }

    typedef std::pair<BDL::time,BDL::duration> TimeInfo;
    typedef std::pair<TimeInfo,float> ValueInfo;

private:
    const SignalBase* m_signal;
    size_t m_pos;
    bool m_valid;
    ValueInfo m_info;

    void loadCurInfo();
};

实现:

SignalFloatIter::SignalFloatIter( const SignalBase* signal, size_t pos ) :
    m_signal( signal ),
    m_pos( pos )
{
    loadCurInfo();
}

SignalFloatIter::SignalFloatIter( const SignalFloatIter& iter )
{
    operator=( iter );
}

SignalFloatIter SignalFloatIter::beginIter( const SignalBase& signal )
{
    return SignalFloatIter( &signal, 0 );
}

SignalFloatIter SignalFloatIter::endIter( const SignalBase& signal )
{
    return SignalFloatIter( &signal, signal.floatDataCount() );
}

SignalFloatIter& SignalFloatIter::operator=( const SignalFloatIter& iter )
{
    if ( this != &iter )
    {
        m_signal = iter.m_signal;
        m_pos = iter.m_pos;
        m_info = iter.m_info;
        m_valid = iter.m_valid;
    }
    return *this;
}

bool SignalFloatIter::operator==( const SignalFloatIter& iter ) const
{
    if ( m_signal == iter.m_signal )
    {
        if ( m_pos == iter.m_pos )
        {
            assert( m_valid == iter.m_valid );
            if ( m_valid )
                assert( m_info == iter.m_info );
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        assert( false );
        return false;
    }
}

bool SignalFloatIter::operator!=( const SignalFloatIter& iter ) const
{
    return !( *this == iter );
}

SignalFloatIter& SignalFloatIter::operator++()
{
    ++m_pos;
    loadCurInfo();
    return *this;
}

SignalFloatIter SignalFloatIter::operator++( int unused )
{
    SignalFloatIter old = *this;
    assert( unused == 0 ); // see http://en.cppreference.com/w/cpp/language/operator_incdec
    ++m_pos;
    loadCurInfo();
    return old;
}

void SignalFloatIter::loadCurInfo()
{ 
    if ( m_signal )
    {
        m_valid = m_signal->loadFloatInfoAt( m_pos, m_info );
    }
    else
    {
        assert( false );
        m_valid = false;
    }
}

它非常简单易用,适用于任何信号:

std::cout << "Signal timestamped data are: ";
for ( BDL::SignalFloatIter iter = signal.beginFloatIter();
      iter != signal.endFloatIter();
      ++iter )
{
    std::cout << iter.when() << " : " << iter.value() << std::endl;
}