I am trying to output a element of enum I declared but for example, when I input push_ups, it outputs a number like 8621623 instead of showing push_ups. I have no idea why. I am Japanese so I am sorry if my English is broken. Thank you so much.
#include <iostream>
#include <string>
using namespace std;
enum exercise { push_ups, sit_ups, squats, walking, radio_calisthenics };
istream& operator>>(istream& is, exercise& i)
{
int tmp;
if (is >> tmp)
i = static_cast<exercise>(tmp);
return is;
}
int main()
{
exercise a;
cin >> a;
cout << a << endl;
}
I am trying to output a element of enum I declared but for example, when I input push_ups, it outputs a number like 8621623 instead of showing push_ups.
In the operator>> overload, std::cin accepts integers so push_ups isn't an integer, so std::cin will fail and and the line i = static_cast<exercise>(tmp); will be skipped making a uninitialized which when printed can cause Undefined Behavior to occur.
If you want to map strings to respective enum values, you could do that by mapping each string to the corresponding enum values manually using a hashmap (In C++, that container is called std::unordered_map):
#include <unordered_map>
// ...
const unordered_map<string, exercise> exercise_map {
{ "push_ups", push_ups },
{ "sit_ups", sit_ups },
{ "squats", squats },
{ "walking", walking },
{ "radio_calisthenics", radio_calisthenics }
};
istream& operator>>(istream& is, exercise& i) {
std::string tmp;
if (is >> tmp) {
auto const it = exercise_map.find(tmp);
if (it != exercise_map.end())
i = it->second;
}
return is;
}
Now, to print out the corresponding string value from the enum, we have to do the reverse, i.e., find the key in the hashmap using the value:
ostream& operator<<(ostream& os, exercise& i) {
auto const it = std::find_if(exercise_map.begin(), exercise_map.end(),
[i](std::pair<std::string, exercise> const& e) {
return e.second == i;
}
);
if (it != exercise_map.end())
os << it->first;
return os;
}
This is how the full code should look like:
#include <unordered_map>
#include <algorithm>
#include <utility>
#include <iostream>
#include <string>
using namespace std;
enum exercise { push_ups, sit_ups, squats, walking, radio_calisthenics };
const std::unordered_map<std::string, exercise> exercise_map {
{ "push_ups", push_ups },
{ "sit_ups", sit_ups },
{ "squats", squats },
{ "walking", walking },
{ "radio_calisthenics", radio_calisthenics }
};
istream& operator>>(istream& is, exercise& i) {
std::string tmp;
if (is >> tmp) {
auto const it = exercise_map.find(tmp);
if (it != exercise_map.end())
i = it->second;
}
return is;
}
ostream& operator<<(ostream& os, exercise& i) {
auto const it = std::find_if(exercise_map.begin(), exercise_map.end(),
[i](std::pair<std::string, exercise> const& e) {
return e.second == i;
}
);
if (it != exercise_map.end())
os << it->first;
return os;
}
int main() {
exercise a;
cin >> a;
cout << a << endl;
}
push_ups is not a valid integer value that you are trying to read at is >> tmp so a remains uninitialized. If you want to input names then you'll need to read string and then manually convert it to corresponding enum value. Same for output. Without a properly overloaded operator << a will be treated as an integer.
this line here:
i = static_cast<exercise>(tmp);
will work perfectly when tmp is holding an int value between 0 and 4,
but in your case tmp = push_ups is breaking the cast operation and your ref i is wrongly initialized
You seem to have problems with understanding enums.
I think you entered the string value "push_ups" and assumed the program would understand it to refer to a value of your enum.
An enum is just a placeholder for an integer type, mostly used to increase the readability of your program.
See an enum to be a better way to express something like
// chess_piece = 1 : pawn
// chess_piece = 2 : rook
// chess_piece = 3 : bishop
...
int chess_piece;
as
enum ChessPiece { Pawn, Rook, Bishop, ...};
ChessPiece chess_piece;
In the upper variant, it is clear that the names pawn, rook etc are comment only. The enum isn't different in that regard. It is clearly more readable to write if(chess_piece == Pawn), but the word "Pawn" is only a part of the code in the programming language, not in the compiled program. It does not exist as string value.
What you can do is to add something like
exercise exercise_from_string(const std::string& input)
{
if(input == "push_ups") return push_ups;
...
Or the same with a switch. I'd also advise that you have a value "unknown" in case a function like that finds no known term.
Edit: One of the other guys updated his answer while I wrote this, his code using a std::map is better than mine.
Hope my answer helps you understanding the core issue, though.
Related
I've not used C++ in over 20 years, so I am a bit rusty on this.
I am using the nlohmann/json json.h library and need to convert a JSON string that is an array of objects to an actual array.
As an example, I need to take this kind of response from a TRESTResponse object and turn it into a C++ array:
RESTRequest1->Execute();
String Content = RESTResponse1->Content;
Where Content =
"[{"fullname":"Norm A","e_mail":null,"phone":"999-555-4971"},{"fullname":"Norm C","e_mail":"ht2#yahoo.com","phone":"999-555-8887"},{"fullname":"Norma Jn","e_mail":null,"phone":"999-555-5947"},{"fullname":"Norma & Frank L","e_mail":null,"phone":"999-555-1790"},{"fullname":"Norm Sh","e_mail":null,"phone":"999-555-7545"},{"fullname":"Norm S","e_mail":null,"phone":"999-555-9955"}]"
and get it into an array of objects. I have been unsuccessful with the library. While I can get an array into json properly, I can't seem to get json back to an array.
I've looked at some similar posts on Stackoverflow, but I did not see one that concerns the nlohmann/json library.
At a guess, you were probably running into a problem because your input data contains null for a number of the strings (some of the email addresses).
To fix that, you need to explicitly check for is_null before attempting to convert the source to an std::string. At a quick guess, for a null input, you'd probably want to just leave that string empty. For that, your from_json would look something like this:
void from_json(json const &j, Person &p) {
j.at("fullname").get_to(p.name);
if (!j.at("e_mail").is_null())
j.at("e_mail").get_to(p.email);
j.at("phone").get_to(p.phone);
}
That's enough to work for the sample data, but depending on the data involved, you might want/need to protect against a null name and/or phone numbers as well (which you'd do in the same way as shown above for the email address).
A complete demo program using this would might look roughly like this:
#include <sstream>
#include <string>
#include <iostream>
#include "nlohmann/json.hpp"
using json = nlohmann::json;
std::string input { R"(
[{"fullname":"Norm A","e_mail":null,"phone":"999-555-4971"},{"fullname":"Norm C","e_mail":"ht2#yahoo.com","phone":"999-555-8887"},{"fullname":"Norma Jn","e_mail":null,"phone":"999-555-5947"},{"fullname":"Norma & Frank L","e_mail":null,"phone":"999-555-1790"},{"fullname":"Norm Sh","e_mail":null,"phone":"999-555-7545"},{"fullname":"Norm S","e_mail":null,"phone":"999-555-9955"}]
)"};
namespace P {
struct Person {
std::string name;
std::string email;
std::string phone;
friend std::ostream &operator<<(std::ostream &os, Person const &p) {
return os << "name: " << p.name << ", email: " << p.email << ", phone: " << p.phone;
}
};
void from_json(json const &j, Person &p) {
j.at("fullname").get_to(p.name);
if (!j.at("e_mail").is_null())
j.at("e_mail").get_to(p.email);
j.at("phone").get_to(p.phone);
}
}
int main() {
json j = json::parse(input);
std::vector<P::Person> people;
j.get_to(people);
for (auto const &person : people) {
std::cout << person << "\n";
}
}
[EDIT] Added an example that uses your input data. [Demo]
// Outputs:
//
// [
// (Norm A, null, 999-555-4971),
// (Norm C, ht2#yahoo.com, 999-555-8887),
// (Norma Jn, null, 999-555-5947),
// (Norma & Frank L, null, 999-555-1790),
// (Norm Sh, null, 999-555-7545),
// (Norm S, null, 999-555-9955)
// ]
The example below loads a JSON node of array type into a std::vector.
The input JSON string only contains a node writers whose content is an array of strings:
{
"writers": [
"Winston Groom",
"Eric Roth"
]
}
We load it into a JSON node with:
nlohmann::json j = nlohmann::json::parse(json_str);
We parse the "value" for the writers "key", i.e. the array, with:
j.at("writers").get_to(writers);
This will make use of the available from_json(const nlohmann::json&, YourCustomType&) in order to do the parsing.
The struct Writers parses the JSON node into a std::vector<Writer> with:
writers.data = j.get<std::vector<Writer>>();
And the struct Writer parses the JSON node into a std::string with:
j.get_to(writer.name);
[Demo]
#include <iostream> // cout
#include <nlohmann/json.hpp>
#include <ostream>
#include <string>
#include <vector>
struct Writer
{
std::string name{};
};
void from_json(const nlohmann::json& j, Writer& writer)
{
j.get_to(writer.name);
}
struct Writers
{
std::vector<Writer> data{};
};
void from_json(const nlohmann::json& j, Writers& writers)
{
writers.data = j.get<std::vector<Writer>>();
}
int main()
{
std::string json_str{R"({"writers": ["Winston Groom", "Eric Roth"]})"};
Writers writers{};
nlohmann::json j = nlohmann::json::parse(json_str.c_str());
j.at("writers").get_to(writers);
for (const auto& writer : writers.data)
{
std::cout << writer.name << ", ";
}
}
// Outputs:
//
// Winston Groom, Eric Roth,
class Person
{
public:
string fname;
string lname;
string occupation;
string gender;
int age;
};
int main()
{
Person bc;
bc.fname = "Bevelry";
bc.lname = "Crusher";
bc.gender = "female";
bc.occupation = "Doctor, USS 1701-D";
bc.age = 40;
cout << bc.all << "\n"; //Something like this?
}
Is it possible for me to print every variable of an object without specifying them by myself? And is it possible for me to make a select list of variables, something like an array of variables, and then print them?
EDIT: i accidently put the cout in the class, fixed now
Is it possible for me to print every variable of an object without specifying them by myself?
No.
And is it possible for me to make a select list of variables, something like an array of variables, and then print them?
Not automatically, but you could create a collection of std::anys and add some decoding for it yourself.
Example:
#include <any>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
// decode and print one std::any
void decode_one(std::ostream& os, const std::any& a) {
if(auto s = std::any_cast<std::reference_wrapper<std::string>>(&a)) os << s->get();
else if(auto i = std::any_cast<std::reference_wrapper<int>>(&a)) os << *i;
else if(auto d = std::any_cast<std::reference_wrapper<double>>(&a)) os << *d;
// add more types to decode here
else os << "<unknown type>";
}
// a custom ostream operator to decode a vector of anys:
std::ostream& operator<<(std::ostream& os, const std::vector<std::any>& va) {
auto begin = va.begin();
auto end = va.end();
if(begin != end) {
decode_one(os, *begin);
for(std::advance(begin, 1); begin != end; std::advance(begin, 1)) {
os << ',';
decode_one(os, *begin);
}
}
return os;
}
int main() {
int a = 10;
std::string b = "Hello world";
double c = 3.14159;
std::vector<std::any> va{
std::ref(a),
std::ref(b),
std::ref(c)
};
c *= 2.; // just to show that the vector holds a reference
std::cout << va << '\n'; // print the variables in the vector
}
Output:
10,Hello world,6.28318
It's not possible. In C++ there isn't anything that does what you want.
At one side one can easily say that this is not possible in C++.
However, at the other side, one can add that this situation happens quite often when people are making object oriented computer programs, and in Java there is the toString() method for this. So, you might actually write your own toString() method (based on the way it is done in Java), and work with this.
Good luck
I have an array of objects and I want to transfer the objects to another array. I've written the code bellow to do so but it did not work. It is basically a code where 52 card objects are created in one array and distributed between two arrays.Can anyone help me?
class card
{
public:
string suit;
string value;
void setValue(string v);
void setSuit(string s);
};
void card::setValue(string v)
{
value=v;
}
void card::setSuit(string s)
{
suit=s;
}
int main()
{
string suites[]={"Spades","Hearts","Diamonds","Clubs"};
string values[]={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
card cards[52];
int i=0;
for(int j=0;j<4;j++){
for(int k=0;k<13;k++){
cards[i].setSuit(suites[j]);
cards[i].setValue(values[k]);
i++;
}
}
card player1_cards[26];
card player2_cards[26];
for(int a=0;a<52;a++){
if(a%2==0){
player1_cards[a]=cards[a];
}
else{
player2_cards[a]=cards[a];
}
}
return 0;
}
If you want every 2nd card to be added to player1_cards and the others to be added to player2_cards you can change your for-loop to:
for(int a=1;a<26;a++) {
player1_cards[a] = cards[2*a];
player2_cards[a] = cards[2*a-1];
}
As Eljay said in the comment to your question your index went past the end of the array.
I tried to compile the code myself and it seems like the following code splits the cards in to two different arrays, althought it is not random (which you might want to add if you are doing a card game).
#include <iostream>
#include <string>
class card
{
public:
std::string suit;
std::string value;
void setValue(std::string v);
void setSuit(std::string s);
std::string printSV();
};
void card::setValue(std::string v)
{
value=v;
}
void card::setSuit(std::string s)
{
suit=s;
}
std::string card::printSV() {
return suit + ": " + value + "\n";
}
int main()
{
std::string suites[]={"Spades","Hearts","Diamonds","Clubs"};
std::string values[]={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
card cards[52];
int i=0;
for(int j=0;j<4;j++){
for(int k=0;k<13;k++){
cards[i].setSuit(suites[j]);
cards[i].setValue(values[k]);
i++;
}
}
card player1_cards[26];
card player2_cards[26];
for(int a=1;a<26;a++){
player1_cards[a] = cards[2*a];
player2_cards[a] = cards[2*a-1];
// prints the suite and value for each card in player1's and player2's hand.
std::cout << player1_cards[a].printSV();
std::cout << player2_cards[a].printSV();
}
return 0;
}
Outputs Suite: Value for each of the cards in cards[52].
As #Eljay notes, you are trying to access elements past the end of the arrays. If you were to use a debugger to step through your program, you would see this.
However, this is also a lesson for you to be careful of writing raw loops, with a lot of "magic-number" indices, and prefer using pre-existing patterns from the libraries (especially std::algorithm) when relevant. See this talk by Sean Parent about this general principle.
Specifically, you could have written your program as follows:
#include <range/v3/view.hpp>
#include <string>
#include <array>
#include <iostream>
struct card {
std::string suite;
std::string value;
};
std::ostream& operator<<(std::ostream& os, const card& c)
{
return os << c.value << " of " << c.suite;
}
int main()
{
using namespace ranges;
std::array suites {"Spades","Hearts","Diamonds","Clubs"};
std::array values {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
auto cards =
views::cartesian_product(suites, values) |
views::transform([](const auto& suite_and_value) -> card {
return { std::get<0>(suite_and_value), std::get<1>(suite_and_value) };
});
auto num_players { 2 };
auto player1_cards =
cards | views::stride(num_players);
auto player2_cards =
cards | views::drop(1) | views::stride(num_players);
std::cout << "Player 1 got: " << player1_cards << '\n';
std::cout << "Player 2 got: " << player2_cards << '\n';
}
in which case you don't use any loops and any index variables. This uses Eric Niebler's ranges-v3 library. See also this quick reference to understand faster what's going on in this code if you're not familiar with the terms.
See this Live on GodBolt.
I tried STL sample program using "map".
http://ideone.com/LB8xvh
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
class ItemName
{
char name[80];
public:
ItemName(char *s) { strcpy(name, s); }
char *get() { return name; }
};
bool operator<(ItemName a, ItemName b)
{
return strcmp(a.get(), b.get()) < 0;
}
class ItemObj
{
char str[80];
public:
ItemObj(char *s) { strcpy(str, s); }
char *get() { return str; }
};
char itemdata[][80] = {
"potion", "heal HP",
"key", "unlock a door",
"lamp", "light",
};
int main() {
map<ItemName, ItemObj> items;
for(int i=0; i<3; i++) {
items.insert(
pair<ItemName, ItemObj>(
ItemName(itemdata[i*2]),
ItemObj(itemdata[i*2+1]))); // ***** pair *****
}
map<ItemName, ItemObj>::iterator p;
char str[80];
const int kMaxLoop = 5;
int nLoop = 0;
while(nLoop < kMaxLoop) {
cout << "> ";
cin >> str;
p = items.find(str);
if(p != items.end() ) {
cout << p->second.get() << endl;
} else {
cout << "unknown item." << endl;
}
nLoop++;
}
return 0;
}
In this example, I am not quite sure where the operator "<" is used.
If I comment out the definition of the operator "<", I receive lots of errors.
std::map has a parameter to specify how to compare elements in the map (needed because a map always maintains its contents sorted in order by key). By default, that's std::less<T>.
std::less<T>, in turn, will do the comparison using operator<.
You can create a map of items for which operator< isn't defined, but to do it you need to specify the comparison function/functor explicitly.
That said: your ItemData and ItemObj are both really just doing things that std::string can already do. You could reduce most of the code above to something like this:
std::map<std::string, std::string> items{
{ "potion", "heal HP" },
{ "key", "unlock a door" },
{ "lamp", "light" }
};
It is used internally by the map to place and find entries. Otherwise, find would have to compare the key you supply it against literally every single other entry one by one and you couldn't iterate the map in key order.
Basically, maps efficiently store elements in order. To do that, they have to have some way to know what the order is, and they do that by calling operator< (unless you specify otherwise).
I have a custom class 'team' and one of its attributes is its 'name.' After each 'team' is created, I add it to a vector teamList.
I would like to implement a function that continuously prompts the user for a team name which is not already taken by a team within the teamList. I have the following code:
while (true) {
string newString;
bool flag = true;
getline(cin, newString);
for (int i = 0; i < teamList.size(); i++) {
if (teamList[i].name.compare(newString) == 0) flag = false;
}
if (flag == true) {
return newString;
} else {
cout << "name already taken." << endl;
}
}
However, this code is really ugly; is there a better way to check? Also, a more general question- faced with an issue of ugly code (like this one), what kinds of steps can I take to find a new, cleaner implementation? Thanks.
I would use std::set, which deals with duplicates for you. As an example, you can see that the class is sorted by the string member, and when three are inserted in main, only two stay because two of the insertions have the same string, so they are treated equal.
#include <iostream>
#include <set>
#include <string>
struct SetThing {
SetThing(int value, const std::string &value2) : i(value), s(value2){}
int i;
std::string s;
bool operator<(const SetThing &other) const {
return s < other.s;
}
};
int main() {
std::set<SetThing> s;
s.insert(SetThing(5, "abc"));
s.insert(SetThing(4, "def"));
s.insert(SetThing(6, "abc"));
std::cout << s.size();
}
Now for inserting, you can just reprompt while the second member of the returned pair is false:
do {
//get input
} while (!teamList.insert(somethingBasedOnInput).second);
define an equality operator in team that can compare a team to a string:
bool team::operator==(string s) const
{
return(s==name);
}
Then you can use find:
vector<team>::const_iterator itr = find(teamList.begin(), teamList.end(),
newString);
if(itr!=league.end())
cout << "name already taken" << endl;