How to define an common iterator to my class hirerachy - c++

My team designed a library meant to store data from different "signals". A signal is a list of timestamped float values. We have three way to store a signal (depending of the way it was recorded from the hardware in the first place):
MarkerSignal: We store a sorted std::vector of std::pair of (boost::posix_time::ptime,float)
RawSignal: We store a start time (boost::posix_time::ptime), a sampling period (boost::posix_time::time_duration) and finally a std::vector of float (each value's timestamp is start time + period * value's index in the vector)
NumericalSignal: We store a start time (boost::posix_time::ptime), a sampling period (boost::posix_time::time_duration), a scale (float), an offset (float) and finally a std::vector of short (timestamp is computed as for RawSignal and float value is short*scale+offset)
Those three signals have a common parent class (SignalBase) storing the signal's name, description, unit and stuff like that. We use the visitor pattern to let people nicely "cast" the SignalBase to a MarkerSignal/RawSignal/NumericalSignal and then access the data it contains.
In the end, what we need for each class is to iterate through all elements, one element being actually a pair of (boost::posix_time::ptime,float) (like MarkerSignal). And it's a pain having to create a visitor every time we want to do that.
Storing all signals as a std::vector<std::pair<boost::posix_time::ptime,float>> (or returning an object of this kind on demand) uses too much memory.
We thought the best was probably to define our own iterator object. The iterator would give access to the timestamp and value, like that:
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();
}
What's the best approach/strategy to create such an iterator class? (simple class with a size_t index attribute, or a MarkerSignal/RawSignal/NumericalSignal container's specific iterator as attribute, store a std::pair<boost::posix_time::ptime,float> and update it from a ++ operator...).
Also, I would much prefer if the solution rpoposed avoids using a virtual table (to have ++, time(), and value() be faster when iterating on huge signals).

To sum up I think the best you can achieve if you value for efficiency could be something like this:
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...
}
}
Then for call:
iterateThroughSignal<MarkerSignal>(signal, [](MarkerSignal::value_type&& msv){
/*doing something with value...*/
});
I'm not sure if you are using C++11 so the lambda can be replace by function pointer, rvalue reference using lvalue reference and the std::function with a function signature...
Edit:
To make it compile when the type of the foo signature won't match the SignalType::value_type there will be a need of playing a little bit with 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) {
}
};
I leave the implementation of first_param_is helper struct to you... Call would change to:
IteratorHelper<MarkerSignal>::iterateThroughSignal(signal, [](MarkerSignal::value_type&& msv){
/*doing something with value...*/
});

As I wanted something easy to use for people using my library (be able to esily do a for loop) I finally implemented my own iterator like that:
Added two virtual functions in SignalBase (found no alternative to that, runtime will use the virtual table):
virtual size_t floatDataCount() const = 0;
virtual bool loadFloatInfoAt( size_t pos, SignalFloatIter::ValueInfo& info ) const = 0;
Added functions in SignalBase to get begin/end iterators:
inline BDL::SignalFloatIter beginFloatIter() const { return BDL::SignalFloatIter::beginIter( *this ); }
inline BDL::SignalFloatIter endFloatIter() const { return BDL::SignalFloatIter::endIter( *this ); }
Declared iterator class like that:
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();
};
Implemented:
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;
}
}
It's pretty straightforward and easy to use for any signal:
std::cout << "Signal timestamped data are: ";
for ( BDL::SignalFloatIter iter = signal.beginFloatIter();
iter != signal.endFloatIter();
++iter )
{
std::cout << iter.when() << " : " << iter.value() << std::endl;
}

Related

How to template creating a map for custom types

