[英]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::vector
的float
(每個值的時間戳是start time + period * value's index in the vector
) NumericalSignal
:我們店的開始時間( boost::posix_time::ptime
),采樣周期( boost::posix_time::time_duration
),規模( float
),偏移( float
),最后一個std::vector
的short
(時間戳是針對RawSignal
計算的,並且float
值是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>
和從更新++
操作者... )。
而且,如果解決方案避免使用虛擬表(在迭代大信號時具有++
, 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_type
與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結構的實現留給您...調用將更改為:
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.