Is Adding boost::any in a boost::property_tree Possible? - c++

I am trying to store a boost::any type to a boost property tree. Here is some runnable example:
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <sys/types.h>
#include <boost/any.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
int main()
{
boost::property_tree::ptree pTree_Root;
pTree_Root.put("sigRoot.property1", "value1");
pTree_Root.put("sigRoot.property2", "value2");
pTree_Root.put("sigRoot.property3", "value3");
std::vector<std::string> vecString;
for(int i = 0; i <= 5; i++) {
vecString.push_back("somestring");
}
boost::any anyVar = vecString;
pTree_Root.put("sigRoot.property4", anyVar);
std::stringstream ss;
boost::property_tree::json_parser::write_json(ss, pTree_Root);
std::string jsonString = ss.str();
jsonString.erase(std::remove(jsonString.begin(), jsonString.end(), '\n'), jsonString.end());
std::cout << jsonString << std::endl;
return 0;
}
This does not work, probably for the same reason boost::any is not serializable. But I see I am allowed to query anyVar.type(), but I have no idea what can be done with it. Is it possible to somehow use auto or something else to add the data to the property tree without explicitly coding a fixed number of known types here? C++11/C++0x methods are also welcome.
This is the error I get:
In file included from /usr/include/boost/property_tree/ptree.hpp:17:0,
from pTreeTest.cpp:8:
/usr/include/boost/property_tree/stream_translator.hpp: In static member function ‘static void boost::property_tree::customize_stream<Ch, Traits, E, Enabler>::insert(std::basic_ostream<_Ch, _Tr>&, const E&) [with Ch = char, Traits = std::char_traits<char>, E = boost::any, Enabler = void]’:
/usr/include/boost/property_tree/stream_translator.hpp:199:13: instantiated from ‘boost::optional<std::basic_string<Ch, Traits, Alloc> > boost::property_tree::stream_translator<Ch, Traits, Alloc, E>::put_value(const E&) [with Ch = char, Traits = std::char_traits<char>, Alloc = std::allocator<char>, E = boost::any]’
/usr/include/boost/property_tree/detail/ptree_implementation.hpp:795:54: instantiated from ‘void boost::property_tree::basic_ptree<Key, Data, KeyCompare>::put_value(const Type&, Translator) [with Type = boost::any, Translator = boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, boost::any>, Key = std::basic_string<char>, Data = std::basic_string<char>, KeyCompare = std::less<std::basic_string<char> >]’
/usr/include/boost/property_tree/detail/ptree_implementation.hpp:817:13: instantiated from ‘boost::property_tree::basic_ptree<K, D, C>& boost::property_tree::basic_ptree<Key, Data, KeyCompare>::put(const path_type&, const Type&, Translator) [with Type = boost::any, Translator = boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, boost::any>, Key = std::basic_string<char>, Data = std::basic_string<char>, KeyCompare = std::less<std::basic_string<char> >, boost::property_tree::basic_ptree<Key, Data, KeyCompare>::path_type = boost::property_tree::string_path<std::basic_string<char>, boost::property_tree::id_translator<std::basic_string<char> > >]’
/usr/include/boost/property_tree/detail/ptree_implementation.hpp:832:72: instantiated from ‘boost::property_tree::basic_ptree<K, D, C>& boost::property_tree::basic_ptree<Key, Data, KeyCompare>::put(const path_type&, const Type&) [with Type = boost::any, Key = std::basic_string<char>, Data = std::basic_string<char>, KeyCompare = std::less<std::basic_string<char> >, boost::property_tree::basic_ptree<Key, Data, KeyCompare>::path_type = boost::property_tree::string_path<std::basic_string<char>, boost::property_tree::id_translator<std::basic_string<char> > >]’
pTreeTest.cpp:26:47: instantiated from here
/usr/include/boost/property_tree/stream_translator.hpp:33:13: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
/usr/include/c++/4.6/ostream:581:5: error: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = boost::any]’

