C++ creating a std::vector from std::string array - c++

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.

Related

How do I add a string to a vector (and subsequently display it)?

I am 4 hours-new to C++ and have hit a brick wall with string vectors. When trying to add multiple strings to a string vector, I keep erroring out. I'd like to use push_back.
I would also like to display this string vector, but I'm not sure how (I know how to display non-vectors). Given that I have not been able to add a string to a vector of strings, I did not attempt to display the vector of strings yet.
profile.hpp
#include <iostream>
#include <vector>
class Profile
{
private:
std::string name;
std::string city;
std::string country;
int age;
std::vector<std::string> hobbies;
public:
std::vector<std::string> add_hobbies(std::string new_hobbies);
};
profile.cpp
#include <iostream>
#include "profile.hpp"
Profile::Profile(std::string new_name, int new_age, std::string new_city, std::string new_country)
: name(new_name), age(new_age), city(new_city), country(new_country)
{}
void Profile::add_hobbies(std::string new_hobbies)
{
hobbies.push_back(new_hobbies);
}
app.cpp
#include <iostream>
#include "profile.hpp"
int main()
{
Profile sam("Sam Drakkila", 30, "New York", "USA");
sam.add_hobbies("Play golf", "Read books", "Eat tennis balls"); // This doesn't seem to work.
}
g++ app.cpp profile.cpp. Prints a massive log of errors.
You have the following problems in your code:
You have declared add_hobbies returns std::vector<std::string>,
but in definition you have returned void. Presumably, you should have declared as a void function as it seems to be a setter function.
Secondly, you are passing a number of strings instead of a single string which
you defined here:
void Profile::add_hobbies(std::string new_hobbies) //>>> as per defenition, only one string can be passed!
// ^^^^^^^^^^^^^^^^^^^^^^^^
If you want to pass an arbitrary number of strings, you could usestd::initializer_list<std::string> instead.
Thirdly, you are missing the constructor declaration in the header file. Add
in the definition of class profile.hpp
Profile(std::string new_name, int new_age, std::string new_city, std::string new_country);
Last but not least, you need to include the <string> header in
order to use std::string(credits #πάντα ῥεῖ)
That means, (See live online)
#include <iostream>
#include <vector>
#include <string> // std::string
#include <initializer_list> // std::initializer_list
class Profile
{
private:
// ... other members
std::vector<std::string> hobbies;
public:
// ... other member functions
void add_hobbies(std::initializer_list<std::string> new_hobbies)
//^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
hobbies.reserve(hobbies.size() + new_hobbies.size()); // reserve memory to avoid, unwanted reallocations
for (const auto& hobby : new_hobbies) hobbies.emplace_back(hobby);
}
};
int main()
{
Profile sam{};
sam.add_hobbies({ "Play golf", "Read books", "Eat tennis balls" }); // now works
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
Alternatively using variadic templates and c++17 feature fold expression.
(See live online)
#include <iostream>
#include <vector>
#include <string> // std::string
#include <type_traits> // std::enable_if, std::is_same, std::common_type
using namespace std::literals;
class Profile
{
private:
// ... other members
std::vector<std::string> hobbies;
public:
// ... other member functions
template<typename... Args> // sfinae to restrict the Args type to be only std::string s
std::enable_if_t<std::is_same_v<std::common_type_t<Args...>, std::string>>
add_hobbies(Args&& ... args)
{
hobbies.reserve(hobbies.size() + sizeof...(Args));
(hobbies.emplace_back(std::forward<Args>(args)), ...);
}
};
int main()
{
Profile sam{};
sam.add_hobbies("Play golf"s, "Read books"s, "Eat tennis balls"s); // now works
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
You declare add_hobbies as returning a vector in your class declaration.
There are a few errors in your code:
Missing constructor declaration:
Profile(std::string new_name, int new_age, std::string new_city, std::string new_country);
Mismatch between return type of the declaration and definition of add_hobbies, should be void (since you're not returning anything).
void Profile::add_hobbies(std::string new_hobbies) { // ... }
You're also trying to pass 3 of them, while the function only has 1 parameter:
void add_hobbies(std::string const& h1, std::string const& h2, std::string const& h3);
// ...
void Profile::add_hobbies(std::string const& h1, std::string const& h2, std::string const& h3) {
hobbies.push_back(h1);
hobbies.push_back(h2);
hobbies.push_back(h3);
}

Use a struct in another struct

Hi there I keep getting error for the code below which I think makes sense!
I'm trying to use a member of the first struct in a function in the second struct. I code in Xcode and this is the error I get:
No member named 'PLZ' in 'std::__1::vector >'
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct place {
string PLZ;
string name;
};
struct adresse {
string firstN;
string lastN;
string Str;
string Hsnum;
string PLZ;
void print(vector<place> a){
cout<<a.PLZ;
}
};
int main()
{
return 0;
}
Just define the function at least like
void print(const vector<place> &a) const
{
for ( const auto &place : a )
{
cout << place.PLZ << ' ';
}
}
std::vector is a container that can contain several elements. So you have to specify whether you are going to output all elements of a vector or a concrete element.
The function above outputs the data member PLZ of each element of the vector.

Cannot declare array of strings as class member

I could not declare an array of strings in my class. Below my class definition:
class myclass{
public:
int ima,imb,imc;
string luci_semaf[2]={"Rosso","Giallo","Verde"};
};
and my main file
#include <iostream>
#include <fstream>
#include "string.h"
#include <string>
using namespace std;
#include "mylib.h"
int main() {
return 0;
}
Why do I get the following warnings / error?
You have two problems: The first is that you can't initialize the array inline like that, you have to use a constructor initializer list. The second problem is that you attempt to initialize an array of two elements with three elements.
To initialize it do e.g.
class myclass{
public:
int ima,imb,imc;
std::array<std::string, 3> luci_semaf;
// Without C++11 support needed for `std::array`, use
// std::string luci_semaf[3];
// If the size might change during runtime use `std::vector` instead
myclass()
: ima(0), imb(0), imc(0), luci_semaf{{"Rosso","Giallo","Verde"}}
{}
};
You can not initialize data member.
You can write like this:
class myclass{
public:
myclass() {
luci_semaf[0] = "Rosso";
luci_semaf[1] = "Giallo";
luci_semaf[2] = "Verde";
}
private:
int ima,imb,imc;
string luci_semaf[3];
};
You can assign the values of the array in the Сonstructor
You're declaring an array of size 2 but providing 3 strings!
Try storing the elements in vector of strings, in c++ vectors are used more often.
class myclass{
public:
int ima,imb,imc;
std::vector<std::string> strings;
myclass() {
strings.push_back("blabla");
}
};

How to reconstruct non-pointer class member containing vector?

I'm facing a weird issue : I can't reset (destruct and construct) properly an attribute containing a vector. It causes a segmentation fault when trying to access the vector.
Here is my code (witten in C++11). I think I simplified it the most possible to underscore the issue, but I might be wrong, sorry about that.
The goal would be to print two times two different (random) vectors. The first vector is working well, the second is completely failing for an unknown reason.
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
class A
{
std::vector<int> n;
public :
A();
std::string toString() const;
};
A::A()
{
for (int i = 0; i < 10; i++)
n.push_back(std::rand()%10);
}
std::string A::toString() const
{
for (auto i : n)
std::cout << i << ' ';
std::cout << std::endl;
}
class B
{
A a;
public :
void resetA();
A getA() const;
};
void B::resetA()
{
a = A();
}
A B::getA() const
{
return a;
}
int main()
{
srand(time(NULL));
B b;
std::cout << b.getA().toString();
b.resetA();
std::cout << b.getA().toString();
return EXIT_SUCCESS;
}
For some reason, I would like to avoid pointers and dynamic allocation as far as possible. It would fit less with my UML conception.
Moreover, this code is working well when using simple int (no vectors).
Thank you.
Your toString() doesn't return anything, so your program has Undefined Behaviour (and, in practice, returns random garbage which is most certainly not a valid std::string object).
Perhaps you wanted to use a string stream instead?
#include <sstream>
// ...
std::string A::toString() const
{
std::ostringstream s;
for (auto i : n)
s << i << ' ';
s << '\n';
return s.str();
}
Live example.
Generally, it's a good idea to compile with as many warnings turned on as possible. This would certainly have been reported as a warning then. For this particular warning (no-void function not returning anything), I strongly suggest treating it as an error.

C++: Wrapping vector<char> with istream

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.