In my code I have a number of places where I need to take an std::vector of things and put it into a std::map indexed by something. For example here are two code snippets:
//sample A
std::map<Mode::Type, std::vector<Mode>> modesByType;
for( const auto& mode : _modes ) {
Mode::Type type = mode.getType();
auto it = modesByType.find( type );
if( it == modesByType.end() ) {
std::vector<Mode> v = { mode };
modesByType.insert( std::pair( type, v ) );
} else {
it->second.push_back( mode );
}
}
//sample B
std::map<unsigned, std::vector<Category>> categoriesByTab;
for( const auto& category : _categories ) {
unsigned tabIndex = category.getTab();
auto it = categoriesByTab.find( tabIndex );
if( it == categoriesByTab.end() ) {
std::vector<Category> v = { category };
categoriesByTab.insert( std::pair( tabIndex, v ) );
} else {
it->second.push_back( category );
}
}
I'd like to generalize this and create a template function like:
template<typename T, typename V>
std::map<T,std::vector<V>> getMapByType( const std::vector<V>& items, ?? ) {
std::map<T,std::vector<V>> itemsByType;
for( const auto& item : items ) {
unsigned index = ??;
auto it = itemsByType.find( index );
if( it == itemsByType.end() ) {
std::vector<V> v = { item };
itemsByType.insert( std::pair( index, v ) );
} else {
it->second.push_back( item );
}
}
return itemsByType;
}
My question is, how do I define the ?? argument to this function so that I can call the correct V.foo() function to get the index value for the map?
Note, I do not want to make all the classes that this template (V) accepts, inherit from a base class. Can I somehow specify a lambda argument?
have a pointer to a member fn as an extra parameter
template<typename T, typename V>
std::map<T,std::vector<V>> getMapByType( const std::vector<V>& items, T (V::*fn)()const) {
std::map<T,std::vector<V>> itemsByType;
for( const auto& item : items ) {
T index = (item.*fn)();
auto it = itemsByType.find( index );
if( it == itemsByType.end() ) {
std::vector<V> v = { item };
itemsByType.emplace( index, v );
} else {
it->second.push_back( item );
}
}
return itemsByType;
}
auto res = getMapByType(items, &Category::getTab);
You can pass a function that determines the key, something like this:
template <typename V,typename F>
auto getMapByType( const std::vector<V>& items,F func) {
using key_t = std::decay_t<delctype(func(item[0]))>;
std::map<key_t,std::vector<V>> result;
for (const auto& item : items) {
result[ func(item) ].push_back(item);
}
return item;
}
Then you can call it like this
std:vector<Category> vec;
auto m = getMapByType( vec, [](const Category& c) { return c.getTab(); });
or
std:vector<Mode> vec;
auto m = getMapByType( vec, [](const Category& c) { return c.getType(); });
Note that operator[] does already what you reimplemented. It tries to find an element with the given key. If none is present it inserts a default constructed one, then it returns a reference to the mapped value.
Even without operator[] you do not need find and then insert, because insert does only insert when no element with given key was present. insert returns an iterator to the element and a bool which tells you if the insertion actually took place.

Getting min_element of from a vector of structs based on two variables

