boost lambda with a vector of shared pointers - c++

Below is a slightly modified code from one good example how to copy values fro one vector of strings to another vector of objects. (see: another copy algorithm )
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
using boost::shared_ptr;
using boost::make_shared;
using namespace boost::lambda;
class Object
{
public:
std::string Name;
Object(const std::string& Name_ = "")
: Name(Name_)
{
}
};
int main()
{
//std::vector<Object> objects(3, Object());
std::vector<shared_ptr<Object> > objects(3, make_shared<Object>());
std::vector<std::string> names;
names.push_back("Alpha");
names.push_back("Beta");
names.push_back("Gamma");
std::vector<std::string>::const_iterator names_it;
names_it = static_cast<const std::vector<std::string>&>(names).begin();
//std::for_each(objects.begin(), objects.end(), bind(&Object::Name, _1) = *var(names_it)++);
std::for_each(objects.begin(), objects.end(), bind(&Object::Name, *_1) = *var(names_it)++);
//std::vector<Object>::iterator it, end = objects.end();
std::vector<shared_ptr<Object> >::iterator it, end = objects.end();
for (it = objects.begin(); it != end; ++it) {
//std::cout << it->Name << std::endl;
std::cout << (*it)->Name << std::endl;
}
return EXIT_SUCCESS;
}
In this case I'm using dynamically allocated Objects, and because boost::lambda::bind can't handle such changes as boost::bind do, I need to dereference placeholder in order to compile:
std::for_each(objects.begin(), objects.end(), bind(&Object::Name, *_1) = *var(names_it)++);
But then at the output I've got:
Gamma
Gamma
Gamma
What is your explanation?

std::vector<shared_ptr<Object> > objects(3, make_shared<Object>());
This is equivalent to doing:
shared_ptr<Object> object = make_shared<Object>();
std::vector<shared_ptr<Object> > objects(3, object);
The vector constructor will then make 3 copies of that object pointer, which will all refer to same one and unique Object (that is to say, *object). Fill the vector with separate pointers each initialized with their own Object.

Related

How can I convert a vector of ints to be a vector of strings using STL algorithms?

For example, I would need {20,6,143} to become {"20","6","143"}.
Simply using a range-based for-loop you could iterate through the vector of integers and using std::to_string you could convert each element to string and store to the new vector of strings.
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<int> vec{ 20,6,143 };
std::vector<std::string> strVec;
strVec.reserve(vec.size()); // reserve memory for unwanted reallocations
for (int element : vec)
strVec.emplace_back(std::to_string(element));
for (const auto& str : strVec)
std::cout << str << " ";
}
(See live online)
Alternatively using std::transform, you could transform the vector of integers as follows.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm> // std::transform
#include <iterator> // std::cbegin
int main()
{
std::vector<int> vec{ 20,6,143 };
std::vector<std::string> strVec;
strVec.reserve(vec.size()); // reserve memory for unwanted reallocations
std::transform(std::cbegin(vec), std::cend(vec),
std::back_inserter(strVec), // insert to the back of strVec
[](const int element) { return std::to_string(element);}
);
for (const auto& str : strVec)
std::cout << str << " ";
}
(See live online)
std::transform combined with std::to_string should do the trick.

Iterator on a map never atteins map.end()

why the iteration in the code below doesn't work ? I mean the iterator is incremented but evaluating this expression o!=RateCurve.end() always give true.
i need such a function because i'm using it in a wrapper of a map to contruct an interest rate curve.
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string>
#include <map>
#include <exception>
#include <vector>
#include <stdio.h>
using namespace std;
map<double, double>::iterator getIt(map<double, double> o){
return o.begin();
}
int main ()
{
map<double, double> RateCurve;
RateCurve[3.3 ]=0.034 ;
RateCurve[1.2 ]=0.03 ;
RateCurve[0.2 ]=.001 ;
RateCurve[6.1 ]=.023 ;
map<double, double>::iterator o=getIt(RateCurve);
while (o!=RateCurve.end()){
cout << "RateCurve[" << o->first << "] = " << o->second << endl;
++o;
}
}
getIt(map<double, double> o)
You copy the map, so the iterator returned points into an entirely unrelated map to the one you wanted. Worse, the copy gets destroyed at the end of the function call, and the iterator you try to use is no longer valid, so you have Undefined Behaviour.
Make getIt take a reference, and since you don't actually change the elements, you can make it const too. Then you need to change the return type:
std::map<double, double>::const_iterator
getIt(map<double, double> const& o)
{
return o.begin();
}
Also, please reconsider your use of bad practices using namespace std; and endl.

How to register member functions in map c++