No. boost::any has no operator <<. You cannot use any in property_tree. anyVar.type() returns std::typeinfo, this class provides runtime information about type.
template<typename Type, typename Translator>
self_type & put(const path_type & path, const Type & value, Translator tr);
Set the value of the node at the given path to the supplied value, translated to the tree's data type. If the node doesn't exist, it is created, including all its missing parents.
You can create Translator and pass it to function put, since ptree is really basic_ptree<std::string, std::string> your translator should convert your any to string.
Simple example of translator
struct SimpleTranslator
{
public:
boost::optional<std::string> put_value(const boost::any& value)
{
if (value.type() == typeid(std::vector<std::string>))
{
std::stringstream ss;
std::vector<std::string> vec = boost::any_cast<std::vector<std::string>>(value);
std::copy(vec.begin(), vec.end(), std::ostream_iterator<std::string>(ss));
return ss.str();
}
return boost::optional<std::string>();
}
};
http://liveworkspace.org/code/275820c1becfb63deda4e4eed8524833

Related

Why does std::pair<char, std::string> have problems with std::copy to ostream

Can someone explain why the code below won't compile if I remove the comments?
#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include <iterator>
#include <algorithm>
std::pair<char, std::string> uncons(const std::string & s) {
std::string::size_type len = s.length();
if (len == 1) {
return std::make_pair(s[0], "");
} else if (len > 1) {
return std::make_pair(s[0], s.substr(1));
}
throw "Ooops, empty string";
}
std::ostream& operator<<(std::ostream & out, const std::pair<char, std::string> & p) {
if (p.second.length() > 0) {
out << "(\'" << p.first << "\', \"" << p.second << "\")";
}
return out;
}
int main(int argc, char ** argv) {
auto ans = uncons("G4143");
std::vector<std::pair<char, std::string> > vec{ans};
//If I remove the comments below the code fails to compile (GNU's g++14))
/*std::copy
(
vec.begin(),
vec.end(),
std::ostream_iterator<std::pair<char, std::string> >(std::cout, "\n")
);*/
return 0;
}
/*
In file included from /usr/include/c++/5/iterator:66:0,
from main.cpp:6:
/usr/include/c++/5/bits/stream_iterator.h: In instantiation of ‘std::ostream_iterator<_Tp, _CharT, _Traits>& std::ostream_iterator<_Tp, _CharT, _Traits>::operator=(const _Tp&) [with _Tp = std::pair<char, std::__cxx11::basic_string<char> >; _CharT = char; _Traits = std::char_traits<char>]’:
/usr/include/c++/5/bits/stl_algobase.h:340:18: required from ‘static _OI std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m(_II, _II, _OI) [with _II = std::pair<char, std::__cxx11::basic_string<char> >*; _OI = std::ostream_iterator<std::pair<char, std::__cxx11::basic_string<char> > >]’
/usr/include/c++/5/bits/stl_algobase.h:402:44: required from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = std::pair<char, std::__cxx11::basic_string<char> >*; _OI = std::ostream_iterator<std::pair<char, std::__cxx11::basic_string<char> > >]’
/usr/include/c++/5/bits/stl_algobase.h:438:45: required from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = __gnu_cxx::__normal_iterator<std::pair<char, std::__cxx11::basic_string<char> >*, std::vector<std::pair<char, std::__cxx11::basic_string<char> > > >; _OI = std::ostream_iterator<std::pair<char, std::__cxx11::basic_string<char> > >]’
/usr/include/c++/5/bits/stl_algobase.h:471:8: required from ‘_OI s/stream_iterator.h:198:13: note: ‘const std::pair<char,
....
std::__cxx11::basic_string<char> >’ is not derived from ‘const std::piecewise_constant_distribution<_RealType>’
*_M_stream << __value;
^
In file included from /usr/include/c++/5/random:51:0,
from /usr/include/c++/5/bits/stl_algo.h:66,
from /usr/include/c++/5/algorithm:62,
from main.cpp:7:
/usr/include/c++/5/bits/random.tcc:3160:5: note: candidate: template<class _RealType, class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::piecewise_linear_distribution<_RealType>&)
operator<<(std::basic_ostream<_CharT, _Traits>& __os,
^
/usr/include/c++/5/bits/random.tcc:3160:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/5/iterator:66:0,
from main.cpp:6:
/usr/include/c++/5/bits/stream_iterator.h:198:13: note: ‘const std::pair<char, std::__cxx11::basic_string<char> >’ is not derived from ‘const std::piecewise_linear_distribution<_RealType>’
*_M_stream << __value;
*/
Now if I wrap the std::string part of the std::pair in a simple class (MyString) then everything works as expected. The code below compiles without issue and works as expected.
#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include <iterator>
#include <algorithm>
class MyString {
public:
MyString(const std::string & s):str(s) {}
std::string str;
};
std::pair<char, MyString> uncons(const std::string & s) {
std::string::size_type len = s.length();
if (len == 1) {
return std::make_pair(s[0], MyString(""));
} else if (len > 1) {
return std::make_pair(s[0], MyString(s.substr(1)));
}
throw "Ooops, empty string";
}
std::ostream& operator<<(std::ostream & out, const std::pair<char, MyString> & p) {
if (p.second.str.length() > 0) {
out << "(\'" << p.first << "\', \"" << p.second.str << "\")";
}
return out;
}
int main(int argc, char ** argv) {
auto ans = uncons("G4143");
std::vector<std::pair<char, MyString> > vec{ans};
std::copy
(
vec.begin(),
vec.end(),
std::ostream_iterator<std::pair<char, MyString> >(std::cout, "\n")
);
return 0;
}
Can anyone shed some light on why this is happening? I modified my post as suggested and tried to enclose the error message in full but the forum won't allow the entire message the compiler barfed out.
As always, it's ADL.
operator<< is found via ADL, so in associated namespaces. Since the associated namespaces for std::string are merely namespace ::std (which declares it and the traits template parameter type argument) you would have to define the operator<< overload in that namespace.
In fact if you hack it and force your overload into the std namespace, it "works": http://coliru.stacked-crooked.com/a/7c2486ae963c2a64. Caution It is not legal to do this.
Once you wrap in a custom type, that type's declaring namespace participates in the ADL lookup.
Extra My usual take on providing custom streaming for primitives or non-user-defined types (optionally generically) is to create a manipulator to do it, see e.g. Template specialization (boost::lexical_cast)