So I have a vector of structs that is defined and used in the following manner:
enum ID {
alpha,
beta,
gamma
};
using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
typedef struct pInfo {
int bar;
int key;
ID id;
TimePoint tPoint;
} pInfo;
std::vector<pInfo> pMembers;
I would like to basically iterate through this vector after populating it with all the items and get a reference to the matching element that meets my criteria.
The criteria is that I would have a function that passes in a type of ID, and it will return a reference to the vector element that is the best fit which means the one with the lowest TimePoint.
So for reference the function would be something like: pInfo& getNext(ID p_id);
And if I have a vector that has a few elements of each, like 4 alpha, 4 gamma, 4 beta I want the function to only check the ones with ID == alpha if that's what I pass in.
Right now I was using something like this:
std::min_element(std::begin(pMembers), std::end(pMembers), [](auto&& lhs, auto&& rhs){return lhs.tPoint < rhs.tPoint};
But this doesn't account for me wanting to only get certain types.
How would I do something like this?
I would just store the objects with different IDs in different vectors, one vector for each ID:
std::map<ID, std::vector<pInfo>> pMembers;
If you can't or won't do that, then I'd use a filtering iterator adaptor. The following example uses Boost.Iterator:
auto const filter = [p_id](auto const& id) { return id == p_id; };
auto const compare = [](auto const& a, auto const& b) { return a.tPoint < b.tPoint; };
auto const it = std::min_element(boost::make_filter_iterator(filter, begin(pMembers), end(pMembers)),
boost::make_filter_iterator(filter, end(pMembers), end(pMembers)),
compare).base();
Expanding on Remy's answer, this is how I would write their first way of doing it:
auto const it = std::min_element(begin(pMembers), end(pMembers), [=](auto const& a, auto const& b)
{
return std::forward_as_tuple(a.id != p_id, a.tPoint)
< std::forward_as_tuple(b.id != p_id, b.tPoint);
});
Make the lambda capture the passed ID so it can be used in the comparisons, eg:
pInfo& getNext(ID p_id)
{
if (pMembers.empty())
throw ...; // nothing to search, can't return a reference to nothing, so throw an exception instead...
auto iter = std::min_element(std::begin(pMembers), std::end(pMembers),
[=](const pInfo &lhs, const pInfo &rhs)
{
if (lhs.id == p_id) {
if (rhs.id != p_id) return true;
}
else if (rhs.id == p_id) {
if (lhs.id != p_id) return false;
}
else {
return false;
}
return lhs.tPoint < rhs.tPoint;
}
);
if (iter->id != p_id)
throw ...; // p_id not found, can't return a reference to nothing, so throw an exception instead...
return *iter;
}
Alternatively, try something more like this:
pInfo& getNext(ID p_id)
{
std::vector<std::reference_wrapper<pInfo>> v;
std::copy_if(std::begin(pMembers), std::end(pMembers), std::back_inserter(v),
[=](const pInfo &item){ return item.id == p_id; }
);
if (v.empty())
throw ...; // p_id not found, can't return a reference to nothing, so throw an exception instead...
auto iter = std::min_element(std::begin(v), std::end(v),
[](const pInfo &lhs, const pInfo &rhs){ return lhs.tPoint < rhs.tPoint; }
);
return *iter;
}
You can also simply apply the range-based-for to this problem as follows:
DEMO
const pInfo& getNext(const std::vector<pInfo>& pMembers, ID p_id)
{
const pInfo* p{nullptr};
TimePoint min{TimePoint::max()};
for(const auto& p_i : pMembers)
{
if(p_i.id == p_id && p_i.tPoint < min)
{
min = p_i.tPoint;
p = &p_i;
}
}
if(!p){
throw std::runtime_error("no data.");
}
return *p;
}

Way to Use C++ 11's for-range Syntax for Sequential Pairs?

Is there a way to use for-range loop syntax to process two sequential elements in an array?
Example...
func( std::vector< vec2 > &points )
{
std::vector< float > distances;
for( int i = 0; i < (int)points.size() - 1; i++ )
{
auto from = points.at( i );
auto to = points.at( i + 1 );
distances.push_back( magnitude( to - from ) );
}
}
Is there a way to use for-range loop syntax to process two sequential elements in an array?
Not out of the box.
However, you can roll your own wrapper class and an iterator class to get what you need.
The begin() and end() member functions of the wrapper class must return an iterator which evaluates to a std::pair when it is dereferenced with the * operator.
Here's a demonstrative program:
#include <iostream>
#include <vector>
struct VectorWrapper;
struct MyIterator
{
MyIterator(VectorWrapper const& wrapper, size_t index) : wrapper_(wrapper), index_(index) {}
std::pair<float, float> operator*();
MyIterator& operator++()
{
++index_;
return *this;
}
bool operator==(MyIterator const& rhs) const
{
return (this->index_ == rhs.index_);
}
bool operator!=(MyIterator const& rhs) const
{
return (this->index_ != rhs.index_);
}
VectorWrapper const& wrapper_;
size_t index_;
};
struct VectorWrapper
{
explicit VectorWrapper(std::vector<float>& distances) : distances_(distances) {}
MyIterator begin() const
{
return MyIterator(*this, 0);
}
MyIterator end() const
{
return MyIterator(*this, distances_.size()-1);
}
std::vector<float>& distances_;
};
std::pair<float, float> MyIterator::operator*()
{
return std::make_pair(wrapper_.distances_[index_], wrapper_.distances_[index_+1]);
}
int main()
{
std::vector<float> dist = {1, 2, 3, 4, 5, 6};
VectorWrapper wrapper(dist);
for ( auto item : wrapper )
{
std::cout << item.first << ", " << item.second << std::endl;
}
}
and its output:
1, 2
2, 3
3, 4
4, 5
5, 6
In c++17 with a bit of library help, you can get this to work:
for (auto&&[ from, to ] : adjacent_overlapped_zip( points ) ) {
distances.push_back( magnitude( to-from) );
}
where adjacent_overlapped_zip returns a range of adpted iterators over pairs or tuples of points.
template<class It>
struct range {
It b; It e;
It begin() const{ return b; }
It end() const{ return e; }
bool empty() const{ return begin()==end(); }
range without_front( std::size_t n = 1 ) const {
return {std::next(begin(), n), end()};
}
range without_back( std::size_t n = 1 ) const {
return {begin(), std::prev(end(), n)};
}
};
template<class It>
range(It b, It e)->range<It>;
template<class It>
struct adjacent_iterator:It {
auto operator*()const {
return std::make_pair( It::operator*(), std::next(*this).It::operator*() );
}
using It::It;
explicit adjacent_iterator(It it):It(it) {}
};
template<class It>
explicit adjacent_iterator( It ) -> adjacent_iterator<It>;
// TODO: support pointers
template<class C>
auto adjacent_overlapped_zip( C& c ) {
using std::begin; using std::end;
range r( begin(c), end(c) );
if (!r.empty()) {
r = r.without_back();
range retval( adjacent_iterator(r.begin()), adjacent_iterator(r.end()) );
return retval;
} else {
return {};
}
}
or something like that. The above code probably contains typos and other errors.
I'd also be tempted by:
for (auto&&[ from, to ] : transform( [](auto it){ return std::make_pair( *it, *std::next(it)); }, range( iterators_of( points ) ).without_back() ) )
distances.push_back( magnitude( to-from) );
}
with a slightly fancier set of primitives. Ranges-v3 would make this even nicer.
In raw c++11, no you are out of luck.

