我的团队设计了一个用于存储来自不同“信号”的数据的库。信号是带时间戳的浮点值列表。我们有三种方式来存储信号(取决于它首先从硬件中记录的方式):
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
{时间戳计算为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>
并从++
运算符更新它。)。
另外,我更倾向于使用解决方案rpoposed避免使用虚拟表(在迭代大量信号时让++
,time()
和value()
更快。)
答案 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;
}