I am trying to create a map to hold functions that can be registered and fired. I cannot seem to get the correct bind / function / pointer syntax in order to get this compiling properly.
Here is what I have: I have tried both boost::bind and boost:
#include <cstdlib>
#include <iostream>
#include <boost/bind/bind.hpp>
#include <boost/function.hpp>
#include <map>
using namespace std;
typedef const std::string& listenArg;
typedef void (*Actions)(listenArg str);
std::multimap<int, Actions> functions;
// fire in the hole!
void fire(int methods, listenArg arg0) {
std::multimap<int, Actions>::iterator function = functions.find(methods);
typedef std::pair<int, Actions> pear;
for (function = functions.begin(); function != functions.end(); ++function) {
(*(function->second))(arg0);
}
}
void listen1(listenArg arg0) {
std::cout << "listen1 called with " << arg0 << std::endl;
}
class RegisteringClass {
public:
RegisteringClass();
virtual ~RegisteringClass();
void callMeBaby(listenArg str) {
std::cout << "baby, i was called with " << str << std::endl;
}
};
int main(int argc, char** argv) {
const int key = 111;
functions.insert(make_pair<int, Actions>(key, listen1));
fire(key, "test");
// make a registeringClass
RegisteringClass reg;
// register call me baby
boost::function<void (listenArg) >
fx(boost::bind(&RegisteringClass::callMeBaby, reg, _1));
//std::bind(&RegisteringClass::callMeBaby, reg, _1);
functions.insert(
make_pair<int, Actions> (key, fx));
// fire
fire(key, "test2");
return 0;
}
Thanks for any help!
typedef boost::function < void (listenArg) > Actions;
Should be used instead of function pointer.
The problem is that you're telling the compiler that Actions is a non-member function pointer, and then you try to put a boost::function into a variable of that type. They're two totally unrelated types and such an assignment can't happen. You need to make your Actions typedef be a boost::function<void (listenArg)> instead.
you can use boost::function template
#include <cstdlib>
#include <iostream>
#include <boost/bind/bind.hpp>
#include <boost/function.hpp>
#include <map>
using namespace std;
typedef const std::string& listenArg;
typedef boost::function < void (listenArg) > Actions;
std::multimap<int, Actions> functions;

how to bind a template function

Hi to all you boost gurus out there!
I want to find a certain element in a vector of strings, ignoring case:
#include <iostream>
#include <string>
#include <vector>
#include "boost/algorithm/string.hpp"
#include "boost/bind.hpp"
using std::string;
using std::vector;
bool icmp(const string& str1, const string& str2)
{
return boost::iequals(str1, str2);
}
int main(int argc, char* argv[])
{
vector<string> vec;
vec.push_back("test");
// if (std::find_if(vec.begin(), vec.end(), boost::bind(&boost::iequals<string,string>, "TEST", _1)) != vec.end()) <-- does not compile
if (std::find_if(vec.begin(), vec.end(), boost::bind(&icmp, "TEST", _1)) != vec.end())
std::cout << "found" << std::endl;
return 0;
}
This is working fine so far, but what I would like to know is, if it is possible to get rid of the extra function (icmp()) and invoke iequals (template function) directly (like in the commented line).
Thanks in advance!
Adding the template params and the default locale parameter works on my machine.
if (std::find_if(vec.begin(), vec.end(), boost::bind(&boost::iequals<string,string>, "TEST", _1, std::locale())) != vec.end())
std::cout << "found" << std::endl;
Compiler is VS2010.
I'm sure this is not what you're hoping for, but this appears to be the only fix I can work out (with g++ c++03 mode):
typedef bool (*fptr)(const std::string&, const std::string&);
if (std::find_if(vec.begin(), vec.end(),
boost::bind((fptr) &boost::iequals<string,string>, "TEST", _1)
) != vec.end())
Using boost lambda :)
#include <iostream>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <algorithm>
#include <vector>
using namespace std;
using namespace boost::lambda;
int main()
{
vector<string> vec;
vec.push_back("TEST");
vec.push_back("test");
vector<string>::iterator it;
it = find_if(vec.begin(),vec.end(),_1 == "test");
cout << *it << endl;
return 0;
}

Populate a vector with all multimap values with a given key