Fill std::map with std::generate_n

I'd like to fill a std::map using std::generate_n but can't get it to work. What I tried is something along these lines:
unsigned number_of_pairs{5};
std::map<std::string, std::string> my_map;
auto read_pair_from_input = [](){
std::string key;
std::getline(std::cin, key);
std::string value;
std::getline(std::cin, value);
return std::make_pair(key, value);
};
std::generate_n(my_map.begin(), number_of_pairs, read_pair_from_input);
This gives me long errors like:
In file included from /opt/wandbox/gcc- head/include/c++/8.0.0/algorithm:62:0,
from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algo.h: In instantiation of '_OIter std::generate_n(_OIter, _Size, _Generator) [with _OIter = std::_Rb_tree_iterator<std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > >; _Size = unsigned int; _Generator = main()::<lambda()>]':
prog.cc:18:74: required from here
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algo.h:4468:11: error: use of deleted function 'std::pair<_T1, _T2>& std::pair<_T1, _T2>::operator= (typename std::conditional<std::__not_<std::__and_<std::is_copy_assignable<_Tp>, std::is_copy_assignable<_T2> > >::value, const std::pair<_T1, _T2>&, const std::__nonesuch_no_braces&>::type) [with _T1 = const std::__cxx11::basic_string<char>; _T2 = std::__cxx11::basic_string<char>; typename std::conditional<std::__not_<std::__and_<std::is_copy_assignable<_Tp>, std::is_copy_assignable<_T2> > >::value, const std::pair<_T1, _T2>&, const std::__nonesuch_no_braces&>::type = const std::pair<const std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&]'
*__first = __gen();
~~~~~~~~~^~~~~~~~~
In file included from /opt/wandbox/gcc-head/include/c++/8.0.0/utility:70:0,
from /opt/wandbox/gcc-head/include/c++/8.0.0/algorithm:60,
from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_pair.h:378:7: note: declared here
operator=(typename conditional<
^~~~~~~~
Is it possible to fill a std::map with std::generate_n?
What you want is a std::inserter:
std::generate_n(std::inserter(my_map, my_map.begin()), number_of_pairs, read_pair_from_input);
The inserter will wrap your map into an iterator-like construct that std::generate_n can use
Demo
std::generate_n can be implemented like
template< class OutputIt, class Size, class Generator >
OutputIt generate_n( OutputIt first, Size count, Generator g )
{
for( Size i = 0; i < count; i++ ) {
*first++ = g();
}
return first;
}
As you can see it tries to assign the result of the generator to the iterator. This does not work with associative containers as you cannot modify the key as that would break the structure of the container.
What you need is different type of iterator, namely a std::insert_iterator that you can get using std::inserter like
std::generate_n(std::inserter(my_map, my_map.begin()), number_of_pairs, read_pair_from_input);

error: no match for 'operator[]' in... <near match>

This fail to compile in gcc 4.1.2 / RedHat 5 :
#include <string>
#include <vector>
#include <map>
class Toto {
public:
typedef std::string SegmentName;
};
class Titi {
public:
typedef Toto::SegmentName SegmentName; // import this type in our name space
typedef std::vector<SegmentName> SegmentNameList;
SegmentNameList segmentNames_;
typedef std::map<SegmentName, int> SegmentTypeContainer;
SegmentTypeContainer segmentTypes_;
int getNthSegmentType(unsigned int i) const {
int result = -1;
if(i < segmentNames_.size())
{
SegmentName name = segmentNames_[i];
result = segmentTypes_[ name ];
}
return result;
}
};
The error is :
error: no match for 'operator[]' in '(...)segmentTypes_[name]'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_map.h:340:
note: candidates are: _Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&)
[with _Key = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Tp = int, _Compare = std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _Alloc = std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> >]
Why ? The map is rather straightforward. I guess this has to do with the typedefs, but what is wrong ?
[edit] Even if I remove all the typedefs and use std::string everywhere, the problem persists... Am I misusing maps ?
std::map::operator[] is non-const and you're trying to use it from a const method.
You could achieve this using std::map::find, which returns a const_iterator:
SegmentTypeContainer::const_iterator iter = segmentTypes_.find(name);
If you're using C++11, you could also use std::map::at, which will throw an exception if the key is not found in the map:
result = segmentTypes_.at(name);
std::map::operator[] is not a const method, but you are calling it from a const method of your class. The reason for this is that it adds an element if the key is not present.
You can use C++11 at():
result = segmentTypes_.at(name); // throws exception if key not present.
or use std::map::find.
SegmentTypeContainer::const_iterator it = segmentTypes_.find(name);
if (it != segmentTypes_.end())
{
// OK, element with key name is present
result = it->second;
}