Unable to insert more than 256 nodes into a custom tree

I've been stuck on this for quite some time now and have even tested the issue between a 64-bit version of gcc on Ubuntu as welll as a 32-bit gcc on Windows (MinGW).
Any time I insert more than 256 nodes into a binary-tree(?), it stops counting the number of nodes. I can still access all of my data. I have a feeling that it has something to do with the way I have my structure setup, by using chars to acquire each bit of each byte, but I have no idea how to fix it.
In this header, I have a structure and some functions setup which allows me to acquire an individual bit of an object.
This is the actual tree implementation. In order to find where to store each object, the tree iterates through each byte of a key, then iterates again through each bit of those bytes. The "iterate" function is what is giving me the most difficulty though; I have no idea why, but once 256 nodes become filled with data, my structure stops counting further, then begins to replace all previous data. I believe this has something to do with the fact that a single char can only hold 0-256, but I can't see where this would be an issue. Since the location of each node is determined by the individual bits of the key, it's hard to determine why only 256 items can be placed into the tree.
The URL to my test program is at the bottom of the post. SO won't let me post more than 2 at the moment. I would like to get this done soon, so any help would be greatly appreciated.
Edit:
Just to make things easier, this is the structure that gives me the individual bit of a byte, as well as a helper function:
struct bitMask {
char b1 : 1;
char b2 : 1;
char b3 : 1;
char b4 : 1;
char b5 : 1;
char b6 : 1;
char b7 : 1;
char b8 : 1;
char operator[] ( unsigned i ) const {
switch( i ) {
case 0 : return b1;
case 1 : return b2;
case 2 : return b3;
case 3 : return b4;
case 4 : return b5;
case 5 : return b6;
case 6 : return b7;
case 7 : return b8;
}
return 0; // Avoiding a compiler error
}
};
/******************************************************************************
* Functions shared between tree-type objects
******************************************************************************/
namespace treeShared {
// Function to retrieve the next set of bits at the pointer "key"
template <typename key_t>
inline const bitMask* getKeyByte( const key_t* key, unsigned iter );
/* template specializations */
template <>
inline const bitMask* getKeyByte( const char*, unsigned );
template <>
inline const bitMask* getKeyByte( const wchar_t*, unsigned );
template <>
inline const bitMask* getKeyByte( const char16_t*, unsigned );
template <>
inline const bitMask* getKeyByte( const char32_t*, unsigned );
} // end treeShared namespace
/*
* Tree Bit Mask Function
*/
template <typename key_t>
inline const bitMask* treeShared::getKeyByte( const key_t* k, unsigned iter ) {
return (iter < sizeof( key_t ))
? reinterpret_cast< const bitMask* >( k+iter )
: nullptr;
}
/*
* Tree Bit Mask Specializations
*/
template <>
inline const bitMask* treeShared::getKeyByte( const char* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
template <>
inline const bitMask* treeShared::getKeyByte( const wchar_t* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
template <>
inline const bitMask* treeShared::getKeyByte( const char16_t* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
template <>
inline const bitMask* treeShared::getKeyByte( const char32_t* str, unsigned iter ) {
return (str[ iter ] != '\0')
? reinterpret_cast< const bitMask* >( str+iter )
: nullptr;
}
And here is the tree class:
template <typename data_t>
struct bTreeNode {
data_t* data = nullptr;
bTreeNode* subNodes = nullptr;
~bTreeNode() {
delete data;
delete [] subNodes;
data = nullptr;
subNodes = nullptr;
}
};
/******************************************************************************
* Binary-Tree Structure Setup
******************************************************************************/
template <typename key_t, typename data_t>
class bTree {
enum node_dir : unsigned {
BNODE_LEFT = 0,
BNODE_RIGHT = 1,
BNODE_MAX
};
protected:
bTreeNode<data_t> head;
unsigned numNodes = 0;
private:
bTreeNode<data_t>* iterate( const key_t* k, bool createNodes );
public:
~bTree() {}
// STL-Map behavior
data_t& operator [] ( const key_t& k );
void push ( const key_t& k, const data_t& d );
void pop ( const key_t& k );
bool hasData ( const key_t& k );
const data_t* getData ( const key_t& k );
unsigned size () const { return numNodes; }
void clear ();
};
/*
* Binary-Tree -- Element iteration
*/
template <typename key_t, typename data_t>
bTreeNode<data_t>* bTree<key_t, data_t>::iterate( const key_t* k, bool createNodes ) {
node_dir dir;
unsigned bytePos = 0;
bTreeNode<data_t>* bNodeIter = &head;
const bitMask* byteIter = nullptr;
while ( byteIter = treeShared::getKeyByte< key_t >( k, bytePos++ ) ) {
for ( int currBit = 0; currBit < HL_BITS_PER_BYTE; ++currBit ) {
// compare the bits of each byte in k
dir = byteIter->operator []( currBit ) ? BNODE_LEFT : BNODE_RIGHT;
// check to see if a new bTreeNode needs to be made
if ( !bNodeIter->subNodes ) {
if ( createNodes ) {
// create and initialize the upcoming sub bTreeNode
bNodeIter->subNodes = new bTreeNode<data_t>[ BNODE_MAX ];
}
else {
return nullptr;
}
}
// move to the next bTreeNode
bNodeIter = &(bNodeIter->subNodes[ dir ]);
}
}
return bNodeIter;
}
/*
* Binary-Tree -- Destructor
*/
template <typename key_t, typename data_t>
void bTree<key_t, data_t>::clear() {
delete head.data;
delete [] head.subNodes;
head.data = nullptr;
head.subNodes = nullptr;
numNodes = 0;
}
/*
* Binary-Tree -- Array Subscript operators
*/
template <typename key_t, typename data_t>
data_t& bTree<key_t, data_t>::operator []( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, true );
if ( !iter->data ) {
iter->data = new data_t();
++numNodes;
}
return *iter->data;
}
/*
* Binary-Tree -- Push
* Push a data element to the tree using a key
*/
template <typename key_t, typename data_t>
void bTree<key_t, data_t>::push( const key_t& k, const data_t& d ) {
bTreeNode<data_t>* iter = iterate( &k, true );
if ( !iter->data ) {
iter->data = new data_t( d );
++numNodes;
}
else {
*iter->data = d;
}
}
/*
* Binary-Tree -- Pop
* Remove whichever element lies at the key
*/
template <typename key_t, typename data_t>
void bTree<key_t, data_t>::pop( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, false );
if ( !iter || !iter->data )
return;
delete iter->data;
iter->data = nullptr;
--numNodes;
}
/*
* Binary-Tree -- Has Data
* Return true if there is a data element at the key
*/
template <typename key_t, typename data_t>
bool bTree<key_t, data_t>::hasData( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, false );
return iter && ( iter->data != nullptr );
}
/*
* Binary-Tree -- Push
* Return a pointer to the data that lies at a key
* Returns a nullptr if no data exists
*/
template <typename key_t, typename data_t>
const data_t* bTree<key_t, data_t>::getData( const key_t& k ) {
bTreeNode<data_t>* iter = iterate( &k, false );
if ( !iter )
return nullptr;
return iter->data;
}
pastebin.com/8MZ0TMpj
template <typename key_t>
inline const bitMask* treeShared::getKeyByte( const key_t* k, unsigned iter ) {
return (iter < sizeof( key_t ))
? reinterpret_cast< const bitMask* >( k+iter )
: nullptr;
}
This doesn't do what you seem to think it does. (k+iter) doesn't retrieve the iter'th byte of k, but the iter'th element of the key_t[] array pointed to by k. In other words, k+iter advances the pointer by iter*sizeof(key_t) bytes, not by iter bytes.
Formally, this code exhibits undefined behavior, by overrunning array bounds. Practically speaking, your program uses just a single byte of the key, and then sizeof(key_t)-1 random bytes that just happen to sit in memory above that key. That's why you are effectively limited to 8 bits of state.
In addition, your reinterpret_cast also exhibits undefined behavior, formally speaking. The only legal use for a pointer obtained with reinterpret_cast is to reinterpret_cast it right back to the original type. This is not the immediate cause of your problem though.

