Without using the features of C++11 and higher (I will accept them but would prefer C++98),
I have to write a template function with argument T which is an STL container of ints. It receives such a container along with another int which it tries to search for,
Right now I have this but it doesn't compile:
template <template<int> class T>
T::iterator easyfind(T &container, int val)
{
T::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return (it);
}
I wonder if I can somehow force the T parameter to always be a class template that is parametrized over integers... I tried writing T<int> but it still doesn't compile.
[NOTE] This answer uses C++20. #PatrickRoberts made me notice that you were preferably requesting a C++98 solution. I leave it anyway because it may be of any help to you.
You can just add a requirement for your template, checking the container's type is int.
[Demo]
#include <iostream> // cout
#include <list>
#include <type_traits> // is_same
#include <vector>
template <typename C>
requires std::is_same<typename C::value_type, int>::value
auto easyfind(const C& container, int val)
{
for (auto it{std::cbegin(container)}; it != std::cend(container); ++it)
{
if (val == *it) { return it; }
}
return std::cend(container);
}
int main()
{
std::vector<int> vi{1, 2, 3};
if (auto it{easyfind(vi, 2)}; it != std::cend(vi))
{
std::cout << *it << "\n";
}
std::list<int> li{4, 5, 6};
if (auto it{easyfind(li, 8)}; it != std::cend(li))
{
std::cout << *it << "\n";
}
std::vector<double> vd{0.5, 1.3, 2.8};
//if (auto it{easyfind(vd, 1.3)}; it != std::cend(vd)) // error
//{
// std::cout << *it << "\n";
//}
}
Though there is an accepted answer, let me try to solve it using C++98.
DEMO
#include <vector>
#include <iostream>
namespace details{
struct true_type{static const bool value = true;};
struct false_type{static const bool value = false;};
template<typename T1,typename T2> struct is_same : false_type{};
template<typename T> struct is_same<T,T>:true_type{};
#define STATIC_ASSERT(expr, msg) \
{ \
char STATIC_ASSERT##msg[(expr)?1:-1]; \
}
};
template <class T>
typename T::iterator easyfind(T &container, int val)
{
using namespace details;
//static_assert can be used in C++11 onwards
STATIC_ASSERT((is_same<typename T::value_type,int>::value == true_type::value),InavalidType);
typename T::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return (it);
}
int main(){
std::vector<int> a{1,2,3};
auto it = easyfind(a,1);
if(it != a.end())
std::cout<<*it<<std::endl;
auto it2 = easyfind(a,4);
if(it2 != a.end())
std::cout<<*it<<std::endl;
else
std::cout<<"Not Found"<<std::endl;
std::vector<double> b{1.0,2.0,3.0};
// std::vector<int>::iterator it3 = easyfind(b,1.0); //error
return 0;
}
template <template<int> class T> is not what you expect.
You want
template <template <typename> class Container>
typename Container<int>::iterator easyfind(Container<int> &container, int val)
{
#if 1 // Your code
typename Container<int>::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return it;
#else // code with <algorithm>
return std::find(container.begin(), container.end(), val);
#endif
}
Unfortunately, std::vector doesn't match Container, as it has extra template parameter (Allocator, which is defaulted).
You might add overload:
template <template <typename, typename> class Container, typename Alloc>
typename Container<int, Alloc>::iterator easyfind(Container<int, Alloc> &container, int val)
C++11 would allow template <template <typename...> class Container.
Simpler would be to use container directly as type:
template <typename Container>
#if 1 // No SFINAE
typename Container::iterator
#else // SFINAE with traits from C++11, which can be written trivially in C++98
typename std::enable_if<std::is_same<int, typename Container::value_type>>::type
#endif
easyfind(Container& container, int val)
{
#if 1 // Your code
typename Container::iterator it = container.begin();
for ( ; it != container.end(); it++)
if (val == *it)
break ;
return it;
#else // code with <algorithm>
return std::find(container.begin(), container.end(), val);
#endif
}
but more generic would be to drop int requirement completely:
template <typename Container>
typename Container::iterator
easyfind(Container& container, typename Container::const_reference val)
{
return std::find(container.begin(), container.end(), val);
}
Related
My question is what is the type for *it, if it is of the type of std::map<std::string, int>::iterator
As a follow up to that question, if I would like to use accumulate to calculate all the map values, how could I do? Thanks.
It's a reference to an std::pair<const KeyT, ValueT> (where KeyT and ValueT are the key and value parameters of the map). You may write some kind of iterator wrapper to wrap map iterators, make them return just the value and then use std::accumulate:
template<typename ItT>
struct SecondIterator
{
ItT it;
SecondIterator(const ItT &it) : it(it) {}
SecondIterator &operator++()
{
++it;
return *this;
}
SecondIterator operator++(int)
{
SecondIterator t=*this;
++(*this);
return t;
}
SecondIterator &operator--()
{
--it;
return *this;
}
SecondIterator operator--(int)
{
SecondIterator t=*this;
--(*this);
return t;
}
typename ItT::value_type::second_type &operator*()
{
return it->second;
}
bool operator==(const SecondIterator &other)
{
return it==other.it;
}
bool operator!=(const SecondIterator &other)
{
return it!=other.it;
}
};
(probably I forgot some typename here and there, but you got the idea)
but if you ask me it's definitely not worth the effort.
If you want to accumulate the mapped_type of a std::map, perhaps the following helper classes will be of interest:
#include <functional>
#include <numeric>
#include <utility>
#ifndef USE_CXX11
#if (__cplusplus >= 201100) || (_MSC_VER >= 1800)
#define USE_CXX11 1
#endif
#endif
#if USE_CXX11
/*
map_value_accumulator - helper class that allows iterators of std::map
to be used with std::accumulate
*/
template <typename T, typename Op = std::plus<typename T::mapped_type> >
class map_value_accumulator
{
public:
typedef typename T::value_type pair_type;
typedef typename T::mapped_type value_type;
value_type operator()( value_type acc, pair_type const& p) const {
return op_( acc, p.second);
}
map_value_accumulator() : op_(Op()) {};
map_value_accumulator(Op&& op) : op_(op) {};
private:
Op op_;
};
/*
make_map_value_accumulator() - function that uses argument deduction to
help create map_value_accumulator objects
*/
// make_map_value_accumulator() that returns a user-specified operation
// the operation defaults to std::plus<> is not specified
template <typename T, typename Op = std::plus<typename T::mapped_type> >
map_value_accumulator< T, Op>
make_map_value_accumulator( T const& m, Op&& op = Op())
{
return map_value_accumulator< T, Op>(std::forward<Op>(op));
}
#else
/*
map_value_accumulator - helper class that allows iterators of std::map
to be used with std::accumulate
*/
template <typename T, typename Op = std::plus<typename T::mapped_type> >
class map_value_accumulator
{
public:
typedef typename T::value_type pair_type;
typedef typename T::mapped_type value_type;
typedef std::plus<typename T::mapped_type> default_operator_type;
value_type operator()( value_type acc, pair_type const& p) const {
return op_( acc, p.second);
}
map_value_accumulator() : op_(default_operator_type()) {};
map_value_accumulator(Op op) : op_(op) {};
private:
Op op_;
};
/*
make_map_value_accumulator() - function that uses argument deduction to
help create map_value_accumulator objects
*/
template <typename T, typename Op>
map_value_accumulator< T, Op>
make_map_value_accumulator( T const& m, Op const& op)
{
return map_value_accumulator< T, Op>(op);
}
template <typename T>
map_value_accumulator< T, std::plus<typename T::mapped_type> >
make_map_value_accumulator( T const& m)
{
typedef std::plus<typename T::mapped_type> default_operator_type;
return map_value_accumulator< T, default_operator_type>();
}
#endif /* USE_CXX11 */
#include <iostream>
#include <ostream>
#include <map>
int main()
{
std::map<char, int> m;
m.insert(std::make_pair('a', 1));
m.insert(std::make_pair('z', 26));
m.insert(std::make_pair('c', 3));
m.insert(std::make_pair('b', 2));
m.insert(std::make_pair('?', -2));
using std::cout;
using std::endl;
// directly create the map_value_accumulator functor (defaults to std::plus)
cout << accumulate(m.begin(), m.end(), 0, map_value_accumulator<std::map<char,int> >()) << endl;
// create a map_value_accumulator deduced from the user's map type (default to std::plus)
cout << accumulate(m.begin(), m.end(), 0, make_map_value_accumulator(m)) << endl;
// create a map_value_accumulator deduced from the user's map type and specifying an operation functor
cout << accumulate(m.begin(), m.end(), 1, make_map_value_accumulator(m, std::multiplies<int>())) << endl;
#if USE_CXX11
cout << "accumulate with a lambda: ";
// create a map_value_accumulator deduced from the user's map type and specifying a lambda for the operation
// (will perform a sum of squares)
cout << accumulate(m.begin(), m.end(), 0, make_map_value_accumulator(m, [](int x, int y){ return x + y * y; })) << endl;
#endif
return 0;
}
Note: I've updated the example. The first example would not work with lambdas and didn't work with MS compilers older than VS 2013. The new example has some conditional compilation that has a C++11 implementation (that supports lambdas) and a non-C++11 variant that works with VS 2003 and later and presumably any reasonable version of GCC.
Suppose we have this template
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
...
}
How can it be stated that, obviously the elements in container should be of type T?
Can this all be abbreviated (maybe in C++11)?
While other answers using value_type are correct , the canonical solution to this frequent problem is to not pass the container in the first place : use the Standard Library semantics, and pass a pair of iterators.
By passing iterators, you don't have to worry about the container itself. Your code is also much more generic : you can act on ranges, you can use reversed iterators, you can combine your template with other standard algorithms etc.. :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
...
}
int main(){
std::vector<int> v { 41, 42 };
contains(std::begin(v), std::end(v), 42);
};
If you want to check the type carried by Iterator, you can use std::iterator_traits :
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
(Note that this assertion is generally not needed : if you provide a value not comparable with T, the template will not compile in the first place)
The final template would look like :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
while(begin != end)
if(*begin++ == value)
return true;
return false;
}
Live demo
Notes:
1) This should not be a surprise, but our contains template now has almost the same signature than std::find (which returns an iterator) :
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
2) If modifying the signature of the original contains is too much, you can always forward the call to our new template :
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
return contains(std::begin(theContainer), std::end(theContainer), theReference);
}
You might restrict the container type in the template:
#include <algorithm>
#include <iostream>
#include <vector>
template< template<typename ... > class Container, typename T>
bool contains(const Container<T>& container, const T& value) {
return std::find(container.begin(), container.end(), value) != container.end();
}
int main()
{
std::vector<int> v = { 1, 2, 3 };
std::cout << std::boolalpha
<< contains(v, 0) << '\n'
<< contains(v, 1) << '\n';
// error: no matching function for call to ‘contains(std::vector<int>&, char)’
contains(v, '0') ;
return 0;
}
A more complete solution (addressing some comments):
#include <algorithm>
#include <array>
#include <iostream>
#include <map>
#include <set>
#include <vector>
// has_member
// ==========
namespace Detail {
template <typename Test>
struct has_member
{
template<typename Class>
static typename Test::template result<Class>
test(int);
template<typename Class>
static std::false_type
test(...);
};
}
template <typename Test, typename Class>
using has_member = decltype(Detail::has_member<Test>::template test<Class>(0));
// has_find
// ========
namespace Detail
{
template <typename ...Args>
struct has_find
{
template<
typename Class,
typename R = decltype(std::declval<Class>().find(std::declval<Args>()... ))>
struct result
: std::true_type
{
typedef R type;
};
};
}
template <typename Class, typename ...Args>
using has_find = has_member<Detail::has_find<Args...>, Class>;
// contains
// ========
namespace Detail
{
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::false_type, const Container<Key, Args...>& container, const Key& value) {
bool result = std::find(container.begin(), container.end(), value) != container.end();
std::cout << "Algorithm: " << result << '\n';;
return result;
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::true_type, const Container<Key, Args...>& container, const Key& value) {
bool result = container.find(value) != container.end();
std::cout << " Member: " << result << '\n';
return result;
}
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(const Container<Key, Args...>& container, const Key& value) {
return Detail::contains(has_find<Container<Key, Args...>, Key>(), container, value);
}
template<typename T, std::size_t N>
bool contains(const std::array<T, N>& array, const T& value) {
bool result = std::find(array.begin(), array.end(), value) != array.end();
std::cout << " Array: " << result << '\n';;
return result;
}
// test
// ====
int main()
{
std::cout << std::boolalpha;
std::array<int, 3> a = { 1, 2, 3 };
contains(a, 0);
contains(a, 1);
std::vector<int> v = { 1, 2, 3 };
contains(v, 0);
contains(v, 1);
std::set<int> s = { 1, 2, 3 };
contains(s, 0);
contains(s, 1);
std::map<int, int> m = { { 1, 1}, { 2, 2}, { 3, 3} };
contains(m, 0);
contains(m, 1);
return 0;
}
For standard container, you may use value_type:
template<typename Container>
bool contains (const Container & theContainer, const typename Container::value_type& theReference) {
...
}
Note that there is also const_reference in your case:
template<typename Container>
bool contains (const Container & theContainer, typename Container::const_reference theReference) {
...
}
You can check the value_type of container and T using static_assert
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
static_assert( std::is_same<typename Container::value_type, T>::value,
"Invalid container or type" );
// ...
}
Demo Here
Using std::enable_if (http://en.cppreference.com/w/cpp/types/enable_if), but a little more complicated than with static_assert.
EDIT: According to P0W's comment, using std::enable_if allows us to use SFINAE, which is nice when you decide to have more overloads. For example if the compiler decides to use this templated function, with a Container with no value_type typedefed, it won't generate an error instantly, like static_assert would, just looks for other functions which perfectly fits the signature.
Tested on Visual Studio 12.
#include <vector>
#include <iostream>
template<typename Container, typename T>
typename std::enable_if<
std::is_same<T, typename Container::value_type>::value, bool>::type //returns bool
contains(const Container & theContainer, const T & theReference)
{
return (std::find(theContainer.begin(), theContainer.end(), theReference) != theContainer.end());
};
int main()
{
std::vector<int> vec1 { 1, 3 };
int i = 1;
float f = 1.0f;
std::cout << contains(vec1, i) << "\n";
//std::cout << contains(vec1, f); //error
i = 2;
std::cout << contains(vec1, i) << "\n";
};
output:
1
0
PS: Your original function does it too, except that allows derived classes too. These solutions does not.
I try to write a function, which will sum the elements of a container. This container can be Vector, List, Queue, etc... That's why I tried templates.
Unfortunately I get this error:
'C' is not a template
Source:
#include <iostream>
#include <vector>
using namespace std;
template<class C, typename T>
T sum( C<T>::iterator begin, C<T>::iterator end ) {
T s = null;
for (C<T>::iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
int main()
{
vector<int> v = {5, 9, 0, 11};
cout << sum(v.begin(), v.end()) << endl;
return 0;
}
What do I wrong? How should I fix it?
You could express the whole thing in terms of a iterator type, and use iterator_traits to get the value_type:
#include <iterator>
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
sum(Iterator begin, Iterator end)
{
using value_type = typename std::iterator_traits<Iterator>::value_type;
value_type s = value_type();
for (Iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
In real life, use std::accumulate:
int sum = std::accumulate(v.begin(), v.end(), 0);
The particular error you get is because you'd need a template template argument:
template<template <typename> class C, typename T>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
T sum( C<T>::iterator begin, C<T>::iterator end )
However, the standard containers typically have more than just one template argument:
template < class T, class Alloc = allocator<T> > class vector
and it is a bit non-trivial to write such function correctly. You could use variadic template arguments, or you could do like the standard library does, and only specialize as much as you really need:
// <algorithm>
namespace std {
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
}
In your case (pretending that your need is not covered by the standard algorithms library already):
template <typename Iterator>
auto sum(Iterator begin, Iterator end)
-> decltype(*begin+*begin) // the type of summing two of them
{
if (begin == end) throw std::logic_error("....");
auto s = *begin;
++begin;
for (; begin != end; ++begin) {
s += *begin;
}
return s;
}
There are some more differences from your original code:
the new code does not assume a null or a default constructor defined (T s = null;)
does not introduce additional iterator (it)
uses pre-increment
throws an exception when begin==end
If you add an init parameter, you can make it almost noexcept:
template <typename Iterator, typename T>
T sum(Iterator begin, Iterator end, T init)
{
for (; begin!=end; ++begin)
init += *begin;
return init;
}
But only almost, because init += *begin could still throw.
If you have such signature, you've by the way reproduced the signature of std::accumulate.
I have a variable with a type similar to:
map<bool, map<string, pair<string, int> > > items;
which I pass around to different functions.
Is there a less tedious way for me to iterate over it then saying
for (map<bool, map<string, pair<string, int> > >::iterator p = items.begin();
p != items.end(); p++)
...
every time? (i.e. can I somehow omit the type name, with a macro or template or something? A manual typedef doesn't count.)
I'm using Visual C++ 2008.
You can use BOOST_FOREACH. You'll have to use a typedef for clarity though:
typedef std::map<std::string, std::pair<std::string, int> > inner_map;
typedef std::pair<bool, inner_map> map_entry;
BOOST_FOREACH(map_entry& p, items)
{
...
}
I prefer a plain typedef and a for loop though. I see typedef the same way I see a variable assignment:
typedef std::map<std::string, std::pair<std::string, int> > inner_map;
typedef std::map<bool, inner_map>::iterator map_iterator;
for (map_iterator i = items.begin(); i != items.end(); ++i)
{
...
}
Those typedefs can also be private members. This coding style is much clearer, since you see at a glance the types involved.
Or you can use plain std::for_each, if you are ready to write a functor. I don't really like this in standard C++ since the loop body is no longer local (this can be an advantage in some cases however):
struct some_functor
{
template <typename K, typename V>
void operator()(std::pair<K, V>& item)
{
// In the context below, K is bool and
// V is map<string, pair<string, int> >
}
};
and then later
std::for_each(items.begin(), items.end(), some_functor());
If you upgrade to VS2010, you have alternatives: auto and std::for_each with a lambda (which I prefer). With C++0x, technically, you also have range-based for loops (not available in VS2010).
To conclude, I'd do:
class meaningful_data
{
typedef std::map<std::string, std::pair<std::string, int> > inner_map;
std::map<bool, inner_map> items;
public:
typedef std::pair<bool, inner_map> value_type;
typedef std::map<bool, inner_map>::iterator iterator;
typedef std::map<bool, inner_map>::const_iterator const_iterator;
iterator begin() { return items.begin(); }
const_iterator begin() const { return items.begin(); }
iterator end() { return items.end(); }
const_iterator end() const { return items.end(); }
// Add some interface here (as small as possible)
};
and iterate like this:
for (meaningful_data::iterator i = d.begin(); i != d.end(); ++i)
{
...
}
or
BOOST_FOREACH(meaningful_data::value_type& i, d)
{
...
}
You'll probably want to encapsulate such a complex type, at least with a few typedefs (you're not forced to use a full blown class if the inner_map type ought to be public).
I recommend using typedef, which is probably a way of saying "no, you can't" ;)
Otherwise, if you were to switch to a compiler that supports auto as defined in C++0x, you could say:
for (auto p = items.begin(); p != items.end(); ++p) // ...
(Oh, by the way, I also recommend ++p to avoid copying the iterator)
You could use the standard for_each algorithm:
#include <algorithm>
struct your_functor {
template<typename T>
void operator()(T const &item) {
// Your loop body here.
}
}
std::for_each(items.begin(), items.end(), your_functor());
You can use BOOST_AUTO
You can write your own algorithm function.
template<class C>
void do_what_I_want_to_do(C& c)
{
for (C::iterator i = c.begin(); i != c.end(); ++c)
{
// do something
}
}
do_what_I_want_to_do(items);
That may or may not be an improvement for you.
Sure, use typedefs:
typedef std::map<std::string, std::pair<std::string, int> > Item;
typedef Item::const_iterator ItemCItr;
typedef std::map<bool, Item> ItemMap;
typedef ItemMap::const_iterator ItemMapItr;
for (ItemMapItr it = m.begin(), end = m.end(); it != end; ++it)
{
const Item & item = it->second;
for (ItemItr jt = item.begin(), jend = item.end(); jt != jend; ++jt)
{
/* ... */
}
}
After seeing all the "no you can't do this" answers, I took some time to try to find at least a partial workaround.
This version almost works, with the caveat that every reference to the iterator also requires a reference to the container. It might not be a good idea to actually use this (because of the heap allocation and other things) but I thought I'd share it anyway:
#include <map>
#include <iostream>
using namespace std;
template<typename T>
bool _end(T& src, void *iterator = NULL)
{ return static_cast<typename T::iterator>(iterator) < src.end(); }
template<typename T>
struct _IterateHelper
{
typename T::iterator *pIterator;
_IterateHelper(T& dummy, void *&p)
{ this->pIterator = static_cast<typename T::iterator *>(p); }
~_IterateHelper() { delete pIterator; }
};
template<typename T>
_IterateHelper<T> _iterateHelper(T& dummy, void *&p)
{ return _IterateHelper<T>(dummy, p); }
template<typename T>
bool _iterate(T& container, void *&iterator)
{
typename T::iterator *&p =
reinterpret_cast<typename T::iterator *&>(iterator);
if (iterator == NULL) { p = new typename T::iterator(container.begin()); }
else { ++*p; }
return *p != container.end();
}
template<typename T>
typename T::iterator & I(T& container, void *&pIterator)
{ return *static_cast<typename T::iterator *>(pIterator); }
#define FOR_EACH(state, container) \
void *state = NULL; \
for (_iterateHelper(container, state); _iterate(container, state); )
int main()
{
map<string, string> m;
items["a"] = "b";
items["1"] = "2";
FOR_EACH(p, items)
cout << I(items, p)->first << ": " << I(items, p)->second << endl;
}
Qt offers its own foreach implementation, so i just reworked it for std::map - basically just a simple modification (->second). Tested on MSVC and gcc.
struct ForeachBaseBase {};
template <typename T1, typename T2>
class ForeachBase: public ForeachBaseBase
{
public:
inline ForeachBase(const std::map<T1,T2>& t): c(t), brk(0), i(c.begin()), e(c.end()){}
const std::map<T1,T2> c;
mutable int brk;
mutable typename std::map<T1,T2>::const_iterator i, e;
inline bool condition() const { return (!brk++ && i != e);}
};
template <typename T1, typename T2> inline std::map<T1,T2> *pMForeachPointer(const std::map<T1,T2> &) { return 0; }
template <typename T1, typename T2> inline ForeachBase<T1,T2> pMForeachBaseNew(const std::map<T1,T2>& t)
{ return ForeachBase<T1,T2>(t); }
template <typename T1, typename T2>
inline const ForeachBase<T1,T2> *pMForeachBase(const ForeachBaseBase *base, const std::map<T1,T2> *)
{ return static_cast<const ForeachBase<T1,T2> *>(base); }
#if defined(Q_CC_MIPS)
/*
Proper for-scoping in MIPSpro CC
*/
# define MAP_FOREACH(variable,container) \
if(0){}else \
for (const ForeachBaseBase &_container_ = pMForeachBaseNew(container); \
pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->condition(); \
++pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i) \
for (variable = pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i->second; \
pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk; \
--pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk)
#elif defined(Q_CC_DIAB)
// VxWorks DIAB generates unresolvable symbols, if container is a function call
# define MAP_FOREACH(variable,container) \
if(0){}else \
for (const ForeachBaseBase &_container_ = pMForeachBaseNew(container); \
pMForeachBase(&_container_, (__typeof__(container) *) 0)->condition(); \
++pMForeachBase(&_container_, (__typeof__(container) *) 0)->i) \
for (variable = pMForeachBase(&_container_, (__typeof__(container) *) 0)->i->second; \
pMForeachBase(&_container_, (__typeof__(container) *) 0)->brk; \
--pMForeachBase(&_container_, (__typeof__(container) *) 0)->brk)
#else
# define MAP_FOREACH(variable, container) \
for (const ForeachBaseBase &_container_ = pMForeachBaseNew(container); \
pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->condition(); \
++pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i) \
for (variable = pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->i->second; \
pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk; \
--pMForeachBase(&_container_, true ? 0 : pMForeachPointer(container))->brk)
#endif // MSVC6 || MIPSpro
#define mforeach MAP_FOREACH
Let's say I'd like to write an algorithm that prints the value of each element in a container. The container could be a Sequence or Associative container (e.g. std::vector or std::map). In the case of a sequence, the algorithm would print the value_type. In the case of an associative type, the algorithm would print the data_type. How can I write my algorithm (only once!) so that it works with either one? Pretend that the algorithm is complex and that I don't want to repeat it for both sequence/associative versions.
For example:
template <class Iterator>
void printSequence(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it;
}
template <class Iterator>
void printAssociative(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << it->second;
}
template <class Iterator>
void printEither(Iterator begin, Iterator end)
{
// ????
}
The difference that you have between your two function templates is not a difference between associative containers and sequences but a difference in the part of the type that is stored.
To clarify, std::set is an associative container but would work with your printSequence function; the problem with map is not the fact that it is associative, but that the value_type is a pair an you are only interested on the second part.
The simplest thing to do is to abstract the dereferencing operation.
E.g. used like this:
#include <map>
#include <vector>
template< class X, class Y >
void test( const std::map<X, Y>& mp )
{
printEither( mp.begin(), mp.end(), MakeMapDerefence( mp ) );
}
template< class Y >
void test( const std::vector<Y>& vec )
{
printEither( vec.begin(), vec.end(), MakeSimpleDereference( vec ) );
}
Defined like this (there's a fair bit of boiler plate that's probably a boost one-liner):
template< class ReferenceType, class IteratorType >
struct SimpleDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return *i;
}
};
template< class ReferenceType, class IteratorType >
struct MapDereference
{
ReferenceType operator() ( IteratorType i ) const
{
return i->second;
}
};
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >
MakeSimpleDereference( const Container& )
{
return SimpleDereference< typename Container::const_reference
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate SimpleDerefence instance
template< class Container >
SimpleDereference< typename Container::reference
, typename Container::iterator >
MakeSimpleDereference( Container& )
{
return SimpleDereference< typename Container::reference
, typename Container::iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >
MakeMapDerefence( const Container& )
{
return MapDereference< const typename Container::mapped_type&
, typename Container::const_iterator >();
}
// Helper template function to make an appropriate MapDerefence instance
template< class Container >
MapDereference< typename Container::mapped_type&
, typename Container::iterator >
MakeMapDereference( Container& )
{
return MapDereference< typename Container::mapped_type&
, typename Container::iterator >();
}
#include <iostream>
#include <ostream>
template <class Iterator, class Dereference> void printEither(Iterator begin, Iterator end, Dereference deref)
{
for (; begin != end; ++begin)
{
std::cout << deref(begin);
}
}
I've whipped up an iterator adapter based on Charles' answer. I'm posting it here in case anyone finds it useful:
#include <iostream>
#include <map>
#include <vector>
#include <boost/iterator/iterator_adaptor.hpp>
//------------------------------------------------------------------------------
template <class Iterator>
void print(Iterator begin, Iterator end)
{
for (Iterator it=begin; it!=end; ++it)
std::cout << *it << "\n";
}
//------------------------------------------------------------------------------
template <class BaseIterator>
class MapDataIterator :
public boost::iterator_adaptor<
MapDataIterator<BaseIterator>,
BaseIterator,
typename BaseIterator::value_type::second_type >
{
public:
typedef typename BaseIterator::value_type::second_type& reference;
MapDataIterator() {}
explicit MapDataIterator(BaseIterator base)
: MapDataIterator::iterator_adaptor_(base) {}
private:
friend class boost::iterator_core_access;
reference dereference() const
{return this->base_reference()->second;}
};
//------------------------------------------------------------------------------
int main()
{
std::vector<int> vec;
vec.push_back(31);
vec.push_back(41);
std::map<int,int> map;
map[31] = 41;
map[59] = 26;
typedef MapDataIterator< std::map<int,int>::iterator > DataIter;
print( vec.begin(), vec.end() );
print( DataIter(map.begin()), DataIter(map.end()) );
}
This solution has the added advantage that the algorithm need not be aware of how to dereference the iterators. It is also reusable for any existing algorithm that expects a "data sequence".
I'm surprised this little critter doesn't already exist in Boost.