Use boost::tokenizer with boost::iterator_range

I'm using boost::tokenizer to read a CSV-like file. I'm storing the the tokens in a std::vector. It works well, but I want to store only a boost::iterator for each token.
I tried:
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/range/iterator_range.hpp>
typedef std::string::const_iterator string_iter;
typedef boost::iterator_range<string_iter> string_view;
int main(){
std::string line;
std::vector<string_view> contents;
boost::tokenizer<boost::escaped_list_separator<char>, string_iter, string_view> tok(line.begin(), line.end());
contents.assing(tok.begin(), tok.end());
}
But it fails to compile:
/usr/include/boost/token_functions.hpp: In instantiation of ‘bool
boost::escaped_list_separator::operator()(InputIterator&, InputIterator, Token&) [with
InputIterator = __gnu_cxx::__normal_iterator >; Token =
boost::iterator_range<__gnu_cxx::__normal_iterator > >; Char = char; Traits =
std::char_traits]’: /usr/include/boost/token_iterator.hpp:70:11:
required from ‘void boost::token_iterator::initialize() [with TokenizerFunc =
boost::escaped_list_separator; Iterator =
__gnu_cxx::__normal_iterator >; Type = boost::iterator_range<__gnu_cxx::__normal_iterator > >]’
/usr/include/boost/token_iterator.hpp:77:63: required from
‘boost::token_iterator::token_iterator(TokenizerFunc, Iterator, Iterator) [with
TokenizerFunc = boost::escaped_list_separator; Iterator =
__gnu_cxx::__normal_iterator >; Type = boost::iterator_range<__gnu_cxx::__normal_iterator > >]’ /usr/include/boost/tokenizer.hpp:86:53:
required from ‘boost::tokenizer::iter
boost::tokenizer::begin() const [with
TokenizerFunc = boost::escaped_list_separator; Iterator =
__gnu_cxx::__normal_iterator >; Type = boost::iterator_range<__gnu_cxx::__normal_iterator > >; boost::tokenizer::iter =
boost::token_iterator,
__gnu_cxx::__normal_iterator >, boost::iterator_range<__gnu_cxx::__normal_iterator > > >]’
/home/wichtounet/dev/gooda-to-afdo-converter/src/gooda_reader.cpp:58:37:
required from here /usr/include/boost/token_functions.hpp:187:16:
error: no match for ‘operator+=’ in ‘tok += (&
next)->__gnu_cxx::__normal_iterator<_Iterator,
_Container>::operator* >()’ /usr/include/boost/token_functions.hpp:193:11: error: no match for
‘operator+=’ in ‘tok += (&
next)->__gnu_cxx::__normal_iterator<_Iterator,
_Container>::operator* >()’ /usr/include/boost/token_functions.hpp: In instantiation of ‘void
boost::escaped_list_separator::do_escape(iterator&,
iterator, Token&) [with iterator = __gnu_cxx::__normal_iterator >; Token =
boost::iterator_range<__gnu_cxx::__normal_iterator > >; Char = char; Traits =
std::char_traits]’:
/usr/include/boost/token_functions.hpp:176:11: required from ‘bool
boost::escaped_list_separator::operator()(InputIterator&, InputIterator, Token&) [with
InputIterator = __gnu_cxx::__normal_iterator >; Token =
boost::iterator_range<__gnu_cxx::__normal_iterator > >; Char = char; Traits =
std::char_traits]’ /usr/include/boost/token_iterator.hpp:70:11:
required from ‘void boost::token_iterator::initialize() [with TokenizerFunc =
boost::escaped_list_separator; Iterator =
__gnu_cxx::__normal_iterator >; Type = boost::iterator_range<__gnu_cxx::__normal_iterator > >]’
/usr/include/boost/token_iterator.hpp:77:63: required from
‘boost::token_iterator::token_iterator(TokenizerFunc, Iterator, Iterator) [with
TokenizerFunc = boost::escaped_list_separator; Iterator =
__gnu_cxx::__normal_iterator >; Type = boost::iterator_range<__gnu_cxx::__normal_iterator > >]’ /usr/include/boost/tokenizer.hpp:86:53:
required from ‘boost::tokenizer::iter
boost::tokenizer::begin() const [with
TokenizerFunc = boost::escaped_list_separator; Iterator =
__gnu_cxx::__normal_iterator >; Type = boost::iterator_range<__gnu_cxx::__normal_iterator > >; boost::tokenizer::iter =
boost::token_iterator,
__gnu_cxx::__normal_iterator >, boost::iterator_range<__gnu_cxx::__normal_iterator > > >]’
/home/wichtounet/dev/gooda-to-afdo-converter/src/gooda_reader.cpp:58:37:
required from here /usr/include/boost/token_functions.hpp:130:9:
error: no match for ‘operator+=’ in ‘tok += '\012'’
/usr/include/boost/token_functions.hpp:134:9: error: no match for
‘operator+=’ in ‘tok += (&
next)->__gnu_cxx::__normal_iterator<_Iterator,
_Container>::operator* >()’ /usr/include/boost/token_functions.hpp:138:9: error: no match for
‘operator+=’ in ‘tok += (&
next)->__gnu_cxx::__normal_iterator<_Iterator,
_Container>::operator* >()’ /usr/include/boost/token_functions.hpp:142:9: error: no match for
‘operator+=’ in ‘tok += (&
next)->__gnu_cxx::__normal_iterator<_Iterator,
_Container>::operator* >()’
I also simply tried to compute the two iterators by myself using the boost::token_iterator, but I haven't been successful so far.
Is there a solution to get only the iterator range of each token instead of the string in order to save some performances ?
This can't work. The tokenizer expects a type (the third template argument) which can be appended with the results of the tokenizer function. Specifically, it must provide the operator += ( tokenizer<...>::iterator::value_type ). The code snippet below should take you a step further, though I am not sure if it's worth the effort...
#include <string>
#include <boost/tokenizer.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>
#include <cstddef>
typedef std::string::const_iterator string_iter;
typedef boost::iterator_range<string_iter> string_view;
// a constant size character buffer, skips anything beyond CSize...
template< std::size_t CSize >
class assignable_view {
std::size_t m_size;
char m_buffer[CSize];
friend std::ostream& operator << (std::ostream& p_out, assignable_view const & p_view)
{
if (p_view.m_size > 0u) {
std::copy(p_view.m_buffer, p_view.m_buffer + p_view.m_size, std::ostream_iterator<char>(p_out));
}
return p_out;
}
public:
template <class TIter>
void operator += (TIter p_input)
{
if (m_size < CSize) {
m_buffer[m_size++] = p_input;
}
}
assignable_view()
: m_size(0u) {}
};
int main(){
std::string line
= "Field 1,\"putting quotes around fields, allows commas\",Field 3";
std::vector<string_view> contents;
boost::tokenizer<
boost::escaped_list_separator<char>,
string_iter,
assignable_view<11>
> tok(line.begin(), line.end());
for (auto const & t_s : tok) {
std::cout << t_s << std::endl;
}
//contents.assing(tok.begin(), tok.end());
}
Ah! You'd need an include:
#include <iostream>
#include <boost/tokenizer.hpp>
#include <boost/range/iterator_range.hpp>
#include <string>
int main()
{
std::string line;
typedef std::string::const_iterator string_iter;
typedef boost::iterator_range<string_iter> string_view;
boost::tokenizer<boost::escaped_list_separator<char>, string_iter, string_view> tok(line.begin(), line.end());
}
compiles fine

