diff --git a/libuavcan/include/uavcan/util/multiset.hpp b/libuavcan/include/uavcan/util/multiset.hpp index d39b31b7c5..0f8b0799f8 100644 --- a/libuavcan/include/uavcan/util/multiset.hpp +++ b/libuavcan/include/uavcan/util/multiset.hpp @@ -135,15 +135,50 @@ private: struct IndexPredicate : ::uavcan::Noncopyable { unsigned index; - IndexPredicate(unsigned target_index) : index(target_index) { } - bool operator()(const T&) { return index--==0; } + IndexPredicate(unsigned target_index) + : index(target_index) + { } + + bool operator()(const T&) + { + return index-- == 0; + } }; struct ComparingPredicate { const T& reference; - ComparingPredicate(const T& ref) : reference(ref) { } - bool operator()(const T& sample) { return reference == sample; } + + ComparingPredicate(const T& ref) + : reference(ref) + { } + + bool operator()(const T& sample) + { + return reference == sample; + } + }; + + template + struct OperatorToFalsePredicateAdapter : ::uavcan::Noncopyable + { + const typename ParameterType::Type oper; + + OperatorToFalsePredicateAdapter(typename ParameterType::Type o) + : oper(o) + { } + + bool operator()(T& item) + { + oper(item); + return false; + } + + bool operator()(const T& item) const + { + oper(item); + return false; + } }; protected: @@ -255,6 +290,26 @@ public: return const_cast*>(this)->find(predicate); } + /** + * Calls Operator for each item of the set. + * Operator prototype: + * void (T& item) + * void (const T& item) - const overload + */ + template + void forEach(Operator oper) + { + OperatorToFalsePredicateAdapter adapter(oper); + (void)find&>(adapter); + } + + template + void forEach(Operator oper) const + { + const OperatorToFalsePredicateAdapter adapter(oper); + (void)find&>(adapter); + } + /** * Returns an item located at the specified position from the beginning. * Note that addition and removal operations invalidate indices. diff --git a/libuavcan/test/util/multiset.cpp b/libuavcan/test/util/multiset.cpp index 2c49e77122..35e7376e75 100644 --- a/libuavcan/test/util/multiset.cpp +++ b/libuavcan/test/util/multiset.cpp @@ -49,6 +49,20 @@ struct NoncopyableWithCounter : uavcan::Noncopyable int NoncopyableWithCounter::num_objects = 0; +template +struct SummationOperator : uavcan::Noncopyable +{ + T accumulator; + SummationOperator() : accumulator() { } + void operator()(const T& x) { accumulator += x; } +}; + +struct ClearingOperator +{ + template + void operator()(T& x) const { x = T(); } +}; + TEST(Multiset, Basic) { @@ -62,6 +76,8 @@ TEST(Multiset, Basic) typedef Multiset MultisetType; std::auto_ptr mset(new MultisetType(poolmgr)); + typedef SummationOperator StringConcatenationOperator; + // Empty mset->removeFirst("foo"); ASSERT_EQ(0, pool.getNumUsedBlocks()); @@ -80,6 +96,12 @@ TEST(Multiset, Basic) ASSERT_TRUE(*mset->getByIndex(0) == "1"); ASSERT_TRUE(*mset->getByIndex(1) == "2"); + { + StringConcatenationOperator op; + mset->forEach(op); + ASSERT_EQ("12", op.accumulator); + } + // Dynamic addition ASSERT_EQ("3", *mset->emplace("3")); ASSERT_EQ("3", *mset->getByIndex(2)); @@ -107,6 +129,13 @@ TEST(Multiset, Basic) ASSERT_EQ("4", *mset->find(FindPredicate("4"))); ASSERT_FALSE(mset->find(FindPredicate("nonexistent"))); + { + StringConcatenationOperator op; + mset->forEach(op); + std::cout << "Accumulator: " << op.accumulator << std::endl; + ASSERT_EQ(4, op.accumulator.size()); + } + // Removing one static; ordering will be preserved mset->removeFirst("1"); mset->removeFirst("foo"); // There's no such thing anyway @@ -165,6 +194,20 @@ TEST(Multiset, Basic) } } + // Clearing all strings + { + StringConcatenationOperator op; + mset->forEach(op); + std::cout << "Accumulator before clearing: " << op.accumulator << std::endl; + } + mset->forEach(ClearingOperator()); + { + StringConcatenationOperator op; + mset->forEach(op); + std::cout << "Accumulator after clearing: " << op.accumulator << std::endl; + ASSERT_TRUE(op.accumulator.empty()); + } + // Making sure the memory will be released mset.reset(); ASSERT_EQ(0, pool.getNumUsedBlocks()); @@ -241,6 +284,19 @@ TEST(Multiset, PrimitiveKey) ASSERT_FALSE(mset->getByIndex(5)); ASSERT_FALSE(mset->getByIndex(1000)); #endif + + // Summation and clearing + { + SummationOperator summation_operator; + mset->forEach&>(summation_operator); + ASSERT_EQ(1 + 2 + 3 + 4, summation_operator.accumulator); + } + mset->forEach(ClearingOperator()); + { + SummationOperator summation_operator; + mset->forEach&>(summation_operator); + ASSERT_EQ(0, summation_operator.accumulator); + } }