C++: Wrapping vector<char> with istream - c++
I want to wrap a vector<char> with std::istream (so reading the vector would be done through the istream interface)
What's the way to do it?
You'd define a streambuf subclass wrapping the vector, and pass an instance of that to the istream constructor.
If the data does not change after construction, it is sufficient to set up the data pointers using streambuf::setg(); the default implementation for the other members does the right thing:
template<typename CharT, typename TraitsT = std::char_traits<CharT> >
class vectorwrapbuf : public std::basic_streambuf<CharT, TraitsT> {
public:
vectorwrapbuf(std::vector<CharT> &vec) {
setg(vec.data(), vec.data(), vec.data() + vec.size());
}
};
std::vector<char> data;
// ...
vectorwrapbuf<char> databuf(data)
std::istream is(&databuf);
If you need anything more advanced than that, override the streambuf::underflow method.
using Boost:
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/array.hpp>
using namespace boost::iostreams;
basic_array_source<char> input_source(&my_vector[0], my_vector.size());
stream<basic_array_source<char> > input_stream(input_source);
or even simpler:
#include <boost/interprocess/streams/bufferstream.hpp>
using namespace boost::interprocess;
bufferstream input_stream(&my_vector[0], my_vector.size());
Adapting the answer from Get an istream from a char* and assuming this is what you're trying to do:
// Forward declarations
std::vector<char> my_create_buffer();
void my_consume_buffer( std::istream & is );
// What you want to be able to write
std::vector<char> buffer = my_create_buffer();
my_consume_buffer( wrap_vector_as_istream(buffer) );
You can then create the wrap_vector_as_istream like this (untested though) :
#include <iostream>
#include <istream>
#include <streambuf>
#include <string>
struct wrap_vector_as_istream : std::streambuf
{
wrap_vector_as_istream(std::vector<char> & vec ) {
this->setg(&vec[0], &vec[0], &vec[0]+vec.size() );
}
};
One thing to be aware of though. The object you've created contains pointers into the vectors memory. So if you add or remove values to the vector while having this wrapper floating around, then you're heading for a crash.
(Oh and if you up vote me, please up vote the post I've adapted this from.)
You'd could get away with simply building a class that implements the >> operator like a stream, something like this:
template<class _ITy>
class RangeStreamLite
{
private:
_ITy Begin;
_ITy End;
_ITy Next;
public:
RangeStreamLite(_ITy begin, _ITy end) :
Begin(begin),
End(end),
Next(begin)
{
// Do nothing.
}
template<class _OTy>
RangeStreamLite& operator>>(_OTy& out)
{
out = *Next;
++Next;
return *this;
}
void reset()
{
Next = Begin;
}
};
This is a 'quick and dirty' solution, a 'stream lite', it isn't really a stream in the proper sense but it works when all you require is a superficial stream-like device. To properly create a custom stream is a little more complicated, and would require you to inherit from std::streambuf and implement the necessary features. Here are a few links worth a look:
Inheriting std::istream or equivalent
Deriving the C++ Stream Buffer
If you are fine with swapping your vector<char> you can use Boost Interprocess' vectorstream. Example:
#include <boost/interprocess/streams/vectorstream.hpp>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
static const char inp[] = "123 45 666";
vector<char> v(inp, inp + sizeof inp - 1);
using ivectorstream =
boost::interprocess::basic_ivectorstream<std::vector<char>>;
ivectorstream is;
is.swap_vector(v);
while (!is.eof()) {
int i = 0;
is >> i;
cout << i << '\n';
}
is.swap_vector(v);
cout << string(v.begin(), v.end()) << '\n';
return 0;
}
Alternatively, if you can't or don't want to mutate your vector<char>, you can use Boost Interprocess' bufferstream. With bufferstream, you don't have to swap your vector into it. Example:
#include <boost/interprocess/streams/bufferstream.hpp>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
static const char inp[] = "123 45 666";
vector<char> v(inp, inp + sizeof inp - 1);
boost::interprocess::ibufferstream is(v.data(), v.size());
while (!is.eof()) {
int i = 0;
is >> i;
cout << i << '\n';
}
return 0;
}
You will have to write a custom stream-implementation that inherits from istream. This can easily be done using Boost.Iostreams - all you'd have to do is implement a simple Source.
Related
C++ creating a std::vector from std::string array
I am learning about c++ and was following a course. A final exercise involves making a program for deck of cards. I have thought of an approach: I initially tried to do everything with string arrays but realised that it would make more sense to use vectors since. I am now trying to create a std::vector std::string out of my std::string array but with no luck. I have found some example code online such as: from https://thispointer.com/5-different-ways-to-initialize-a-vector-in-c/ And tried to implement it for my program, however, I cannot get it to work and cant fully understand what is the issue. My code: #include <iostream> #include <string> #include <vector> class card_deck { public: card_deck(); std::vector<std::string> deal_hand(int num_cards); void new_deck(); private: //std::vector<std::string> cards_vector; std::string cards[52] = { "As","2s","3s","4s","5s","6s","7s","8s","9s","Ts","Js","Qs","Ks", "Ah","2h","3h","4h","5h","6h","7h","8h","9h","Th","Jh","Qh","Kh", "Ad","2d","3d","4d","5d","6d","7d","8d","9d","Td","Jd","Qd","Kd", "Ac","2c","3c","4c","5c","6c","7c","8c","9c","Tc","Jc","Qc","Kc" }; std::vector<std::string> cards_vector(cards, sizeof(cards)/sizeof(std::string) ); }; As you can see from my code, I initialize a string array in my private variables, and then want to convert this string array to std::vector The error message returned: UPDATE Code works when called in main() int main() { std::string cards[52] = { "As","2s","3s","4s","5s","6s","7s","8s","9s","Ts","Js","Qs","Ks", "Ah","2h","3h","4h","5h","6h","7h","8h","9h","Th","Jh","Qh","Kh", "Ad","2d","3d","4d","5d","6d","7d","8d","9d","Td","Jd","Qd","Kd", "Ac","2c","3c","4c","5c","6c","7c","8c","9c","Tc","Jc","Qc","Kc" }; // Initialize vector with a string array std::vector<std::string> vecOfStr(cards, cards + sizeof(cards) / sizeof(std::string)); for (std::string str : vecOfStr) std::cout << str << std::endl; } Does not work when used in class #include <iostream> #include <string> #include <vector> class card_deck { public: card_deck(); std::vector<std::string> deal_hand(int num_cards); void new_deck(); private: std::string cards[52] = { "As","2s","3s","4s","5s","6s","7s","8s","9s","Ts","Js","Qs","Ks", "Ah","2h","3h","4h","5h","6h","7h","8h","9h","Th","Jh","Qh","Kh", "Ad","2d","3d","4d","5d","6d","7d","8d","9d","Td","Jd","Qd","Kd", "Ac","2c","3c","4c","5c","6c","7c","8c","9c","Tc","Jc","Qc","Kc" }; // Initialize vector with a string array std::vector<std::string> vecOfStr(cards, cards + sizeof(cards) / sizeof(std::string)); for (std::string str : vecOfStr) std::cout << str << std::endl; }; int main() { }
Easy way: std::vector<std::string> cards { "As","2s","3s","4s","5s","6s","7s","8s","9s","Ts","Js","Qs","Ks", "Ah","2h","3h","4h","5h","6h","7h","8h","9h","Th","Jh","Qh","Kh", "Ad","2d","3d","4d","5d","6d","7d","8d","9d","Td","Jd","Qd","Kd", "Ac","2c","3c","4c","5c","6c","7c","8c","9c","Tc","Jc","Qc","Kc" }; and drop the separate cards_vector member. cards.size() yields the number of elements in the vector. This uses the initialiser-list syntax of C++11. And the compiler works out the size for you: handy if you need to add the jokers in later for example.
How do I display the contents of a std::vector of my own datatype and display the name on the console using std::copy?
I would like to display the contents of the std::vector<User> using std::copy in a similar way to how I've achieved it through std::for_each in the code below. #include <iostream> #include <vector> #include <string> #include <algorithm> class User { private: std::string m_sName; public: User(std::string sName):m_sName(sName) {} std::string GetName()const { return m_sName; } }; int main() { std::vector<User> vectNames; vectNames.emplace_back("Jack"); vectNames.emplace_back("George"); vectNames.emplace_back("Jose"); //How can I get std::copy to do what the std::for_each is doing? std::copy(vectNames.begin(), vectNames.end(), std::ostream_iterator<User>(std::cout, "\n")); //The following line is what I really would like std::copy to do. std::for_each(vectNames.begin(), vectNames.end(), [](const User &user) {std::cout << user.GetName() << "\n"; }); }
You can simply overload the ostream operator: #include <iostream> #include <vector> #include <string> #include <algorithm> class User { private: std::string m_sName; public: User(std::string sName):m_sName(sName) {} std::string GetName()const { return m_sName; } void print(std::ostream& where) const { where << m_sName; } }; /// Overloaded ostream operator std::ostream& operator<< (std::ostream& out, const User& user) { user.print(out); return out; } int main() { std::vector<User> vectNames; vectNames.emplace_back("Jack"); vectNames.emplace_back("George"); vectNames.emplace_back("Jose"); std::copy(vectNames.begin(), vectNames.end(), std::ostream_iterator<User>(std::cout, "\n")); } In general I always have an overloaded ostream as part of classes that store and output information.
Because of your comment I am a new programmer and am experimenting with different ideas, I'm posting this answer as an alternative approach which also shows some algorithm that you might enthusiastic about. I think that std::copy isn't the right tool if what you want to do is both calling a member function on the objects in a collection, and only after that copying/printing them to screen One, and maybe the best, approach is the one in the atru's answer, which basically solves only point 2 with std::copy (and this is something you already knew how to do it, based on your question), and point 1 by overloading <<, which makes the trick. The alternative I propose is based on the idea that "calling a member function on every object of a collection" is actually a transform operation of that collection. std::transform, however, just like std::copy acts on iterators, not on ranges, so they cannot be easily composed with one another. Here comes Boost, with boost::copy and boost::adaptors::transformed which allow you to do this: boost::copy( vectNames | transformed([](auto const& x){ return x.GetName(); }), std::ostream_iterator<std::string>(std::cout, "\n") ); where vectNames is "piped into" transformed, which applies the lambda on every element of the collection, resulting in a new temporary collection, which is the argument to boost::copy. If you were to use std::copy, you would have to store the temporary somewhere, e.g. in temp, before passing to std::copy its iterators temp.begin() and temp.end(). The full example is below. I hope it will give you some insight in different, more functional, approaches. #include <functional> #include <iostream> #include <vector> #include <string> #include <boost/range/adaptor/transformed.hpp> #include <boost/range/algorithm/copy.hpp> class User { private: std::string m_sName; public: User(std::string sName):m_sName(sName) {} std::string GetName()const { return m_sName; } }; int main() { std::vector<User> vectNames; vectNames.emplace_back("Jack"); vectNames.emplace_back("George"); vectNames.emplace_back("Jose"); using boost::adaptors::transformed; boost::copy( vectNames | transformed([](auto const& x){ return x.GetName(); }), std::ostream_iterator<std::string>(std::cout, "\n") ); }
How to move std::ostringstream's underlying string object?
#include <sstream> #include <string> using namespace std; template<typename T> string ToString(const T& obj) { ostringstream oss; oss << obj; // // oss will never be used again, so I should // MOVE its underlying string. // // However, below will COPY, rather than MOVE, // oss' underlying string object! // return oss.str(); } How to move std::ostringstream's underlying string object?
The standard says that std::ostringstream::str() returns a copy. One way to avoid this copy is to implement another std::streambuf derived-class that exposes the string buffer directly. Boost.IOStreams makes this pretty trivial: #include <boost/iostreams/stream_buffer.hpp> #include <iostream> #include <string> namespace io = boost::iostreams; struct StringSink { std::string string; using char_type = char; using category = io::sink_tag; std::streamsize write(char const* s, std::streamsize n) { string.append(s, n); return n; } }; template<typename T> std::string ToString(T const& obj) { io::stream_buffer<StringSink> buffer{{}}; std::ostream stream(&buffer); stream << obj; stream.flush(); return std::move(buffer->string); // <--- Access the string buffer directly here and move it. } int main() { std::cout << ToString(3.14) << '\n'; }
Since C++20 you can. std::move(oss).str()
find strings in vector with equal_to function object
I have following code: #include <iostream> #include <fstream> #include <algorithm> #include <iterator> #include <vector> #include <functional> using namespace std; typedef istream_iterator<string> is_it; typedef vector<string>::iterator v_str_it; int main() { int i = 4; ifstream ifstr("1.txt"); is_it ifstrm(ifstr); is_it eof; vector<string> v_str(ifstrm, eof); v_str_it vsit = v_str.begin(); while( (vsit = find_if(vsit, v_str.end(), bind2nd(equal_to<string>(), i ))) != v_str.end()) { cout << *vsit << endl; ++vsit; } return 0; } As far as I understand in find_if(vsit, v_str.end(), bind2nd(equal_to<string>(), i ) i should use const char like "sometext" instead of int i. But how can i find words with length equal to 4 e.g. ? I'm confused and need some advice.
find_if will only return the first item in the sequence that satisfies the predicate. For this you really want a lambda and if you are using C++11. This will look something like: [](std::string const& x) { return x.size() == i; } (Not sure of the exact syntax). To create a "functor" which is the simplest here you might do: struct CompareStringLength { int len_; explicit CompareStringLength( int len ) : len_(len) { } bool operator()(std::string const& str ) const { return str.size() == len_; } }; Within your vector you would now use std::find_if( v.begin(), v.end(), CompareStringLength(i) ); to get the first element. To find all of them there is no std::copy_if to copy them into another vector so you'd actually have to create a different predicate that returns the opposite and use remove_copy_if which does exist or write your own copy_if algorithm.
Is there a C++ way to write file with any type of data?
Like this function in C: size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ); I've looked in C++ file stream and found this one: ostream& write ( const char* s , streamsize n ); this one only accepts char* instead of void* but does it really matter if I use a C-style fwrite function in c++?
Streams are probably what you're looking for unless I misunderstand your question. There are many flavors that handle different jobs, like outputting to a file: #include <cstdlib> #include <fstream> #include <string> using namespace std; int main() { ofstream f("c:\\out.txt"); const char foo[] = "foo"; string bar = "bar"; int answer = 42; f << foo << bar<< answer; return 0; } ...building strings like you would with printf: #include <cstdlib> #include <sstream> #include <string> #include <iostream> using namespace std; int main() { stringstream ss; const char foo[] = "foo"; string bar = "bar"; int answer = 42; ss << foo << bar<< answer; string my_out = ss.str(); return 0; } ...and they can even handle your own types, if you tell them how: #include <cstdlib> #include <string> #include <iostream> using namespace std; class MyGizmo { public: string bar_; int answer_; MyGizmo() : bar_("my_bar"), answer_(43) {}; }; ostream& operator<<(ostream& os, const MyGizmo& g) { os << g.bar_ << " = " << g.answer_; return os; } int main() { MyGizmo gizmo; cout << gizmo; return 0; }
You can use either one. Using char * instead of void * doesn't make much real difference -- both fwrite and ostream::write are typically used for a variety of data types (with with C++, you need to add an explicit cast to char *, where in C the cast will happen implicitly, assuming you've included a proper prototype for fwrite).
In C++ you will want to use std::ofstream objects to write to a file. They can accept any type of data using the << operator, in much the same way that std::cout works for writing to the console. Of course, just like std::cout, if you want to print a custom type, you will need to define an operator<< overload for it. An example: std::ofstream outfile("myfile.txt"); int i = 5; double d = 3.1415926535898; std::string s = "Hello, World!"; outfile << i << std::endl; outfile << d << std::endl; outfile << s << std::endl; To use std::ofstream, you need to #include <fstream>. The outfile object will automatically close the file when it destructs, or you can call its close() method.
Contrary to already given answers, there is an important difference between fwrite() and ostream::write(). fwrite() writes binary data unmodified (well, on those poor non-Unix platforms there is endline translation, unless the file is opened in binary mode). ostream::write() uses locale to transform every character, this is why it accepts char* rather than void*. Normally, it uses the default "C" locale, which does not do any transformation. Just keep in mind that basic_ostream is a formatter on top of basic_streambuf, not a binary sink.