Is it possible to use STL copy function with map

I wonder if there is any trick to use copy with maps to copy the contents of map into an array. Because STL maps are by the combination of a key value and a mapped value an element of a map forms a key value pair. That prevents us to use standard algorithms like std::copy. For example following code gives error:
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
int
main()
{
std::map <int, double> test(4);
test[0] = 11;
test[2] = 1.23;
test[3] = 23.29;
test[1] = 12.12;
double *test_arr = (double *) malloc(4 * sizeof(double));
std::copy(test.begin(), test.end(), test_arr);
std::cout << test_arr[3] << std::endl;
return 0;
}
Error:
stl_copy_tests.cpp: In function ‘int main()’:
stl_copy_tests.cpp:9:32: error: no matching function for call to ‘std::map<int, double>::map(int)’
/usr/include/c++/4.5/bits/stl_map.h:170:7: note: candidates are: std::map<_Key, _Tp, _Compare, _Alloc>::map(const std::map<_Key, _Tp, _Compare, _Alloc>&) [with _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, double> >, std::map<_Key, _Tp, _Compare, _Alloc> = std::map<int, double>]
/usr/include/c++/4.5/bits/stl_map.h:159:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::map(const _Compare&, const allocator_type&) [with _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, double> >, allocator_type = std::allocator<std::pair<const int, double> >]
/usr/include/c++/4.5/bits/stl_map.h:150:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::map() [with _Key = int, _Tp = double, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, double> >]
In file included from /usr/include/c++/4.5/bits/char_traits.h:41:0,
from /usr/include/c++/4.5/ios:41,
from /usr/include/c++/4.5/ostream:40,
from /usr/include/c++/4.5/iostream:40,
from stl_copy_tests.cpp:1:
/usr/include/c++/4.5/bits/stl_algobase.h: In static member function ‘static _OI std::__copy_move<<anonymous>, <anonymous>, <template-parameter-1-3> >::__copy_m(_II, _II, _OI) [with _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*, bool <anonymous> = false, bool <anonymous> = false, <template-parameter-1-3> = std::bidirectional_iterator_tag]’:
/usr/include/c++/4.5/bits/stl_algobase.h:404:70: instantiated from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false, _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*]’
/usr/include/c++/4.5/bits/stl_algobase.h:442:39: instantiated from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false, _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*]’
/usr/include/c++/4.5/bits/stl_algobase.h:474:18: instantiated from ‘_OI std::copy(_II, _II, _OI) [with _II = std::_Rb_tree_iterator<std::pair<const int, double> >, _OI = double*]’
stl_copy_tests.cpp:15:47: instantiated from here
/usr/include/c++/4.5/bits/stl_algobase.h:319:6: error: cannot convert ‘std::pair<const int, double>’ to ‘double’ in assignment
Is there any easy trick/hack to overcome this problem.
Disclaimer: Not interested in solutions that iterates over map in a for loop and adds elements to the array.
You could use std::transform instead:
template <typename T, typename U>
const U &extract_second(const std::pair<T,U> &p)
{
return p.second;
}
std::transform(test.begin(), test.end(), test_arr, extract_second<int,double>);
And as #Andre points out in a comment below, if you want a slightly more verbose overhead, you can avoid having to explicitly state the template arguments via a functor:
struct extract_second
{
template <typename T, typename U>
const U operator() (const std::pair<T,U> &p) const
{
return p.second;
}
};
std::transform(test.begin(), test.end(), test_arr, extract_second());
I'm sure there's a less-verbose solution using Boost binders, but I can't remember the syntax off the top of my head.
Ewww, malloc? Anyway, if you want to copy a map, you have to remember the keys too.
int main()
{
std::map <int, double> test(4);
test[0] = 11;
test[2] = 1.23;
test[3] = 23.29;
test[1] = 12.12;
std::vector<std::pair<int, double>> test_arr(test.size());
std::copy(test.begin(), test.end(), test_arr.begin());
std::cout << test_arr[3] << std::endl;
return 0;
}
If you consider std::map an STL container, then it is a container of
std::pair<key_type, mapped_type>. (This is what its value_type is
defined to be, and it is designed so that it can be used as a
container.) If you want simply one part of it, the correct function is
std::transform, with a transformation function which maps the
value_type to either the key_type or the mapped_type. (If you make
much use of std::pair—or std::map, whose value_type is an
std::pair, you should probably have functional objects for this in
your tool kit:
struct ExtractFirst
{
template<typename Pair>
typename boost::remove_const<typename Pair::first_type>::type
operator()( Pair const& from ) const
{
return from.first;
}
};
, and the same thing for ExtractSecond.
Your target would be an arraystd::vector[please!] of std::pair<int,double> objects unless, yes, you unroll it yourself.
(You could create your own InputIterator as a proxy, or play with std::transform and a std::back_inserter, but that's just being silly. You'll make your code far more verbose than just looping through the map.)
The simplest way is to use std::transform in combination with boost::bind:
typedef std::map<int, double> map_t;
map_t mm;
// add elements to mm
// ...
// copy
typedef std::vector<double> vec_t;
vec_t vv;
vv.reserve( mm.size() );
std::transform( mm.begin(), mm.end(), std::back_inserter(vv),
boost::bind( &map_t::value_type::second, _1 ) );
If you could use C++0x (without boost):
std::transform( mm.begin(), mm.end(), back_inserter(vv),
[](map_t::value_type val) -> double { return val.second; } );
// or
std::for_each( mm.begin(), mm.end(),
[&vv](map_t::value_type val) { vv.push_back( val.second ); } );