Related
I am trying to achieve something like this but getting boost bind related errors
#include <map>
#include "boost/assign.hpp"
#include <boost/foreach.hpp>
#include <string>
#include <boost/function.hpp>
#include <boost/bind.hpp>
struct myStruct_t
{
int getInt () {return 1;};
};
int foo( myStruct_t* m, std::string str)
{
if(str == "A")
{
return m->getInt();
}
else if (str == "B")
{
return 2;
}
else
{
return 3;
}
}
typedef std::map<std::string, boost::function<int(myStruct_t*, std::string)> > Map_t;
Map_t myMap = boost::assign::map_list_of ("Jake", boost::bind((&foo, _1), "A")
("Ken", boost::bind((&foo, _1), "B")
("Pete", boost::bind((&foo, _1), "C");
int main ()
{
std::vector<std::string> myVal;
myVal.push_back("Jake");
myVal.push_back("Ken");
myVal.push_back("Pete");
myStruct_t myStruct;
BOOST_FOREACH( const std::string& aStr, myVal)
{
Map_t::const_iterator iter = myMap.find(aStr);
if(iter != myMap.end())
{
int myInt = (iter->second)(myStruct);
}
}
return 0;
}
The errors I am getting are
In file included from /usr/local/boost-1.60.0/include/boost/bind.hpp:22:0,
from prog.cc:6:
/usr/local/boost-1.60.0/include/boost/bind/bind.hpp: In instantiation of 'boost::_bi::result_traits<boost::_bi::unspecified, boost::arg<1> >':
/usr/local/boost-1.60.0/include/boost/bind/bind.hpp:1212:48: instantiated from 'boost::_bi::bind_t<boost::_bi::unspecified, boost::arg<1>, boost::_bi::list1<boost::_bi::value<const char*> > >'
prog.cc:32:81: instantiated from here
/usr/local/boost-1.60.0/include/boost/bind/bind.hpp:75:37: error: no type named 'result_type' in 'struct boost::arg<1>'
prog.cc:34:82: error: expected ')' before ';' token
prog.cc: In function 'int main()':
prog.cc:50:48: error: no match for call to '(const boost::function<int(myStruct_t*, std::basic_string<char>)>) (myStruct_t&)'
/usr/local/boost-1.60.0/include/boost/function/function_template.hpp:765:17: note: candidate is: result_type boost::function2<R, T1, T2>::operator()(T0, T1) const [with R = int, T0 = myStruct_t*, T1 = std::basic_string<char>, result_type = int]
It seems I am puzzled the way boost::bind is used. Can someone please help me doing it correctly? Many Thanks.
The function signature expected at the call site is int(myStruct*)
because of this line (I added the & to remove a logic error):
int myInt = (iter->second)(&myStruct);
in which case the declaration of the map should be:
typedef std::map<std::string, boost::function<int(myStruct_t*)> > Map_t;
Map_t myMap = boost::assign::map_list_of
("Jake", boost::bind(&foo, boost::placeholders::_1, std::string("A")))
("Ken", boost::bind(&foo, boost::placeholders::_1, std::string("B")))
("Pete", boost::bind(&foo, boost::placeholders::_1, std::string("C")));
And then you're good to go.
explanation:
boost[std]::bind returns a function object which is designed to accept only the parameters that are mentioned as placeholders in the bind expression.
This means that the function that is finally called will often have more arguments than the function object returned by bind
so in your case, foo has the following signature:
int foo( myStruct_t* m, std::string str)
i.e. takes two arguments and returns an int.
However at the point of the call to bind:
boost::bind(&foo, boost::placeholders::_1, std::string("A"))
What we're saying is "capture the function foo and the second argument (a string). Return me a function object that requires one argument (_1) and forward that argument as the first argument to foo while passing the bound string as the second argument.
so given:
auto f = boost::bind(&foo, boost::placeholders::_1, std::string("A"));
f has the signature int f(MyStruct*)
and when called with
auto i = f(&mystruct);
it is equivalent to calling:
auto i = foo(&mystruct, std::string("A"));
Inspired by Antony's Williams "C++ Concurrency in Action" I wanted to take a closed look at his thread safe hash map. I copied its code and added some output operators and this is what I came up with:
#include <boost/thread/shared_mutex.hpp>
#include <functional>
#include <list>
#include <mutex>
#include <iostream>
template <typename Key, typename Value, typename Hash = std::hash<Key>>
class thread_safe_hashmap
{
private:
class bucket_type
{
public:
typedef std::pair<Key, Value> bucket_value;
typedef std::list<bucket_value> bucket_data;
typedef typename bucket_data::iterator bucket_iterator;
bucket_data data;
mutable boost::shared_mutex mutex;
bucket_iterator find_entry_for(const Key& key) const
{
return std::find_if(data.begin(), data.end(),
[&](const bucket_value& item) { return item.first == key; });
}
public:
void add_or_update_mapping(Key const& key, Value const& value)
{
std::unique_lock<boost::shared_mutex> lock(mutex);
bucket_iterator found_entry = find_entry_for(key);
if (found_entry == data.end())
{
data.push_back(bucket_value(key, value));
}
else
{
found_entry->second = value;
}
}
};
std::vector<std::unique_ptr<bucket_type>> buckets;
Hash hasher;
bucket_type& get_bucket(Key const& key) const
{
std::size_t const bucket_index = hasher(key) % buckets.size();
return *buckets[bucket_index];
}
template <typename Key2, typename Value2>
friend std::ostream& operator<<(std::ostream& os, const thread_safe_hashmap<Key2, Value2>& map);
public:
thread_safe_hashmap(unsigned num_buckets = 19, Hash const& hasher_ = Hash())
: buckets(num_buckets), hasher(hasher_)
{
for (unsigned i = 0; i < num_buckets; ++i)
{
buckets[i].reset(new bucket_type);
}
}
thread_safe_hashmap(thread_safe_hashmap const& other) = delete;
thread_safe_hashmap& operator=(thread_safe_hashmap const& other) = delete;
void add_or_update_mapping(Key const& key, Value const& value)
{
get_bucket(key).add_or_update_mapping(key, value);
}
};
template <typename First, typename Second>
std::ostream& operator<<(std::ostream& os, const std::pair<First, Second>& p)
{
os << p.first << ' ' << p.second << '\n';
return os;
}
template <typename Key, typename Value>
std::ostream& operator<<(std::ostream& os, const thread_safe_hashmap<Key, Value>& map)
{
for (unsigned i = 0; i < map.buckets.size(); ++i)
{
for (const auto el : map.buckets[i]->data) os << el << ' ';
os << '\n';
}
return os;
}
int main()
{
thread_safe_hashmap<std::string, std::string> map;
map.add_or_update_mapping("key1", "value1"); // problematic line
std::cout << map;
}
The marked line is causing problems on both gcc and clang:
clang++ -Wall -std=c++14 main2.cpp -lboost_system -o main
main2.cpp:24:14: error: no viable conversion from returned value of type 'std::_List_const_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >' to function
return type 'bucket_iterator' (aka '_List_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >')
return std::find_if(data.begin(), data.end(),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main2.cpp:32:37: note: in instantiation of member function 'thread_safe_hashmap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, std::hash<string> >::bucket_type::find_entry_for'
requested here
bucket_iterator found_entry = find_entry_for(key);
^
main2.cpp:71:21: note: in instantiation of member function 'thread_safe_hashmap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, std::hash<string>
>::bucket_type::add_or_update_mapping' requested here
get_bucket(key).add_or_update_mapping(key, value);
^
main2.cpp:98:7: note: in instantiation of member function 'thread_safe_hashmap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, std::hash<string> >::add_or_update_mapping'
requested here
map.add_or_update_mapping("key1", "value1");
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/stl_list.h:125:12: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from
'std::_List_const_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >' to 'const std::_List_iterator<std::pair<std::__cxx11::basic_string<char>,
std::__cxx11::basic_string<char> > > &' for 1st argument
struct _List_iterator
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.3.1/../../../../include/c++/5.3.1/bits/stl_list.h:125:12: note: candidate constructor (the implicit move constructor) not viable: no known conversion from
'std::_List_const_iterator<std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >' to 'std::_List_iterator<std::pair<std::__cxx11::basic_string<char>,
std::__cxx11::basic_string<char> > > &&' for 1st argument
1 error generated.
melpon's online demo
What am I missing here?
This is the expected behavior. In find_entry_for you're trying to return const_iterator, which doesn't match the return type iterator.
find_entry_for is const member function, for data.begin(), data will be const std::list<bucket_value>, begin() called on it will return const_iterator. And std::find_if will return the same type with the type of the parameter iterator, i.e. const_iterator, which could not be implicitly converted to the return type of find_entry_for, i.e. bucket_iterator (std::list::iterator).
Because the returned iterator might be used to change the value it points to, you could
Change find_entry_for to non-const member function. (Or add it as new overloading function, change the original const member function's return type to const_iterator.)
Try to convert const_iterator to iterator before returns.
bucket_iterator is defined the following way
typedef typename bucket_data::iterator bucket_iterator;
That is it is not a constant iterator.
However in member function find_entry_for
bucket_iterator find_entry_for(const Key& key) const
{
return std::find_if(data.begin(), data.end(),
[&](const bucket_value& item) { return item.first == key; });
}
standard algorithm std::find_if uses the constant iterator because this member function is declared with qualifier const and there are used overloaded functions begin and end for the constant data member data.
So you need to define the constant iterator in the class and use it as the return type of the function.
For example
typedef typename bucket_data::const_iterator const_bucket_iterator;
Consider the following code:
#include <boost/serialization/nvp.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
class Foo{
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int)
{
ar & BOOST_SERIALIZATION_NVP(i);
}
int i;
Foo():i(0){}
public:
Foo(int k):i(k){}
};
int main(int argc, char *argv[])
{
std::vector< Foo> f;
f.push_back(Foo(12));
std::ofstream os("path");
boost::archive::xml_oarchive oa(os);
oa << boost::serialization::make_nvp("f", f);
os.close();
std::vector<Foo> g;
std::ifstream is("path");
boost::archive::xml_iarchive ia(is);
ia >> boost::serialization::make_nvp("f", g);
}
Which works fine when serializing a vector of Foos. However, if I try to serialize a map of Foos, it fails on the private default constructor:
std::map<std::string, Foo> f;
f.insert(std::make_pair("hello", Foo(12)));
std::ofstream os("path");
boost::archive::xml_oarchive oa(os);
oa << boost::serialization::make_nvp("f", f);
os.close();
std::map<std::string, Foo> g;
std::ifstream is("path");
boost::archive::xml_iarchive ia(is);
ia >> boost::serialization::make_nvp("f", g);
fails with
In file included from main.cpp:2:
In file included from /usr/local/include/boost/serialization/nvp.hpp:19:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/utility:70:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_pair.h:109:18: error: field of type 'Foo' has private default constructor
: first(), second() { }
^
/usr/local/include/boost/serialization/access.hpp:132:17: note: in instantiation of member function 'std::pair<const std::basic_string<char>, Foo>::pair' requested here
::new(t)T;
^
/usr/local/include/boost/serialization/serialization.hpp:93:13: note: in instantiation of function template specialization 'boost::serialization::access::construct<std::pair<const std::basic_string<char>, Foo> >' requested here
access::construct(t);
^
/usr/local/include/boost/serialization/serialization.hpp:158:9: note: in instantiation of function template specialization 'boost::serialization::load_construct_data<boost::archive::xml_iarchive, std::pair<const std::basic_string<char>, Foo> >' requested here
load_construct_data(ar, t, v);
^
/usr/local/include/boost/serialization/detail/stack_constructor.hpp:58:31: note: in instantiation of function template specialization 'boost::serialization::load_construct_data_adl<boost::archive::xml_iarchive, std::pair<const std::basic_string<char>, Foo> >' requested here
boost::serialization::load_construct_data_adl(
^
/usr/local/include/boost/serialization/collections_load_imp.hpp:83:48: note: in instantiation of member function 'boost::serialization::detail::stack_construct<boost::archive::xml_iarchive, std::pair<const std::basic_string<char>, Foo> >::stack_construct' requested here
detail::stack_construct<Archive, type> t(ar, v);
^
/usr/local/include/boost/serialization/collections_load_imp.hpp:158:16: note: (skipping 12 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
hint = ifunc(ar, s, item_version, hint);
^
/usr/local/include/boost/archive/detail/common_iarchive.hpp:66:18: note: in instantiation of function template specialization 'boost::archive::load<boost::archive::xml_iarchive, std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > >' requested here
archive::load(* this->This(), t);
^
/usr/local/include/boost/archive/basic_xml_iarchive.hpp:86:39: note: in instantiation of function template specialization 'boost::archive::detail::common_iarchive<boost::archive::xml_iarchive>::load_override<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > >' requested here
this->detail_common_iarchive::load_override(t.value(), 0);
^
/usr/local/include/boost/archive/xml_iarchive.hpp:93:38: note: in instantiation of function template specialization 'boost::archive::basic_xml_iarchive<boost::archive::xml_iarchive>::load_override<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > >' requested here
basic_xml_iarchive<Archive>::load_override(t, 0);
^
/usr/local/include/boost/archive/detail/interface_iarchive.hpp:60:23: note: in instantiation of function template specialization 'boost::archive::xml_iarchive_impl<boost::archive::xml_iarchive>::load_override<const boost::serialization::nvp<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > > >' requested here
this->This()->load_override(t, 0);
^
main.cpp:50:8: note: in instantiation of function template specialization 'boost::archive::detail::interface_iarchive<boost::archive::xml_iarchive>::operator>><const boost::serialization::nvp<std::map<std::basic_string<char>, Foo, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, Foo> > > > >' requested here
ia >> boost::serialization::make_nvp("f", g);
^
main.cpp:34:5: note: implicitly declared private here
Foo():i(0){}
^
I'm using clang
Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
and boost version 1.55 as shipped with Ubuntu 14.04LTS.
I have tried providing the load_construct_data() function as follows:
namespace boost
{
namespace serialization
{
template<class Archive>
inline void load_construct_data(Archive &archive, Foo*a, unsigned int
file_version)
{
::new(a)Foo(0);
}
}
}
but I'm still getting the same error, because it requires the constructor when instantiating a std::pair
Oh. Aha.
I just used Boost 1.57.0 to compare the situation with map<string, Foo>.
Well, you're in luck. You've found another library version dependency (likely a bug).
Not using that, but providing the private default constructor, GCC 4.8.2 compiles it just fine: Live On Coliru [1]
GCC 4.9.0 fails to compile it though (it uses a newer version of the standard library too). The std::pair<> default constructor fails to compile there, since Foo is not default constructible: Live On Coliru
Solution
Luckily the solution with save_construct_data/load_construct_data saves the day, again.
However, you need to cater for the fact that the element type is actually not Foo, but std::pair<T const, Foo>.
template <class Archive, typename K> inline friend void save_construct_data(Archive& ar, std::pair<K, Foo> const* v, const unsigned int) {
std::cerr << __PRETTY_FUNCTION__ << "\n";
ar & boost::serialization::make_nvp("first", v->first);
ar & boost::serialization::make_nvp("second", v->second.i);
}
template <class Archive, typename K> inline friend void load_construct_data(Archive& ar, std::pair<K, Foo>* v, const unsigned int) {
std::cerr << __PRETTY_FUNCTION__ << "\n";
typename std::remove_cv<K>::type first;
ar & boost::serialization::make_nvp("first", first);
int tmp;
ar & boost::serialization::make_nvp("second", tmp);
new(v) std::pair<K, Foo>(first, tmp);
}
Now it all works:
Live On Coliru
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>
#include <boost/version.hpp>
#include <fstream>
#include <iostream>
class Foo {
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &, const unsigned int) {
std::cerr << __PRETTY_FUNCTION__ << "\n";
}
template <class Archive, typename K> inline friend void save_construct_data(Archive& ar, std::pair<K, Foo> const* v, const unsigned int) {
std::cerr << __PRETTY_FUNCTION__ << "\n";
ar & boost::serialization::make_nvp("first", v->first);
ar & boost::serialization::make_nvp("second", v->second.i);
}
template <class Archive, typename K> inline friend void load_construct_data(Archive& ar, std::pair<K, Foo>* v, const unsigned int) {
std::cerr << __PRETTY_FUNCTION__ << "\n";
typename std::remove_cv<K>::type first;
ar & boost::serialization::make_nvp("first", first);
int tmp;
ar & boost::serialization::make_nvp("second", tmp);
new(v) std::pair<K, Foo>(first, tmp);
}
int i;
public:
Foo(int k) : i(k) {}
friend std::ostream& operator<<(std::ostream& os, Foo const& foo) {
return os << "Foo { " << foo.i << " }";
}
};
namespace boost { namespace serialization {
} }
int main() {
using Data = std::map<std::string, Foo>;
std::cout << "Boost version: " << BOOST_VERSION << "\n";
{
auto f = Data { {"a", 12 }, {"b", 42} };
//for (auto& e : f) std::cout << e.first << ", " << e.second << "\n";
std::ofstream os("path");
boost::archive::xml_oarchive oa(os);
oa << boost::serialization::make_nvp("f", f);
}
{
Data g;
std::ifstream is("path");
boost::archive::xml_iarchive ia(is);
ia >> boost::serialization::make_nvp("f", g);
for (auto& e : g)
std::cout << e.first << ", " << e.second << "\n";
}
}
Which prints:
Boost version: 105700
void save_construct_data(Archive&, const std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_oarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_oarchive]
void save_construct_data(Archive&, const std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_oarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_oarchive]
void load_construct_data(Archive&, std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_iarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_iarchive]
void load_construct_data(Archive&, std::pair<K, Foo>*, unsigned int) [with Archive = boost::archive::xml_iarchive; K = const std::__cxx11::basic_string<char>]
void Foo::serialize(Archive&, unsigned int) [with Archive = boost::archive::xml_iarchive]
a, Foo { 12 }
b, Foo { 42 }
[1] (I can't link it on Coliru because the boost libraries there have been recompiled to the GCC 5.0 ABI)
Notes
A better, general, solution would be to do the load/save_construct_data trick generically for non-default-constructible types in the boost::serialization namespace. That way, people won't have to "know" about the std::pair<> implementation detail. They could just implement load/save_construct_data for their own user-types and it would JustWork™ whether they put it in a vector or a map.
Implementing that generically is less-than-trivial though, and might interfere with some other machineries internal to the Boost Serialization framework.
I'll prefer to get some help of the Boost Serialization maintainers to do that in a reliable way. So, it seems I'll be submitting two tickets today.
It doesn't necessarily work for vector either. Vector deserialization resizes the vector to the required size first. This requires the elements to be default-constructible.
Note how this is only a problem because
construction is not accessed via the serialization::access friend "token"
the class is default constructible
The Documented Solution
The documentation tells you to use save_construct_data and load_construct_data for types that are not default-constructible.
Specifically they promise it will work for STL containers of these as well:
In addition to the deserialization of pointers, these overrides are used in the deserialization of STL containers whose element type has no default constructor.
In practice, this works well in v1.57.0:
Live On Coliru
But in 1.58.0 it isn't true...
A Bug In Boost 1.58.0
Version 1.58.0 seems to have broken this:
The code seems to do required check (from serialization/vector.hpp in the unoptimized version of load(...)):
if(detail::is_default_constructible<U>()){
t.resize(count);
// ... snip ...
}
else{
t.reserve(count);
// ... snip ...
}
However, this does the check at runtime. The method will statically refuse to compile. Oops.
The Fix
Instead of having the branches in the same flow code, it should be dispatched so that only the applicable branch is instantiated. I tested with this simplistic approach:
namespace sehe_bugfix {
template<class Archive, class U, class Allocator>
inline void load_elements(
Archive & ar,
std::vector<U, Allocator> &t,
const unsigned int /* file_version */,
collection_size_type count,
mpl::true_
){
const boost::archive::library_version_type library_version(
ar.get_library_version()
);
item_version_type item_version(0);
if(boost::archive::library_version_type(3) < library_version){
ar >> BOOST_SERIALIZATION_NVP(item_version);
}
t.resize(count);
typename std::vector<U, Allocator>::iterator hint;
hint = t.begin();
while(count-- > 0){
ar >> boost::serialization::make_nvp("item", *hint++);
}
}
template<class Archive, class U, class Allocator>
inline void load_elements(
Archive & ar,
std::vector<U, Allocator> &t,
const unsigned int /* file_version */,
collection_size_type count,
mpl::false_
){
const boost::archive::library_version_type library_version(
ar.get_library_version()
);
item_version_type item_version(0);
if(boost::archive::library_version_type(3) < library_version){
ar >> BOOST_SERIALIZATION_NVP(item_version);
}
t.reserve(count);
while(count-- > 0){
detail::stack_construct<Archive, U> u(ar, item_version);
ar >> boost::serialization::make_nvp("item", u.reference());
t.push_back(u.reference());
ar.reset_object_address(& t.back() , & u.reference());
}
}
}
template<class Archive, class U, class Allocator>
inline void load(
Archive & ar,
std::vector<U, Allocator> &t,
const unsigned int file_version,
mpl::false_
){
const boost::archive::library_version_type library_version(
ar.get_library_version()
);
// retrieve number of elements
item_version_type item_version(0);
collection_size_type count;
ar >> BOOST_SERIALIZATION_NVP(count);
sehe_bugfix::load_elements(ar, t, file_version, count, detail::is_default_constructible<U>());
}
And it works.
SUMMARY
Sadly I don't have time now to investigate on the map<> situation. But I suspect that things are similar. The documented solution should still work. And it might still be broken.
I'll report the above issue later today to the issue tracker of boost.
I hope the answer helps you find the/a solution none-the-less
I am new to template function and cannot figure this error. Hope you can help.
#include <iostream>
#include <vector>
/*
* Print to the screen the content of a vector
* Define function template in the header
*/
template <typename T> void print_vector(T& v) {
for(typename std::vector<T>::const_iterator i = v.begin(); i != v.end(); ++i)
std::cout << *i << ' ';
}
int main() {
std::vector<int> field;
field.resize(12, 1);
/*
for( std::vector<int>::const_iterator i = field.begin(); i != field.end(); ++i)
std::cout << *i << ' ';
*/
print_vector(field);
}
My code failed to compile with a very long error message that I can't even insert here.
error: conversion from ‘std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}’ to non-scalar type ‘std::vector<std::vector<int>, std::allocator<std::vector<int> > >::const_iterator {aka __gnu_cxx::__normal_iterator<const std::vector<int>*, std::vector<std::vector<int>, std::allocator<std::vector<int> > > >}’ requested
utility.h:21:59: error: no match for ‘operator!=’ in ‘i != (& v)->std::vector<_Tp, _Alloc>::end<int, std::allocator<int> >()’
utility.h:21:59: note: candidates are:
In file included from /usr/include/x86_64-linux-gnu/c++/4.7/./bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/string:43,
from /usr/include/c++/4.7/bits/locale_classes.h:42,
from /usr/include/c++/4.7/bits/ios_base.h:43,
from /usr/include/c++/4.7/ios:43,
from /usr/include/c++/4.7/istream:40,
from /usr/include/c++/4.7/fstream:40,
from utility.h:4:
/usr/include/c++/4.7/ext/new_allocator.h:134:5: note: template<class _Tp> bool __gnu_cxx::operator!=(const __gnu_cxx::new_allocator<_Tp>&, const __gnu_cxx::new_allocator<_Tp>&)
/usr/include/c++/4.7/ext/new_allocator.h:134:5: note: template argument deduction/substitution failed:
utility.h:21:59: note: ‘std::vector<std::vector<int>, std::allocator<std::vector<int> > >::const_iterator {aka __gnu_cxx::__normal_iterator<const std::vector<int>*, std::vector<std::vector<int>, std::allocator<std::vector<int> > > >}’ is not derived from ‘const __gnu_cxx::new_allocator<_Tp>’
When you call
std::vector<int> field;
...
print_vector(field);
the T in print_vector1 is the type of field, i.e., std::vector<int>. Therefore the typename std::vector<T>::const_iterator in
for(typename std::vector<T>::const_iterator i = v.begin();
is a std::vector<std::vector<int> >::const_iterator, to which v.begin() (itself being a std::vector<int>::iterator) is not convertible. Use
for(typename T::const_iterator i = v.begin();
instead.
1 That is to say: in the function that's made from the function template print_vector for this case.
In your function:
template <typename T>
void print_vector(T& v) {
for(typename std::vector<T>::const_iterator i = v.begin(); i != v.end(); ++i)
std::cout << *i << ' ';
}
T is deduced as std::vector<int>, so you're trying to convert a std::vector<int>::iterator (the result of v.begin()) into a std::vector<std::vector<int>>::const_iterator (the type of i), which is not a valid conversion.
Instead, just make a more specialized template function:
template <typename T>
void print_vector(std::vector<T>& v) {
// as before
}
Either replace std::vector<T>::const_iterator i with simply T::const_iterator i if you want to pass it any iterable, or specialize it appropriately with void print_vector(std::vector<T>& v) if you wish for it to only work against vectors of any types.
If you run a substitution as you have it now you will see you are giving a vector to something creating a vector out of the type, hence getting a vector of vector.
Also GCC 4.8 and 4.9 have improved messages and add a more precise error caret.
If you can't move compiler, which is understandable, then you can always feed small snippets like that to an online one like ideone.com. The error would most likely have been a lot more concise and readable in 4.8.
Oh, and if you have access to C++11 features (I don't remember what made it in 4.7) you can auto those type declarations AND use something like cbegin() instead of begin to get the type resolution to pick a const_iterator instead of a normal one. If you're learning about templates, and have no work environment constraints, I would strongly recommend a move to C++11 and a more recent GCC, it will make easing in a lot easier.
You called the function properly. But then you confused the template-argument (type of the container) with the containers element-type.
Anyway, there's no reason for it not being const.
Thus, it should look like this instead:
template <typename T> void print_vector(const T& v) {
for(typename T::const_iterator i = v.begin(); i != v.end(); ++i)
std::cout << *i << ' ';
}
Or using the standard-library and lambdas:
template <typename T> void print_vector(const T& v) {
using std::begin; // Using ADL for begin and end to support raw arrays
using std::end;
std::for_each(begin(v), end(v), [](auto&& x){std::cout<<*i<<' ';});
}
Or using auto and range-for-loops (my favorite):
template <typename T> void print_vector(const T& v) {
for(auto&& i : v)
std::cout << *i << ' ';
}
Or using C++1z (projected for 2017):
void print_vector(const auto& v) {
for(auto&& i : v)
std::cout << *i << ' ';
}
I try to use find_if to find a key in a map by its value. But I can't compile the code:
struct IsCurrency : binary_function<pair<const Bill::CodeCurrency, string>, string, bool> {
bool isCurrency(const pair<const Bill::CodeCurrency, string>& element, const string& expected) const {
return element.second == expected;
}
};
string currency = "RUB";
map<Bill::CodeCurrency, string>::const_iterator my_currency = find_if(Bill::currency_code_map().begin(), Bill::currency_code_map().end(), bind2nd(IsCurrency(), currency)); /// <--- ERROR IS HERE
Bill::CodeCurrency is an enum.
error:
/usr/include/c++/4.7/bits/stl_algo.h:4490:41: required from ‘_IIter std::find_if(_IIter, _IIter, _Predicate) [with _IIter = std::_Rb_tree_const_iterator<std::pair<const Bill::CodeCurrency, std::basic_string<char> > >; _Predicate = std::binder2nd<IsCurrency>]’
../src/money_acceptor/itl_bill_acceptor.cpp:182:121: required from here
/usr/include/c++/4.7/backward/binders.h:155:29: error: no match for call to ‘(const IsCurrency) (const first_argument_type&, const second_argument_type&)’
Could you please help me to determine what's the error here?
As described in my comment, the actual problem is that the predicate provided to find_if is required to have an operator(), not some function with a name similar to the class name.
C++03 version:
#include <map>
#include <functional>
#include <string>
#include <algorithm>
namespace Bill
{
enum CodeCurrency
{
A, B, C
};
typedef std::map<CodeCurrency, std::string> currency_code_map_t;
currency_code_map_t const& currency_code_map()
{
static currency_code_map_t m;
return m;
}
}
struct IsCurrency
: std::binary_function< Bill::currency_code_map_t::value_type, std::string,
bool >
{
bool operator()(Bill::currency_code_map_t::value_type const& element,
std::string const& expected) const
{
return element.second == expected;
}
};
int main()
{
std::string currency = "RUB";
Bill::currency_code_map_t::const_iterator my_currency =
std::find_if( Bill::currency_code_map().begin(),
Bill::currency_code_map().end(),
bind2nd(IsCurrency(), currency) );
}
C++11 version:
#include <map>
#include <functional>
#include <string>
#include <algorithm>
namespace Bill
{
enum CodeCurrency
{
A, B, C
};
typedef std::map<CodeCurrency, std::string> currency_code_map_t;
currency_code_map_t const& currency_code_map()
{
static currency_code_map_t m;
return m;
}
}
int main()
{
std::string currency = "RUB";
auto check = [¤cy](Bill::currency_code_map_t::value_type const& element)
{ return element.second == currency; };
auto my_currency =
std::find_if( Bill::currency_code_map().cbegin(),
Bill::currency_code_map().cend(),
check );
}
Note this algorithm is O(N). You might want to consider using something like a boost::bimap if you need to find elements often, which can be O(logN).
Replace bool isCurrency(...) by bool operator () (...) to make your structure callable.