Syntax for finding structs in multisets - C++

I can't seem to figure out the syntax for finding structs in containers.
I have a multiset of Event structs. I'm trying to find one of these structs by searching on its key. I get the compiler error commented below.
struct Event {
public:
bool operator < ( const Event & rhs ) const {
return ( time < rhs.time );
}
bool operator > ( const Event & rhs ) const {
return ( time > rhs.time );
}
bool operator == ( const Event & rhs ) const {
return ( time == rhs.time );
}
double time;
int eventID;
int hostID;
int s;
};
typedef std::multiset< Event, std::less< Event > > EventPQ;
EventPQ currentEvents;
double oldRecTime = 20.0;
EventPQ::iterator ceItr = currentEvents.find( EventPQ::key_type( oldRecTime ) ); // no matching function call
I've tried a few permutations to no avail. I thought defining the conditional equality operator was going to be enough.
Solution
After correcting my typo (sorry), I now have a solution closest to AraK's, augmented by Soapbox's suggested use of explicit:
struct Event {
explicit Event(double t) : time(t), eventID(), hostID(), s() {}
Event(double t, int eid, int hid, int stype) : time(t), eventID( eid ), hostID( hid ), s(stype) {}
...
};
EventPQ::iterator ceItr = currentEvents.find( EventPQ::key_type( Event(oldRecTime) ) );
I recently discovered that another option would have been to use find_if, discussed here.
Thanks for the help.
You don't have a suitable constructor that accepts double. Just add the following constructor:
Event(double t) : time(t), eventID(/**/), hostIDeventID(/**/), s(/**/)
{ }
Here is how Event would look like:
struct Event {
public:
// Initialize other variables as needed
Event(double t) : time(t), eventID(/**/), hostIDeventID(/**/), s(/**/)
{ }
bool operator < ( const Event & rhs ) const {
return ( time < rhs.time );
}
bool operator > ( const Event & rhs ) const {
return ( time > rhs.time );
}
bool operator == ( const Event & rhs ) const {
return ( time == rhs.time );
}
double time;
int eventID;
int hostID;
int s;
};
// No need for std::less because it is used by default,
// when you define 'operator <' in your class
typedef std::multiset< Event > EventPQ;
EventPQ currentEvents;
double oldRecTime = 20.0;
// You can just pass the double, a temporary object will be created
// for you.
EventPQ::iterator ceItr = currentEvents.find( oldRecTime );
Besides the missing constructor, you don't want to call find() on the iterator ceItr but on currentEvents:
EventPQ::iterator ceItr = currentEvents.find(EventPQ::key_type(oldRecTime));
Note that find() only gives you an iterator to the first match, use equal_range() to get a range of all matches:
std::pair<EventPQ::iterator, EventPQ::iterator> result;
result = currentEvents.find(EventPQ::key_type(oldRecTime));
for(EventPQ::iterator it = result.first; it != result.second; ++it) {
// do stuff
}
You seem to have multiset and multimap confused. Multiset is for when the key and the value are one-and-the-same. Multimap is for when a key and a value are associated, but not the same object.
In this case, Event isn't actually the key. The "time" double appears to be the key. Since the key and the value are not exactly the same, you should use a multimap. Using event as both the key and value doesn't make sense here.
You don't want to construct an event with extra fields you don't need just to search for a given value. Instead, multimap lets you search using the double, which is what you really want. This also eliminates the need for less than operators in the Event class.
Your code would then look something like this:
struct Event {
double time;
int eventID;
int hostID;
int s;
};
typedef std::multimap<double, Event> EventPQ;
EventPQ currentEvents;
double oldRecTime = 20.0;
std::pair<EventPQ::iterator, EventPQ::iterator> results = currentEvents.equal_range(oldRecTime);
for(EventPQ::iterator cur = results.first; cur != results.second; ++cur) {
// do something to *cur
}