I have a collection of a certain struct, and I want to join a string member of each of these structs into a semicolon-separated list. Making good use of the standard algorithms library and Boost, the problem breaks down easily:
std::transform
to get a collection of the strings, thenboost::algorithm::join
to join them together.However, making a copy of the strings just to feed them to join
is silly, so I attempted to use a reference_wrapper
around the strings as the output for std::transform
.
The corresponding code can be seen here:
struct IPAddress {
vector<uint8_t> binaryValue;
string stringValue;
}
// ---snip---
vector<IPAddress> addresses;
// Populate and manipulate the addresses vector
// ---snip---
// Join the list.
vector<reference_wrapper<string>> addressStrings(addresses.size());
transform(begin(addresses), end(addresses), begin(addressStrings),
[](const IPAddress& addr) {
return ref(addr.stringValue); // Expodes
});
return boost::algorithm::join(addressStrings, ";"); // Also explodes
The lambda provided to std::transform
fails to compile, claiming that there is no match for the assignment operator between two reference_wrapper<string>
s, despite that being documented and the entire point of a reference_wrapper
.
Then, if I comment out the transform
call, boost::algorithm::join
fails anyways by trying to call a default constructor of reference_wrapper
(which has none) instead of string
.
Am I missing something straightforward, or should I just give up and use a for
loop with iterators?
With a little help from boost ranges you can have your cake and eat it.
It removes the sting from this mix: the complication is that you want to use an intermediate collection of std::string const&
but, obviously that doesn't work with std::vector
.
So, we drop the intermediate collection, instead using an adapted view (boost::adaptors::transformed
) and, no need for a lambda either[¹], just use std::mem_fun
: See it Live On Coliru
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptors.hpp>
#include <vector>
#include <string>
#include <functional>
struct IPAddress {
std::vector<uint8_t> binaryValue;
std::string stringValue;
};
std::string foo(std::vector<IPAddress> const& addresses)
{
using namespace boost::adaptors;
return boost::algorithm::join(
addresses | transformed(std::mem_fn(&IPAddress::stringValue)),
";");
}
#include <iostream>
int main()
{
std::vector<IPAddress> const addresses {
{ {}, "test1" },
{ {}, "test2" },
};
std::cout << foo(addresses);
}
Prints
test1;test2
[¹] unless stringValue
was an overloaded member function