I have the following small and easy code:
int main(int argc, char *argv[]) {
std::vector<std::string> in;
std::set<std::string> out;
in.push_back("Hi");
in.push_back("Dear");
in.push_back("Buddy");
for (const auto& item : in) {
*** std::transform(item.begin(),item.end(),item.begin(), ::tolower);
*** out.insert(item);
}
return 0;
}
I'd like to copy all items of in into out.
However, with an in-place lowercase conversion, preferrably without an extra temporary variable.
So this is the required content of out at the end:
hi
dear
buddy
Please note, const auto& item is fixed, meaning I can't remove the const requirement (this is part of a bigger library, here is over-simplified for demo).
How should I do this? (If I remove the "const" modifier, it works, but if modifications are not allowed on item, how can I still insert the item into the set, while also transforming it to lowercase?)
Note, you have to copy - since items in the original in container can not be moved into out container. The below code makes the copy of each element exactly once.
...
in.push_back("Hi");
in.push_back("Dear");
in.push_back("Buddy");
std::transform(in.begin(), in.end(), std::inserter(out, out.end()),
[] (std::string str) { boost::algorithm::to_lower(str); return str;}
);
return 0;
You need a lambda function with transform, and you shouldn't have a const & to your strings or transform can't modify it.
#include <algorithm>
#include <set>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> in;
std::set<std::string> out;
in.push_back("Hi");
in.push_back("Dear");
in.push_back("Buddy");
for (/*const*/ auto& item : in) // you can't have a const & if your going to modify it.
{
std::transform(item.begin(), item.end(), item.begin(), [](const char c)
{
return static_cast<char>(::tolower(c));
});
out.insert(item);
}
return 0;
}
Related
It is easy to find a string in a set of strings using set::find or first of a set of strings in a set of strings using std::find_first_of. But I think that STL doesn't handle this case of find_first_of set of strings (substrings) in a string. For low latency reasons I use parallel execution, would you please let me know if this implementation is idiomatic using modern C++ :
#include <string>
#include <list>
#include <atomic>
#include <execution>
#include <iostream>
class Intent{
const std::list<std::string> m_Context;
const std::string m_Name;
std::atomic_bool m_Found;
public:
Intent(const std::list<std::string> context, const std::string name)
: m_Context(context)
, m_Name(name)
, m_Found(false)
{}
Intent(const Intent & intent) = delete;
Intent & operator=(const Intent & intent) = delete;
Intent(Intent && intent) : m_Context(std::move(intent.m_Context))
, m_Name(std::move(intent.m_Name))
, m_Found(static_cast< bool >(intent.m_Found))
{}
bool find(const std::string & sentence)
{
for_each( std::execution::par
, std::begin(m_Context)
, std::end(m_Context)
, [& m_Found = m_Found, & sentence](const std::string & context_element){
//
// Maybe after launching thread per context_element one of them make intent Found
// so no need to run string::find in the remaining threads.
//
if(!m_Found){
if(sentence.find(context_element) != std::string::npos)
{
m_Found = true;
}
}
}
);
return m_Found;
}
const bool getFound() const {return m_Found;}
const std::string & getName() const {return m_Name;}
};
int main()
{
Intent intent({"hello", "Hi", "Good morning"}, "GREETING");
std::cout << intent.find("Hi my friend.");
}
I think the idiomatic way of doing it would be to use std::find_if. Then you don't need the atomic<bool> either.
// return iterator to found element or end()
auto find(const std::string & sentence)
{
return std::find_if( std::execution::par
, std::begin(m_Context)
, std::end(m_Context)
, [&sentence](const std::string & context_element) {
return sentence.find(context_element) != std::string::npos;
}
);
}
If you really only want a bool you could use std::any_of:
bool find(const std::string & sentence)
{
return std::any_of( std::execution::par
, std::begin(m_Context)
, std::end(m_Context)
, [&sentence](const std::string & context_element) {
return sentence.find(context_element) != std::string::npos;
}
);
}
You may want to consider using a std::vector instead of a std::list too. vectors provide random access iterators while lists only provide bidirectional iterators.
Having the following piece of code:
std::vector<int64> values;
std::copy(
std::istream_iterator<int64>(std::cin),
std::istream_iterator<int64>(),
std::back_inserter(values)
);
I'd like to make it read the input stream only until the end of line. How can I do that with std::istream_iterator?
You can't do it with std::istream_iterator.
But it is relatively easy to write an input iterator.
#include <iterator>
#include <iostream>
#include <sstream>
#include <vector>
#include <cctype>
template<typename T>
class istream_line_iterator: public std::iterator<std::input_iterator_tag, T>
{
std::istream* stream;
public:
// Creating from a stream or the end iterator.
istream_line_iterator(std::istream& s): stream(&s) {dropLeadingSpace();}
istream_line_iterator(): stream(nullptr) {}
// Copy
istream_line_iterator(istream_line_iterator const& copy): stream(copy.stream) {}
istream_line_iterator& operator=(istream_line_iterator const& copy) {stream = copy.stream;return *this;}
// The only valid comparison is against the end() iterator.
// All other iterator comparisons return false.
bool operator==(istream_line_iterator const& rhs) const {return stream == nullptr && rhs.stream == nullptr;}
bool operator!=(istream_line_iterator const& rhs) const {return !(*this == rhs);}
// Geting the value modifies the stream and returns the value.
// Note: Reading from the end() iterator is undefined behavior.
T operator*() const {T value;(*stream) >> value;return value;}
T* operator->() const; // Not sure I want to implement this.
// Input streams are funny.
// Does not matter if you do a pre or post increment. The underlying stream has changed.
// So the effect is the same.
istream_line_iterator& operator++() {dropLeadingSpace();return *this;}
istream_line_iterator& operator++(int) {dropLeadingSpace();return *this;}
private:
void dropLeadingSpace()
{
// Only called from constructor and ++ operator.
// Note calling this on end iterator is undefined behavior.
char c;
while((*stream) >> std::noskipws >> c) {
if (c == '\n') {
// End of line. So mark the iterator as reaching end.
stream = nullptr;
return;
}
if (!std::isspace(c)) {
// Found a non space character so put it back
stream->putback(c);
return;
}
}
// End of stream. Mark the iterator as reaching the end.
stream = nullptr;
}
};
int main()
{
std::stringstream s{"0 1 2 3 4 5 6 7 8 9 10\n11 12 13 14 15 16\n17 18 19"};
std::vector<int> d{istream_line_iterator<int>(s), istream_line_iterator<int>()};
for(auto v: d) {
std::cout << "V: " << v << "\n";
}
}
Running:
> g++ -std=c++17 main.cpp
> ./a.out
V: 0
V: 1
V: 2
V: 3
V: 4
V: 5
V: 6
V: 7
V: 8
V: 9
V: 10
If you want the functionality without adding a std::getline or std::stringstream, you can change the character classification of the stream so that a newline is not considered discardable whitespace. Here is a minimal example:
struct set_newline_as_ws : std::ctype<char> {
static const mask* make_table( std::ctype_base::mask m ) {
static std::vector<mask> v(classic_table(), classic_table() + table_size);
v['\n'] &= m;
return &v[0];
}
set_newline_as_ws( bool skip, std::size_t refs = 0 ) : ctype(make_table(skip ? ~space : space), false, refs) {}
};
std::istream& skipnewline( std::istream& is ) {
is.imbue(std::locale(is.getloc(), new std::ctype<char>));
return is;
}
std::istream& noskipnewline( std::istream& is ) {
is.imbue(std::locale(is.getloc(), new set_newline_as_ws(true)));
return is;
}
int main() {
std::vector<int64> values;
std::cin >> noskipnewline;
std::copy(
std::istream_iterator<int64>(std::cin),
std::istream_iterator<int64>(),
std::back_inserter(values)
);
std::cin >> skipnewline;
}
Adapted from an earlier answer to a similar question (here):
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
namespace detail
{
class Line : public std::string
{
friend std::istream & operator>>(std::istream & is, Line & line)
{
return std::getline(is, line);
}
};
}
template<class OutIt>
void read_lines(std::istream& is, OutIt dest)
{
typedef std::istream_iterator<detail::Line> InIt;
std::copy_n(InIt(is), 1, dest);
}
int main()
{
std::vector<std::string> v;
read_lines(std::cin, std::back_inserter(v));
return 0;
}
This code should take only one line and saves it into the vector v. This is thanks to the use of std::copy_n function which takes the number of elements-to-be-copied as input. There is a quirk, though, reported here. Depending on your platform, one or two lines will be read even though only the first one will be saved into v.
That being said, if you want to make it fail-safe, you can just implement your own copy_n(...) function, like so:
template<class InIt, class Range, class OutIt>
OutIt own_copy_n(InIt first, Range count, OutIt dest)
{
*dest = *first;
while (0 < --count)
*++dest = *++first;
return (++dest);
}
Then instead of using std::copy_n(...) in you code, you can use own_copy_n(...).
This way you'll be sure you'll have to input one line only, and that that line will be saved into your vector v.
It is impossible to change istream_iterator in this way, because it has no knowledge of newlines, or even of characters, for that matter. It only knows about int64(Or whatever type you instantiated it with) and if the stream ended or failed (When it then will return false on operator bool()).
This means that our point of customization must be the actual stream.
The stream object is actually just a wrapper around a std::basic_streambuf. The streambuf will spit out characters until it finds an EOF. You can simply adapt it to return an EOF once it finds a newline, then the stream will temporarily treat that as the end of stream.
Doing this is easy: The streambuf object is accessible through the member funtion rdbuf(). You can just replace the buffer with your custom one using this function. To have it continue reading after the newline when you're done, you can just return the original std::basic_streambuf back to the stream.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <streambuf>
#include <vector>
// This is our custom std::basic_streambuf object.
// We chose the underflow function as our point of customization, because
// it is there that the action is happening: it is this function that
// that is responsible for reading more data from the stream.
class newline_buf : public std::streambuf {
std::streambuf* src;
char ch; // single-byte buffer
protected:
int underflow() {
if( (ch= src->sbumpc()) == '\n') {
return traits_type::eof(); // return EOF on new line.
}
setg(&ch, &ch, &ch+1); // make one read position available
return ch; // may also return EOF by the regular means
}
public:
newline_buf(std::streambuf* buf) : src(buf) {
setg(&ch, &ch+1, &ch+1); // buffer is initially full
}
};
int main() {
// testing with a stringstream to make things easier to reproduce.
// Should work fine with any type of input stream.
std::istringstream iss(R"(12345 12345 12345
67890 67890 67890)");
// We store the original rdbuf so we can recover it later.
auto original_rdbuf = iss.rdbuf();
newline_buf buf(original_rdbuf);
iss.basic_ios::rdbuf(&buf);
// Do the copy and then recover the original rdbuf
std::copy(std::istream_iterator<int>(iss), std::istream_iterator<int>(), std::ostream_iterator<int>(std::cout, " "));
iss.basic_ios::rdbuf(original_rdbuf);
// You can try doing a new copy, just to convince yourself that the stream is still in a valid state.
//std::copy(std::istream_iterator<int>(iss), std::istream_iterator<int>(), std::ostream_iterator<int>(std::cout, " "));
}
See it live!
I have a function returning a vector of objects, and I want to create a vector of members taken from these objects. I am using std::transform to do this. However, the code segfualts. GDB wasn't very helpful. Could anyone explain what is happening?
#include <algorithm>
#include <iostream>
using namespace std;
class Container
{
private:
string _id;
public:
Container(const string &str): _id(str) {}
const decltype(_id) &id() const {
return this->_id;
}
};
Container a{"hello"}, b{"world"};
vector<Container *> fn()
{
return {&a,&b};
}
int main() {
vector<string> ids;
const auto &elements = fn();
std::transform(elements.begin(), elements.end(), ids.begin(), [](const Container *container){ return container->id();});
}
ids.begin() is not an iterator into a valid range of length elements.size(), since ids is empty and elements has size 2.
You probably want std::back_inserter(ids) instead:
std::vector<std::string> ids;
std::transform(elements.begin(), elements.end(),
std::back_inserter(ids),
[](const Container *container){ return container->id();});
assert(ids.size() == elements.size());
vector<string> ids; is an empty vector and you are trying to add elements to it using a normal iterator. This is going to cause a segfault as you are going to access memory you do not own. What you need is a back_inserter to push_back the elements into the vector.
std::transform(elements.begin(), elements.end(), std::back_inserter(ids),
[](const Container *container){ return container->id();});
I don't want just the first one, I want them all. How can I do this? I always get either nothing or the first value only and I've been converting the stringstream into a string. Is there a way to get it directly from stringstream? The ints are separated by semicolons.
You can use std::vector, and, if you know the number of ints before-hand, you could use reserve to improve performance. Since you're using semi-colons as delimiters, one solution would be to use this delimiter with std::getline:
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
int main()
{
std::stringstream ss{"1;2;3;4;5;6;7;8;9;10"};
std::vector<int> ints;
// Reserve space for 10 integers:
ints.reserve(10);
std::string temp;
while (std::getline(ss, temp, ';'))
ints.push_back(std::stoi(temp));
for (int i : ints)
std::cout << i << ' ';
return 0;
}
Thats just simply using copy and iteratrs.
template<std::size_t SIZE>
void func(std::istream& stream, std::array<int, SIZE>& arr)
{
std::copy_n(std::istream_iterator<int>(stream), SIZE, std::begin(arr));
}
But rater than thinking about arrays you should think about using iterator. That way you can use any container types.
template<typename I>
void func(std::istream& stream, I dst)
{
std::copy(std::istream_iterator<int>(stream),
std::istream_iterator<int>(),
dst);
}
You also mention vector in the comments:
void func(std::istream& stream, std::vector<int>& vec)
{
std::copy(std::istream_iterator<int>(stream),
std::istream_iterator<int>(),
std::back_inserter(vector)
);
}
Now you mention that your numbers are comma seporated. To cope with this you can defined a class that reads a number and a trailing comma;
struct NumberAndComma
{
int number;
operator int() {return number;}
friend std::istream& operator>>(std::istream& str, NumberAndComma& data)
{
if (str >> number) {
char x;
stream >> x;
if (x != ',') {
stream.unget();
}
if (!stream) {
// It was good before we tried to read comma.
// so really it should still be good even if there is no comma
stream.clear();
}
}
return stream;
}
};
Now you can use this inplace of int above:
void func(std::istream& stream, std::vector<int>& vec)
{
std::copy(std::istream_iterator<NumberAndComma>(stream),
std::istream_iterator<NumberAndComma>(),
std::back_inserter(vector)
);
}
Consider the following code:
#include <boost/range.hpp>
#include <boost/range/any_range.hpp>
#include <boost/range/join.hpp>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <list>
struct TestData {
TestData() : m_strMem01("test"), m_intMem02(42), m_boolMem03(true) {}
std::string m_strMem01;
int m_intMem02;
bool m_boolMem03;
};
struct IntComp {
bool operator()(const TestData &s, int i) { return s.m_intMem02 < i; }
bool operator()(int i, const TestData &s) { return i < s.m_intMem02; }
bool operator()(const TestData &i, const TestData &s) {
return i.m_intMem02 < s.m_intMem02;
}
};
struct StrComp {
bool operator()(const TestData &s, const std::string &str) {
return s.m_strMem01 < str;
}
bool operator()(const std::string &str, const TestData &s) {
return str < s.m_strMem01;
}
bool operator()(const TestData &i, const TestData &s) {
return i.m_strMem01 < s.m_strMem01;
}
};
typedef boost::any_range<TestData, boost::forward_traversal_tag,
const TestData &, std::ptrdiff_t> TestRange;
std::vector<TestData> vecData(10);
std::list<TestData> listData(20);
TestRange foo() {
TestRange retVal;
auto tmp1 = std::equal_range(vecData.cbegin(), vecData.cend(), 42, IntComp());
retVal = boost::join(retVal, tmp1);
auto tmp2 =
std::equal_range(listData.cbegin(), listData.cend(), "test", StrComp());
retVal = boost::join(retVal, tmp2);
return retVal;
}
int main(int argc, char *argv[]) {
auto res = foo();
for (auto a : res) {
std::cout << a.m_strMem01 << std::endl;
}
//std::cout << res[4].m_intMem02 << std::endl;
}
If you uncomment the last line the code fails since distance_to not implemented for any_forward_iterator_interface. I'm not sure what exactly I'm missing here, like implementing operator[] or distance_to but for what? My own version traversal tag? And why it doesn't work in the first place?
Coliru version
I would say the answer depends on your performance needs and your laziness when it comes to implementing a new iterator abstraction. The core reason for your [] operator not working is the fact that std::list<...> does not provide a random access traversal iterator. If you would have chosen a container that provides such an iterator. You any_range<...> could have taken the random_access_traversal_tag and everything would be fine.
I think it's fair to say that it is not such a big deal to implement a random access iterator on top of a list by simply encapsulating the current index and count forward and backward within the list whenever a specific position is meant to be accessed, but it's clearly against the nature of the list performance-wise.
Is there a good reason to hold one of the collection in a list ?
Is there a good reason to access the resulting any_range by random ?
Is it worth the effort to provide a inefficient random access interface for std::list ?
Of course any_iterator (which underlies the any_range implementation) doesn't gratuitously emulate RandomAccess iterators for any odd iterator you pass.
If you want that, just make an iterator adaptor that does this (making it very slow to random access elements in a list - so don't do this).