Given a multimap<A,B> M what's a neat way to create a vector<B> of all values in M with a specific key.
e.g given a multimap how can I get a vector of all strings mapped to the value 123?
An answer is easy, looping from lower->upper bound, but is there a neat loop-free method?
Here's the way to do it STL style :
// The following define is needed for select2nd with DinkumWare STL under VC++
#define _HAS_TRADITIONAL_STL 1
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <map>
#include <iterator>
#include <iostream>
using namespace std;
void main()
{
typedef multimap<string, int> MapType;
MapType m;
vector<int> v;
// Test data
for(int i = 0; i < 10; ++i)
{
m.insert(make_pair("123", i * 2));
m.insert(make_pair("12", i));
}
MapType::iterator i = m.lower_bound("123");
MapType::iterator j = m.upper_bound("123");
transform(i, j, back_inserter(v), select2nd<MapType::value_type>());
copy(v.begin(), v.end(), ostream_iterator<int>(cout, ","));
}
Let's go lambda
given: multimap<A,B> M
requested: vector<B> (of all values in M with a specific key 'a'.)
method:
std::pair<M::iterator, M::iterator> aRange = M.equal_range('a')
std::vector<B> aVector;
std::transform(aRange.first, aRange.second,std::back_inserter(aVector), [](std::pair<A,B> element){return element.second;});
System environment:
compiler: gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 (with -std=c++11)
os: ubuntu 16.04
Code example:
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <iostream>
int main()
{
typedef std::multimap<std::string, int> MapType;
MapType m;
std::vector<int> v;
/// Test data
for(int i = 0; i < 10; ++i)
{
m.insert(std::make_pair("123", i * 2));
m.insert(std::make_pair("12", i));
}
std::pair<MapType::iterator,MapType::iterator> aRange = m.equal_range("123");
std::transform(aRange.first, aRange.second, std::back_inserter(v), [](std::pair<std::string,int> element){return element.second;});
for(auto & elem: v)
{
std::cout << elem << std::endl;
}
return 0;
}
You need a loop anyway. All "loop-free" methods just abstract the loop away.
#include <map>
#include <vector>
#include <algorithm>
#include <ext/functional>
using namespace std;
int main () {
multimap<int, double> mm;
mm.insert(make_pair(1, 2.2));
mm.insert(make_pair(4, 2.6));
mm.insert(make_pair(1, 9.1));
mm.insert(make_pair(1, 3.1));
vector<double> v;
transform(mm.lower_bound(1), mm.upper_bound(1),
back_inserter(v), __gnu_cxx::select2nd<pair<int, double> >());
// note: select2nd is an SGI extension.
for (vector<double>::const_iterator cit = v.begin(); cit != v.end(); ++ cit)
printf("%g, ", *cit); // verify that you've got 2.2, 9.1, 3.1
return 0;
}
template <class Key, class Val>
vector<Val>& getValues(multimap<Key, Val>& multi, Key& key)
{
typedef multimap<Key, Val>::iterator imm;
static vector<Val> vect;
static struct
{
void operator()(const pair<Key, Val>& p) const
{
vect.push_back(p.second);
}
} Push;
vect.clear();
pair<imm, imm> range = multi.equal_range(key);
for_each(range.first, range.second, Push);
return vect;
}
This is a bit contrived because of your 'no loop' requirement.
I prefer:
template <class Key, class Val>
vector<Val> getValues(multimap<Key, Val>& map, Key& key)
{
vector<Val> result;
typedef multimap<Key, Val>::iterator imm;
pair<imm, imm> range = map.equal_range(key);
for (imm i = range.first; i != range.second; ++i)
result.push_back(i->second);
return result;
}
You could initialise the vector by giving it two iterators, like this:
std::multimap<std::string, std::string> bar;
...
std::vector<pair<string,string> > foo(bar.lower_bound("123"), bar.upper_bound("123"));
but that would give you a vector of pairs (ie, with both the key and value).
Another option would be to use std::copy with something like a back_inserter, which is another way to hide the loop, but with the same downside as above.
std::copy(bar.lower_bound("123"), bar.upper_bound("123"), std::back_inserter(foo));
This would append the elements (if any) to the vector foo.
For extracting the values only, I can't think of any way but to loop over the results as I'm not aware of a standard way to get only the value out of a range.
Just some addenda to the other answers hereā€¦
std::mem_fn (from #include <functional>) can be used as a shorthand for the transform operator:
// previously we might've used this longhand
[](pair<int,string> element){return element.second;}
And we can use vector::resize and std::distance to allocate space for the vector in one go, rather than repeatedly resizing it with back_inserter.
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <iterator>
#include <iostream>
using namespace std;
typedef multimap<int, string> MapType;
int main()
{
MapType multimap;
vector<string> valuesForKey123;
multimap.emplace(0, "red");
multimap.emplace(123, "hello");
multimap.emplace(123, "world");
multimap.emplace(0, "herring");
MapType::iterator lower{multimap.lower_bound(123)};
MapType::iterator upper{multimap.upper_bound(123)};
valuesForKey123.resize(distance(lower, upper));
transform(
lower,
upper,
valuesForKey123.begin(),
mem_fn(&MapType::value_type::second));
copy(
valuesForKey123.begin(),
valuesForKey123.end(),
ostream_iterator<string>(cout, " "));
}
// outputs "hello world "