JSON that I'm trying to parse looks something like this is:
{
"testBool": true,
"testString": "eu"
}
And my current parser looks really ugly and it really feels like there is a more elegant way to solve this problem. I tried looking into rapidjson::Type for a switch case using document.GetObject().GetType() but it doesn't provide the same type precision that you can achieve by using Get%TypeName%() functions. hashmap is nothing but a wrapper around std::unordered_map<std::string, std::any>.
rapidjson::Document document;
document.Parse(tmp_string.c_str());
for (auto& member : document.GetObject())
{
if (member.value.IsBool())
{
hashmap->addEntry<bool>(member.name.GetString(), member.value.GetBool());
}
else if (member.value.IsString())
{
hashmap->addEntry<std::string>(member.name.GetString(), member.value.GetString());
}
else if (member.value.IsInt())
{
hashmap->addEntry<int>(member.name.GetString(), member.value.GetInt());
}
.....
//And so on
.....
}
my current parser looks really ugly
Beauty is in the eye of the be(er)holder...here's my code:
static void
printField(const Value& e, const string& fld, bool print_newline = true) {
const Value &v = fld.empty() ? e : e[fld];
if (print_newline)
cout << endl << "\t";
if (not fld.empty())
cout << fld << ": ";
if ( /* custom stuff required? */ ) {
// Do custom stuff
else {
switch (v.GetType()) {
case kNullType:
cout << "Null";
break;
case kFalseType:
case kTrueType:
cout << v.GetBool();
break;
case kObjectType: {
bool first = true;
cout << "{ ";
for (const auto &subfield: v.GetObject()) {
if (first)
first = false;
else
cout << ", ";
printField(v, subfield.name.GetString(), false);
}
cout << " }";
break;
}
case kArrayType: {
bool first = true;
cout << "[ ";
for (const auto &arrEntry: v.GetArray()) {
if (first)
first = false;
else
cout << ", ";
printField(arrEntry, "", false);
}
cout << " ]";
break;
}
case kStringType:
cout << v.GetString();
break;
case kNumberType:
if (v.IsInt64())
cout << v.GetInt64();
else if (v.IsUint64())
cout << v.GetUint64();
else
cout << v.GetDouble();
break;
default:
stringstream msg;
msg << "Unexpected RapidJSON Value type: " << v.GetType();
throw logic_error(msg.str());
}
}
}
This uses the stringize stuff to solve some problems, but, if you don't like that, you can get the same effect manually. It subdivides the IsNumber case using a cascading if; if you need more resolution, you can add the other cases to that.
Related
I am trying to make a text based C++ game.
I have a player class set up and I am now working on a method inside that class called displayMenu(), which will ask the user a variety of questions based on their player and send the data to the main/client code and then that data will create an object of player class via a constructor of player class.
My main question is...
I am trying to compare the input (string) from the user to the (string) they need to inputting, but I am getting an error which says "lower()" can not be resolved. I believe you have to compare each character, but I think there may be a more effective way to code this to provide simplicity and readability. What exactly am I doing wrong? What is the best way to code this?
Here is my code...
void Player::displayMenu(std::string& PlaName, std::string& cName, int& lvl, int& HP)
{
std::cout << "Player Creation Menu" << std::endl;
std::cout << "====================" << std::endl;
std::cout << std::endl;
std::cout << std::endl;
std::cout << "What is your name? " << std::endl;
std::cin >> PlaName;
std::cout << "What is your specitality? " << std::endl;
std::cin >> cName;
while(cName.lower() != "brawler" || cName.lower() != "thief" || cName.lower() != "persuader" || cName.lower()
!= "gunman")
{
std::cout << "That is not your true specitality..." << std::endl;
std::cout << "You must pick from { 'Brawler', 'Thief' , 'Persuader', 'Gunman' }" << std::endl;
std::cin >> cName;
}
}
I have several remarks on your original code:
Reading and comparing the strings seems like a bit complicated for this use-case. It is common to see usage of first character as identifier, to make it simpler.
The specialty is a classic example for enum (or enum class, which is enum that you must use always with it's name)
The displayMenu method should not be part of the Player class, since it isn't a behavior (an action) of the player. It should be part of the "Game"/"UI" class.
If you really want to use the complete string in order to identify the specialty, you can use the code examples in the first answer in the link Ayxan put in the comments.
Here is my proposed code:
#include <iostream>
constexpr char INVALID_CHARACTER_INPUT = '#';
enum class CharacterSpecialty
{
BRAWLER,
THIEF,
PERSUADER,
GUNMAN,
NUM_OF_SPECIALITY_TYPES
};
`
class Player
{
public:
Player(const std::string& player_name, CharacterSpecialty char_specialty) :
name(player_name),
specialty(char_specialty)
{
}
private:
std::string name;
CharacterSpecialty specialty;
};
Player displayMenuAndCreatePlayer()
{
std::cout << "\nPlayer Creation Menu\n" << "====================\n\n" << std::endl;
std::cout << "Enter your name: " << std::endl;
std::string player_name{};
std::cin >> player_name;
CharacterSpecialty char_specialty = CharacterSpecialty::NUM_OF_SPECIALITY_TYPES;
while(char_specialty == CharacterSpecialty::NUM_OF_SPECIALITY_TYPES)
{
std::cout << "What is your specialty?\n" << "[B]rawler, [T]hief, [P]ersuader or [G]unman"<< std::endl;
std::string char_type_input;
std::cin >> char_type_input;
char input = char_type_input.size() == 1 ? char_type_input[0] : INVALID_CHARACTER_INPUT;
switch(char_type_input)
{
case 'b':
case 'B':
char_specialty = CharacterSpecialty::BRAWLER;
break;
case 't':
case 'T':
char_specialty = CharacterSpecialty::THIEF;
break;
case 'p':
case 'P':
char_specialty = CharacterSpecialty::PERSUADER;
break;
case 'g':
case 'G':
char_specialty = CharacterSpecialty::GUNMAN;
break;
default:
std::cout << "Invalid Specialty Entered!\n" << std::endl;
break;
}
}
return Player(player_name, char_specialty);
}
int main()
{
Player player = displayMenuAndCreatePlayer();
}
I'm creating a board game (stratego) in c++ and was wondering if it considered a poor practice to return an integer from a class method in order to determine which case in a switch statement to show to the user.
Example:
In stratego, you can't attack board pieces that are part of your own army so I have a message "You cannot attack your own army" when the user tries to do so.
Same thing if a movement is performed that would result in the player jumping off the board, moving too many spaces, etc.
Each of these invalid movements has it's own unique message, but to avoid printing them from the Class.cpp file, which is where the player's moves are validated, I have the Class.cpp file returning an integer to a switch statement in the main() that it was called from.
What is the most recommended way to handle how the messages get called?
class Test
{
public:
Test()
{
}
int Validate_Move(int valid)
{
if (valid > 0 && valid < 5)
{
return 1;
}
else if (valid > 5)
{
return 2;
}
}
};
int main()
{
int entry;
std::cout << "Enter move: ";
std::cin >> entry;
Test obj;
switch (obj.Validate_Move(entry))
{
case 1:
std::cout << "Move is valid" << std::endl;
case 2:
std::cout << "Move is invalid" << std::endl;
default:
std::cout << "Error occured" << std::endl;
}
return 0;
}
There's nothing wrong with that technique. If you want to be more explicit, you could always make an enum
class Test
{
public:
Test() = default;
enum EValidity {eError, eValid, eInvalid};
EValidity Validate_Move(int valid)
{
if (valid > 0 && valid < 5)
{
return eValid;
}
else if (valid > 5)
{
return eInvalid;
}
else
{
return eError;
}
}
};
int main()
{
int entry;
std::cout << "Enter move: ";
std::cin >> entry;
Test obj;
switch (obj.Validate_Move(entry))
{
case Test::eValid:
std::cout << "Move is valid" << std::endl;
break;
case Test::eInvalid:
std::cout << "Move is invalid" << std::endl;
break;
case Test::eError:
std::cout << "Error occured" << std::endl;
break;
default:
assert(false);
}
return 0;
}
I have started working with C++ libcql library for Cassandra.. I am trying to retrieve data from Cassandra using C++ with libcql library..
Whenever I go on the command line using cqlsh and do select like this -
select records from profile_user where user_id = '1';
I always get the below output on the cql command line and in which records column is actually a map in which key is e1 and value is HELLO. In the same way key is e2 and value is HELLO again.. When I created the table in CQL, I created records as the map as I was using collection feature of CQL..
records
--------------------------------
{'e1': 'HELLO', 'e2': 'HELLO'}
Now Coming to C++ world-
Now I am trying to retrieve the same thing from the C++ libcql library... I will be running the same above select query in C++ and I want to return a map which will have e1, e2 as the key and HELLO as there value inside that map... It is possible to do it in C++?
/**
* This method will retrieve the data from Cassandra..
* And then call print_rows method to print it out on the console
*/
void get_attributes(string id){
try{
// some code
//Connection open
connection_open();
execute_query("USE testks;");
//this will give me the result back of the select query
cql_result_t& result = execute_query("select * from profile_user where key ='"+id+"';");
// and this is printing it out on the console
print_rows(result);
// some code
} catch (int e){
// some code here
}
}
Below is the method which will print out the results on the console after running my C++ program -
/**
* This method prints out the result on the console.. *
*
*/
void print_rows(cql::cql_result_t& result) {
while (result.next()) {
for (size_t i = 0; i < result.column_count(); ++i) {
cql::cql_byte_t* data = NULL;
cql::cql_int_t size = 0;
result.get_data(i, &data, size);
std::cout.write(reinterpret_cast<char*>(data), size);
std::cout << " | ";
}
std::cout << std::endl;
}
}
The result that I see on the console after running my above C++ program is something like this -
e1HELLOe2HELLO |
But what I am looking for is - Store the result in a Map in C++, in such a way such that key should be e1 and e2 in the Map.. And the value for them should be HELLO in the same Map... And then iterate the Map and print out the result in C++? Is this possible to do with the current code I have?
If yes, can anyone provide a simple example on this? Thanks...
It is basically a C++ question I guess.. Just retrieve the data and put it into the Map... But the problem I am facing is my background is totally in Java so having little bit hard time to figure out how to do that...
I don't know libcql and I failed to locate any documentation. Looking at the header for cql_result_t indicates that there are functions to determine how many columns there are and how to access them. From the looks of it, you merely copied the demo example which doesn't seem to be a particular good demo. I would start off with refining the print_result() function to look something like the below and see what I'd get. My guess is that you get a "map" type from your query and you'll need to see how to extract and use the corresponding representation by digging through their headers (unless there is somewhere some documentation). The code below mere extracts a few types and mostly prints that it needs to deal with processing the respective type (assuming it actually compiles):
void print_result(cql::cql_result_t& result)
{
std::size_t const columns(result.column_count());
while (result.next()) {
for (std::size_t column(0); column != columns; ++column) {
cql::cql_column_type_enum type;
if (result.column_type(column, type)) {
switch (type) {
case cql::CQL_COLUMN_TYPE_CUSTOM:
std::cout << "todo: process custom type\n";
break;
case cql::CQL_COLUMN_TYPE_ASCII:
std::cout << "todo: process ascii type\n";
break;
case cql::CQL_COLUMN_TYPE_BIGINT:
std::cout << "todo: process bigint type\n";
break;
case cql::CQL_COLUMN_TYPE_BLOB:
std::cout << "todo: process blob type\n";
break;
case cql::CQL_COLUMN_TYPE_BOOLEAN:
std::cout << "todo: process boolean type\n";
break;
case cql::CQL_COLUMN_TYPE_COUNTER:
std::cout << "todo: process counter type\n";
break;
case cql::CQL_COLUMN_TYPE_DECIMAL:
std::cout << "todo: process decimal type\n";
break;
case cql::CQL_COLUMN_TYPE_DOUBLE: {
double value;
if (result.get_double(column, value)) {
std::cout << "column=" << column << " "
<< "double=" << value << "\n";
}
else {
std::cout << "failed to extract double for column "
<< column << "\n";
}
break;
case cql::CQL_COLUMN_TYPE_FLOAT: {
float value;
if (result.get_float(column, value)) {
std::cout << "column=" << column << " "
<< "float=" << value << "\n";
}
else {
std::cout << "failed to extract float for column "
<< column << "\n";
}
break;
case cql::CQL_COLUMN_TYPE_INT: {
int value;
if (result.get_int(column, value)) {
std::cout << "column=" << column << " "
<< "int=" << value << "\n";
}
else {
std::cout << "failed to extract int for column "
<< column << "\n";
}
break;
case cql::CQL_COLUMN_TYPE_TEXT: {
std::string value;
if (result.get_string(column, value)) {
std::cout << "column=" << column << " "
<< "text='" << value << "'\n";
}
else {
std::cout << "failed to extract text for column "
<< column << "\n";
}
break;
case cql::CQL_COLUMN_TYPE_TIMESTAMP:
std::cout << "todo: process timestamp type\n";
break;
case cql::CQL_COLUMN_TYPE_UUID:
std::cout << "todo: process uiid type\n";
break;
case cql::CQL_COLUMN_TYPE_VARCHAR:
std::cout << "todo: process varchar type\n";
break;
case cql::CQL_COLUMN_TYPE_VARINT:
std::cout << "todo: process varint type\n";
break;
case cql::CQL_COLUMN_TYPE_TIMEUUID:
std::cout << "todo: process timeuuid type\n";
break;
case cql::CQL_COLUMN_TYPE_INET:
std::cout << "todo: process inet type\n";
break;
case cql::CQL_COLUMN_TYPE_LIST:
std::cout << "todo: process list type\n";
break;
case cql::CQL_COLUMN_TYPE_MAP:
std::cout << "todo: process map type\n";
break;
case cql::CQL_COLUMN_TYPE_SET:
std::cout << "todo: process set type\n";
break;
}
}
}
}
}
cql_result_t has a method get_map. Use get_map instead of get_data:
cql::cql_result_t *r;
cql::cql_map_t *props = 0;
if (!r->get_map("records", &props)) {
delete props;
// throw an error
}
std::auto_ptr<cql::cql_map_t> p(props);
std::map<std::string, std::string> m;
for (std::size_t i = 0; i < p->size(); ++i) {
std::string key;
if (!p->get_key_string(i, key))
// throw an error
std::string value;
if (!p->get_value_string(i, value))
// throw an error
m.insert(std::make_pair(key, value));
}
I am having an issue getting a vector-based inventory system to work. I am able to list the items in the inventory, but not able to allow a user-selected item to be accessed. Here is the code:
struct aItem
{
string itemName;
int damage;
bool operator==(aItem other)
{
if (itemName == other.itemName)
return true;
else
return false;
}
};
int main()
{
int selection = 0;
aItem healingPotion;
healingPotion.itemName = "Healing Potion";
healingPotion.damage= 6;
aItem fireballPotion;
fireballPotion.itemName = "Potion of Fiery Balls";
fireballPotion.damage = -2;
aItem testPotion;
testPotion.itemName = "I R NOT HERE";
testPotion.damage = 9001;
int choice = 0;
vector<aItem> inventory;
inventory.push_back(healingPotion);
inventory.push_back(healingPotion);
inventory.push_back(healingPotion);
inventory.push_back(fireballPotion);
cout << "This is a test game to use inventory items. Woo!" << endl;
cout << "You're an injured fighter in a fight- real original, I know." << endl;
cout << "1) Use an Item. 2) ...USE AN ITEM." << endl;
switch (selection)
{
case 1:
cout << "Which item would you like to use?" << endl;
int a = 1;
for( vector<aItem>::size_type index = 0; index < inventory.size(); index++ )
{
cout << "Item " << a << ": " << inventory[index].itemName << endl;
a+= 1;
}
cout << "MAKE YOUR CHOICE." << endl << "Choice: ";
cin >> choice;
^^^^
Everything above this line, works. I assume that my problem is the if statement, but I cannot figure out where I am going wrong in my syntax, or if there is a better way to do what I am doing.
if (find(inventory.begin(), inventory.at(choice), healingPotion.itemName) != inventory.end())
cout << "You used a healing potion!";
else
cout << "FIERY BALLS OF JOY!";
break;
case 2:
cout << "Such a jerk, you are." << endl;
break;
}
EDIT: I think I'm not representing this correctly. I need for the player's choice to affect the message displayed. Here's a sample output of the 1st snippet:
Item 1: Healing Potion
Item 2: Healing Potion
Item 3: Healing Potion
Item 4: Potion of Fiery Balls
MAKE YOUR CHOICE.
Choice:
From there, the player can type 1-4, and what I would like is for the number (minus 1, to reflect the vector starting at zero) to be passed to the find, which would then determine (in this small example) if the item at inventory[choice - 1] is a healing potion. If so, display "You used a healing potion!" and if it is not, to display "Fiery balls of joy".
Three problems.
One, Your operator should be declared as:
bool operator==(const aItem& other) const
Two, in this code:
find(inventory.begin(), inventory.at(choice), healingPotion) != inventory.end())
you aren't searching the whole vector from begin() to end() -- you're only searching from begin() to at(choice) where at(choice) points to one-past-the-end of your search set. So you either should do this:
find(&inventory.at(0), &inventory.at(choice), healingPotion) != &inventory.at(choice))
or this...
find(inventory.begin(), inventory.end(), healingPotion.itemName) != inventory.end())
Edit Three, you are trying to compare apples to oranges. You are searching a vector of aItem objects to find a matching aItem object, but the parameter you send to find isn't an aItem object, it is one of the aItem data members.
You should either search for a matching item, like this:
find( inventory.begin(), inventory.end(), healingPotion ) != inventory.end() )
^^^^^^^^
In C++03 you can provide a functor:
#include <functional>
struct match_name : public std::unary_function<aItem, bool>
{
match_name(const string& test) : test_(test) {}
bool operator()(const aItem& rhs) const
{
return rhs.itemName == test_;
}
private:
std::string test_;
};
... and then search for a match using find_if:
find_if( inventory.begin(), inventory.end(), match_name(healingPotion.itemName) ) // ...
In C++11 you can simplify this mess using a closure:
string test = healingPotion.itemName;
if( find_if( inventory.begin(), inventory.end(), [&test](const aItem& rhs)
{
return test == rhs.itemName;
}) == inventory.end() )
{
// not found
}
To add onto John Dibling's answer, the last part is that you are looking for a name, not an aItem.
So it either needs to be:
find(inventory.begin(), inventory.end(), healingPotion) != inventory.end();
where the operator== is defined as:
bool operator==(const aItem& other) const
{
return itemName == other.itemName;
}
Or you need to have your operator== take a string:
find(inventory.begin(), inventory.end(), healingPotion.itemName) != inventory.end();
where the operator== is defined as:
bool operator==(const std::string& name) const
{
return itemName == name;
}
Instead of:
case 1:
cout << "Which item would you like to use?" << endl;
int a = 1;
for( vector<aItem>::size_type index = 0; index < inventory.size(); index++ )
{
cout << "Item " << a << ": " << inventory[index].itemName << endl;
a+= 1;
}
cout << "MAKE YOUR CHOICE." << endl << "Choice: ";
cin >> choice;
if (find(inventory.begin(), inventory.at(choice), healingPotion.itemName) != inventory.end())
cout << "You used a healing potion!";
else
cout << "FIERY BALLS OF JOY!";
break;
case 2:
cout << "Such a jerk, you are." << endl;
break;
}
I neglected to realize that one of the wonders of vectors is the ability to access the value directly- Ryan Guthrie mentioned this in his comment, but I found a simpler "answer". Namely:
case 1:
cout << "Which item would you like to use?" << endl;
//TODO: Learn what the hell the following line actually means.
for( vector<aItem>::size_type index = 0; index < inventory.size(); index++ )
{
//Makes a numerical list.
cout << "Item " << index + 1 << ": " << inventory[index].itemName << endl;
a+= 1;
}
cout << "MAKE YOUR CHOICE." << endl << "Choice: ";
cin >> choice;
//Cannot define this outside of the statement, or it'll initialize to -1
invVecPos = (choice - 1);
//This checks for an invalid response. TODO: Add in non-int checks.
if ((invVecPos) >= inventory.size())
{
cout << "Choice out of bounds. Stop being a dick." << endl;
}
//If the choice is valid, proceed.
else
{
//checking for a certain item type.
if(inventory[invVecPos].itemType == "ITEM_HEALTHPOT")
{
cout << "You used a healing potion!" << endl;
//this erases the potion, and automagically moves everything up a tick.
inventory.erase (inventory.begin() + (invVecPos));
}
else if(inventory[invVecPos].itemType == "ITEM_FIREPOT")
{
cout << "FIERY BALLS OF JOY!" << endl;
}
else
{
//error-handling! Whee!
cout << "Invalid Item type" << endl;
}
}
break;
case 2:
cout << "Why do you have to be so difficult? Pick 1!" << endl;
break;
Thank you, Ryan- with your prodding, I was able to look elsewhere and find the code I needed! The "fixed" code is commented heavily, so anyone else who runs into issues should be able to glean what they need!
I want to make hierarchical data with yaml, unfortunately, I'm not much used to this format, but I'd love to use it because it's human friendly.
Here is my yaml:
items:
list1:
itemA:
item property a
itemB:
list2:
itemC:
itemD:
I'm using yaml-cpp and when I do doc["items"]["list1"]["itemA"], I end up with exception TypedKeyNotFound, and I don't think I understand well how yaml is supposed to be used, I do
doc["items"]["list1"]["itemA"].Type()
but I still have this exception.
Well I managed to better understand how yaml works, and also how it can be parsed. I don't want to get data like this a["fdfds"]["frwrew"]["vbxvxc"], because I don't want to require to know the key before parsing. I managed to make a code which shows the structure of a document using mostly maps and sequences, here it is.
int spaces = 0; // define it in global scope, since unroll is a recursive function.
void unroll(const YAML::Node & node)
{
switch(node.Type())
{
case YAML::NodeType::Map:
{
for(auto it = node.begin(); it != node.end(); ++ it)
{
string s; it.first() >> s;
indent();
cout << s << "\n";
const YAML::Node & dada = it.second();
spaces ++;
unroll(dada);
spaces--;
cout << "\n";
}
break;
}
case YAML::NodeType::Scalar:
{
indent();
string s; node >> s;
cout << "found scalar " << s << "\n";
break;
}
case YAML::NodeType::Null:
{
indent();
cout << "null";
break;
}
case YAML::NodeType::Sequence:
{
//cout << "sequence";
for(auto it = node.begin(); it != node.end(); ++ it)
{
string s; *it >> s;
indent();
cout << s << "\n";
}
break;
}
default: cout << "error: undefined"; break;
}
}