簡體   English   中英

如何為我的班級定義通用的迭代器

[英]How to define an common iterator to my class hirerachy

我的團隊設計了一個庫,用於存儲來自不同“信號”的數據。 信號是帶有時間戳的浮點值的列表。 我們有三種存儲信號的方式(首先取決於從硬件記錄信號的方式):

  • MarkerSignal :我們存儲std::pair的排序std::vector (boost::posix_time::ptime,float)
  • RawSignal :我們存儲一個開始時間( boost::posix_time::ptime ),一個采樣周期( boost::posix_time::time_duration ),最后是一個std::vectorfloat (每個值的時間戳是start time + period * value's index in the vector
  • NumericalSignal :我們店的開始時間( boost::posix_time::ptime ),采樣周期( boost::posix_time::time_duration ),規模( float ),偏移( float ),最后一個std::vectorshort (時間戳是針對RawSignal計算的,並且float值是short*scale+offset

這三個信號具有一個公共的父類( SignalBase ),用於存儲信號的名稱,描述,單位和類似內容。 我們使用訪問者模式讓人們很好的“演員”的SignalBaseMarkerSignal / 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>和從更新++操作者... )。

而且,如果解決方案避免使用虛擬表(在迭代大信號時具有++time()value()更快value() ,我將更願意。

綜上所述,我認為如果您對效率有所重視,那么您可以實現的最好成績是:

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,因此可以通過函數指針,使用左值引用的右值引用以及帶有函數簽名的std :: function來替換lambda ...

編輯:要使其在foo簽名的SignalType::value_typeSignalType::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結構的實現留給您...調用將更改為:

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

因為我想要讓使用我的庫的人易於使用的東西(能夠輕松地執行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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM