Setting a variable in a child class [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am currently working on a very basic assembler. The assembler needs to take in assembly instructions and output 16-bit binary instructions for use with a computer we are making.
My design strategy has been to create a Command class, that has 3 child classes. There is one for each type of command: A-commands, C-commands, and L-commands. To identify the type of command I am working with, I have included a string command_type that is either "A", "C", or "L" respectively.
EDIT:
I am still having a lot of trouble figuring out how to properly derive these classes. Basically, A and L commands should have a "symbol" string, which represents an integer value that needs to be converted, while C commands have "dest","comp", and "jump" values that also must be accessed, however they do not have "symbol" values.
Command.h
#include <fstream>
#include <string>
class Command {
std::string command_type = "";
protected:
void set_commandType(std::string x){command_type = x;}
public:
Command();
virtual ~Command();
std::string commandType() const {return command_type;}
};
class A_COMMAND : public Command
{
std::string symbol;
public:
A_COMMAND(std::string s);
std::string get_symbol(){return symbol;}; //Returns the symbol or decimal Xxx of the current command #Xxx or (Xxx) . Should be called only when commandType() is A_COMMAND or L_COMMAND.
};
class C_COMMAND : public Command
{
std::string comp;
std::string dest;
std::string jump;
public:
C_COMMAND(std::string s, std::string d, std::string j);
std::string get_comp(){return comp;}; //Returns the comp mnemonic in the current C-command (28 possibilities). Should be called only when commandType() is C_COMMAND.
std::string get_dest(){return dest;}; //Returns the dest mnemonic in the current C-command (8 possibilities). Should be called only when commandType() is C_COMMAND.
std::string get_jump(){return jump;}; //Returns the jump mnemonic in the current C-command (8 possibilities). Should be called only when commandType() is C_COMMAND.
};
class L_COMMAND : public Command
{
std::string symbol;
public:
L_COMMAND(std::string s);
std::string get_symbol(){return symbol;}; //Returns the symbol or decimal Xxx of the current command #Xxx or (Xxx) . Should be called only when commandType() is A_COMMAND or L_COMMAND.
};
Command.cpp
#include "Command.h"
//---------------------------------------------
//A-Command functions
Command::Command(){}
A_COMMAND::A_COMMAND(std::string s) : symbol(s)
{
set_commandType("A");
}
//---------------------------------------------
//C-Command functions
C_COMMAND::C_COMMAND(std::string c, std::string d, std::string j) : comp(c), dest(d), jump(j)
{
set_commandType("C");
}
//---------------------------------------------
//L-Command functions
L_COMMAND::L_COMMAND(std::string s) : symbol(s)
{
set_commandType("L");
}
I have a Parser.cpp and Parser.h that process the input and are responsible for creating a deque of commands:
Parser.h
#include "Command.h"
#include <vector>
#include <deque>
class Parser {
private:
std::deque<Command> commands;
public:
Parser(std::vector<std::string>);
bool hasMoreCommands() //are there more commands in the input?
{
if(commands.size() != 0)
return true;
else
return false;
}
void advance(){commands.pop_front();} //move to next command, should only work if hasMoreCommands returns false}
Command currentCommand(){return commands.front();}
std::vector<std::string> translateCommands(); //convert commands into binary strings
};
Parser.cpp
#include "Parser.h"
#include "Command.h"
#include <vector>
#include <iostream>
#include <string>
#include <unordered_map>
bool inList(std::string& str, std::vector<std::string> list) //check if a given string contains one of the elements in the comp, dest, jump vectors. if so extract string for use in constructor
{
for(auto i = list.begin(); i!=list.end(); ++i)
{
std::size_t found = str.find(*i);
if(found!=std::string::npos)
{
return true;
}
}
return false;
}
Parser::Parser(std::vector<std::string> input) {
std::vector<std::string> dest_list = {"","M","D","MD","A","AM","AD","AMD"}; //all possible dests
std::vector<std::string> comp_list = {"0","1","D","A","!D","!A","-D","-A","D+1","A+1","D-1","A-1","D+A","D-A","A-D","D&A","D|A","M","!M","-M","M+1","M-1","D+M","D-M","M-D","D&M","D|M"}; //all possible comps
std::vector<std::string> jump_list = {"","JGT","JEQ","JGE","JLT","JNE","JLE","JMP"}; //all possible jumps
std::string dest, comp, jump;
std::deque<Command> commands;
for(std::vector<std::string>::const_iterator i = input.begin(); i != input.end(); ++i)
{
std::string line = *i;
if(*line.begin()=='#') //A-command
{
A_COMMAND command(line.substr(1));
std::cout << "Command type: " << command.commandType() << "\n";
std::cout << "symbol: " << command.get_symbol() << "\n";
commands.push_back(command);
}
else if(*line.begin()=='(' && *line.rbegin() == ')' && line.size() > 2) //L-command
{
L_COMMAND command(line.substr(1, line.size() - 2));
std::cout << "Command type: " << command.commandType() << "\n";
std::cout << "symbol: " << command.get_symbol() << "\n";
commands.push_back(command); }
else
{
std::string rhs = line;
std::string dest_string = "";
std::string comp_string = "";
std::string jump_string = "";
size_t equals_pos = line.find('='); //position of = in string, if present
size_t semi_pos = line.find(';'); //position of ; in string, if present
if(equals_pos != line.npos) //if there is an = then we have a dest
{
dest_string = line.substr(0,equals_pos);
rhs = line.substr(equals_pos+1);
}
if(semi_pos != line.npos) //jump
{
comp_string = rhs.substr(0,semi_pos);
jump_string = rhs.substr(semi_pos+1);
}
else //no jump
{
comp_string = rhs;
}
//now confirm if inputs are valid
if(inList(dest_string, dest_list))
dest = dest_string;
else
std::cout << "invalid dest \n";
if(inList(comp_string, comp_list))
comp = comp_string;
else
std::cout << "invalid comp \n";
if(inList(jump_string, jump_list))
jump = jump_string;
else
std::cout << "invalid jump \n";
C_COMMAND command(comp, dest, jump);
std::cout << "Command type: " << command.commandType() << "\n";
std::cout << "dest: " << command.get_dest() << "\n";
std::cout << "comp: " << command.get_comp() << "\n";
std::cout << "jump: " << command.get_jump() << "\n";
commands.push_back(command);
}
}
}
My main.cpp loads the input, and passes it through the parser. The problem I have is that I cannot do anything with the input.
I have tried to write a function like so:
string translateLine(Command command, Code code) //Code is a table for translating the command
{
string output;
if(command.commandType() == "A")
{
string symbol = parser.currentCommand().get_symbol();
cout << symbol << endl;
//perform binary conversion
}
/*else if(command.commandType() == "C")
{
string dest = command.get_dest();
}*/
//shouldn't be any L commands in symbol-less version
else
{
std::cout << "unexpected command value \n";
}
return output;
}
But as soon as I call get_symbol(), the compiler doesn't recognize the function. I know that this is because the base Command doesn't have a get_symbol() function, but I can't figure out how to correctly add the functions to the base class and derive them to the lower 3. I can't just make the pure virtual because not all of the functions are used in each class. How can I correctly accomplish this?

First, if translateLine() should be able to accept A_COMMAND, C_COMMAND, or L_COMMAND objects, then it needs to take a Command* parameter, not a Command parameter. A pointer to a base class can hold a pointer to a class derived from that base, but an object of the base class cannot hold a derived object.
Second, you cannot call a function that belongs to A_COMMAND even with a Command pointer that is really pointing to a A_COMMAND object without doing a dynamic_cast. A dynamic_cast can convert a pointer from Command* to A_COMMAND at run-time and will return NULL if the object pointed to is not really an A_COMMAND object.

The basic problem is that you're reinventing Run-Time Type information. You don't need to add "command_type=A" to class A_COMMAND. C++ already knows what types your objects have, if there's at least one virtual method. And it looks like your class needs a virtual destructor.

Related

Segment fault upon calling method of class

I was able to safely call builder, but builder2 exits with a segment fault.
The compiler does not output any warnings.
I would like to know the cause of the segment fault.
This code is a builder pattern to compose html. ul and li are collected with emplace_back and finally str() is called to build the parts and return them as string.
#include <memory>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
struct HtmlBuilder;
struct HtmlElement {
string name;
string text;
vector<HtmlElement> elements;
const size_t indent_size = 2;
HtmlElement() {}
HtmlElement(const string& name, const string& text)
: name(name), text(text) {}
string str(int indent = 0) const {
ostringstream oss;
string i(indent_size * indent, ' ');
oss << i << "<" << name << ">" << endl;
if (text.size() > 0)
oss << string(indent_size * (indent + 1), ' ') << text << endl;
for (const auto& e : elements) oss << e.str(indent + 1);
oss << i << "</" << name << ">" << endl;
return oss.str();
}
static unique_ptr<HtmlBuilder> build(string root_name) {
return make_unique<HtmlBuilder>(root_name);
}
};
struct HtmlBuilder {
HtmlBuilder(string root_name) { root.name = root_name; }
// void to start with
HtmlBuilder& add_child(string child_name, string child_text) {
HtmlElement e{child_name, child_text};
root.elements.emplace_back(e);
return *this;
}
// pointer based
HtmlBuilder* add_child_2(string child_name, string child_text) {
HtmlElement e{child_name, child_text};
root.elements.emplace_back(e);
return this;
}
string str() { return root.str(); }
operator HtmlElement() const { return root; }
HtmlElement root;
};
int main() {
// easier
HtmlBuilder builder{"ul"};
builder.add_child("li", "hello").add_child("li", "world");
cout << builder.str() << endl;
auto* builder2 = HtmlElement::build("ul")
->add_child_2("li", "hello")
->add_child_2("li", "world");
cout << (*builder2).str() << endl;
return 0;
}
The output results are as follows
hello
world
Segmentation fault (core dumped)
You dynamically create a std::unique_ptr object holding the builder with HtmlElement::build("ul"). This std::unique_ptr object gets destroyed at the end of the full expression which means the object builder2 points to is destroyed and dereferencing it is undefined behaviour resulting in the crash you observed.
I recommend not returning a dynamically allocated builder object at all. Instead move the definition of the HtmlElement::build below the definition of HtmlBuilder. You may also want to consider allowing for move semantics to avoid creating unnecessary copies of the objects:
struct HtmlElement {
...
static HtmlBuilder build(string root_name);
};
struct HtmlBuilder {
HtmlBuilder(string root_name) { root.name = std::move(root_name); }
// void to start with
HtmlBuilder& add_child(string child_name, string child_text) &
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return *this;
}
HtmlBuilder&& add_child(string child_name, string child_text) &&
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return std::move(*this);
}
string str() { return root.str(); }
operator HtmlElement() &&
{
return std::move(root);
}
HtmlElement root;
};
inline HtmlBuilder HtmlElement::build(string root_name) {
return { root_name };
}
int main() {
HtmlBuilder builder{ "ul" };
builder.add_child("li", "hello").add_child("li", "world");
cout << builder.str() << endl;
auto builder2 = HtmlElement::build("ul")
.add_child("li", "hello")
.add_child("li", "world");
cout << builder2.str() << endl;
HtmlElement product = std::move(builder2); // use move constructor for creating product here (works without std::move if HtmlElement::build is in the same full expression as the conversion to HtmlElement)
return 0;
}
As far as I understand, you return a unique_ptr with the build method. However, in your main function when you do this:
auto* builder2 = HtmlElement::build ...
What you actually do is retrieve the raw pointer from the unique_ptr that is returned and then you let this temporary unique_ptr be destroyed. Which in turn frees the instance that was handled by the unique_ptr, which is the very same instance that you retrieved the raw pointer for.
So in the next line:
cout << (*builder2).str() << endl;
You are actually trying to dereference a pointer to invalid memory, since the instance that resided there was just deleted by the temoporary unique pointer that was destroyed in the previous line.
If you remove the raw pointer from the "auto" part like this:
auto builder2 = HtmlElement::build("ul") ...
Then you will have a smart pointer to your instance.
Then you can call the str method on the smart pointer as if it was a pointer to your instance:
cout << builder2->str() << endl;

A case od the most vexing parse

My code keeps giving me error Invalid operands to binary expression ('std::__1::ostream' (aka 'basic_ostream<char>') and 'const bus') Is this a case of the most vexing parse? if so how to fix it. I am trying to print out objects stored in vector. std::vector<bus> v = {} is my vector to contain the object whereas bus is my class
#include <iostream>
#include <vector>
class bus{
public:
int carNum, releaseYear;
};
int temp1, temp2;
void print(std::vector<bus> const &input)
{
for (auto it = input.cbegin(); it != input.cend(); it++)
{
std::cout << *it << ' '<< std::endl;
}
}
int main()
{
bus bus1;
bus1.carNum = 0;
bus1.releaseYear = 0;
bus bus2;
bus2.carNum = 0;
bus2.releaseYear = 0;
// Create a vector containing objects
std::vector<bus> v = {};
// Add two more integers to vector
std::cout<<"enter number"<<std::endl;
std::cin>>temp1;
temp1 = bus1.carNum;
std::cout<<"enter year"<<std::endl;
std::cin>>temp2;
temp2 = bus1.releaseYear;
v.push_back(bus1);
print(v)
}
There is no vexing parse here. You simply need to overload the stream insertion operator for the bus type. Since bus only has public data members, this can be a non-friend function:
std::ostream& operator<<(std::ostream& stream, bus const& b)
{
stream << "#" << b.carNum << " Year" << b.releaseYear;
return stream;
}
You can of course, format your output however you want.

How do i parse a text file and use the file input in constructors to create a container of objects

I have a program that reads a text file line by line for names, and stores those names as objects using a constructor. The constructor is being used to make a vector of all the names. However, my problem is that i need my names to have attributes tied to them, i have the constructor for the attributes but i have no clue how to parse the text file to separate the names from the attributes, and then how to store the attributes with the names.
My code works for just names in the file, and i cannot simply use a delimiter in this case since i need to look for "Name" then attribute attribute attribute.
Example:
"Baron Samedi" Mage Magical Ranged
the name needs to be stored without the quotes included and then the attributes need to be constructed in a container that corresponds to the names, so that when i call .getAttackType for a specific name (object) it will return the appropriate type.
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <exception>
#include <sstream>
#include <ctime>
#include <random>
enum class AttackType {
MELEE,
RANGE
};
enum class DamageType {
MAGICAL,
PHYSICAL
};
enum class AbilityType {
Mage,
Guardian,
Warrior,
Hunter,
Assassin
};
struct EntityAttributes {
AttackType attackType;
DamageType damageType;
AbilityType abilityType;
};
class Entity {
private:
std::string name_;
EntityAttributes attribs_;
public:
Entity() = default;
explicit Entity(const std::string& name) :
name_(name)
{}
Entity(const std::string& name, EntityAttributes attribs) :
name_(name),
attribs_(attribs)
{}
void assignAttributes(EntityAttributes attribs) {
attribs_ = attribs;
}
std::string getName() const { return name_; }
AbilityType getClassType() const { return attribs_.abilityType; }
AttackType getAttackType() const { return attribs_.attackType; }
DamageType getDamageType() const { return attribs_.damageType; }
};
void getAllLinesFromFile(const char* filename, std::vector<std::string>& output) {
std::ifstream file(filename);
if (!file)
{
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error(stream.str());
}
std::string line;
while (std::getline(file, line)) {
if (line.size() > 0)
output.push_back(line);
}
file.close();
}
int main() {
srand(time(NULL));
try {
// This will store all of the names in from the text file.
std::vector<std::string> names;
getAllLinesFromFile("Names.txt", names);
// This will give us a container of all of our entities with a provided name
// after this container is filled you can go back later and add the addition
// properties, or if you read the properties in from a file as well you can use
// the other Entity constructor to generate all of them with their names and properties
std::vector<Entity> entities;
for (auto& n : names) {
Entity e(n);
entities.push_back(e);
}
// Check array of Entities
std::cout << "There are " << entities.size() << " entities\n";
for (auto& e : entities) {
std::cout << e.getName() << '\n';
}
}
catch (std::runtime_error& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
system("pause");
return EXIT_SUCCESS;
}```
With your original file format:
#include <string> // std::string
#include <vector> // std::vector<>
#include <iostream> // std::cin, std::cout, std::cerr
#include <fstream> // std::ifstream
#include <ctime> // std::time()
#include <cstdlib> // std::rand(), EXIT_FAILURE
#include <iterator> // std::istream_iterator<>
#include <limits> // std::numeric_limits<>
#include <algorithm> // std::find()
char const *AbilityTypeStrings[]{ "Mage", "Guardian", "Warrior", "Hunter", "Assassin" };
enum class AbilityType {
Mage,
Guardian,
Warrior,
Hunter,
Assassin
};
char const *DamageTypeStrings[]{ "Magical", "Physical" };
enum class DamageType {
MAGICAL,
PHYSICAL
};
char const *AttackTypeStrings[]{ "Melee", "Range" };
enum class AttackType {
MELEE,
RANGE
};
struct EntityAttributes {
AttackType attackType;
DamageType damageType;
AbilityType abilityType;
};
class Entity {
std::string name;
EntityAttributes attributes;
public:
Entity(std::string const &name = {}, EntityAttributes const &attributes = {}) :
name(name),
attributes(attributes)
{}
friend std::istream& operator>>(std::istream &is, Entity &entity)
{
// ignore everything up to the first '"'.
is.ignore(std::numeric_limits<std::streamsize>::max(), '\"');
// try to read the entities name
std::string name;
if (!std::getline(is, name, '\"')) {
return is;
}
// try to read its abilities
std::string abilities;
if (!(is >> abilities)) {
return is;
}
EntityAttributes attributes{};
auto ability_type{ std::find(std::begin(AbilityTypeStrings), std::end(AbilityTypeStrings), abilities) };
if (ability_type == std::end(AbilityTypeStrings)) {
is.setstate(std::ios::failbit);
return is;
}
attributes.abilityType = static_cast<AbilityType>(ability_type - std::begin(AbilityTypeStrings));
std::string damage;
if (!(is >> damage)) {
return is;
}
auto damage_type{ std::find(std::begin(DamageTypeStrings), std::end(DamageTypeStrings), damage) };
if (damage_type == std::end(DamageTypeStrings)) {
is.setstate(std::ios::failbit);
return is;
}
attributes.damageType = static_cast<DamageType>(damage_type - std::begin(DamageTypeStrings));
std::string attack;
if (!(is >> attack)) {
return is;
}
auto attack_type{ std::find(std::begin(AttackTypeStrings), std::end(AttackTypeStrings), attack) };
if (attack_type == std::end(AttackTypeStrings)) {
is.setstate(std::ios::failbit);
return is;
}
attributes.attackType = static_cast<AttackType>(attack_type - std::begin(AttackTypeStrings));
entity.name = name;
entity.attributes = attributes;
return is;
}
friend std::ostream& operator<<(std::ostream &os, Entity const &entity)
{
os << '\"' << entity.name << "\"\n" << DamageTypeStrings[static_cast<std::size_t>(entity.attributes.damageType)] << '\n'
<< AbilityTypeStrings[static_cast<std::size_t>(entity.attributes.abilityType)] << '\n'
<< AttackTypeStrings[static_cast<std::size_t>(entity.attributes.attackType)] << '\n';
return os;
}
};
int main()
{
std::srand(static_cast<unsigned>(std::time(nullptr))); // why do you include <random> when
// you're using the old old C stuff?
char const *filename{ "test.txt" };
std::ifstream is{ filename };
if (!is.is_open()) {
std::cerr << "Couldn't open \"" << filename << "\" for reading :(\n\n";
return EXIT_FAILURE;
}
std::vector<Entity> entities{ std::istream_iterator<Entity>{ is }, std::istream_iterator<Entity>{} };
for (auto const &e : entities)
std::cout << e << '\n';
}
How you parse your data all depends on your file structure and remember you can structure your file to your own specific format as long as you stay consistent with your convention.
You can do it as you attempted:
SingleWordName Class DamageType AttackType
"Multiple Word Name" Class DamageType AttackType
Then you would have to parse each line (string) of text individually but you could also make it simpler by changing the structure of your text file. If you know that there is going be a similar pattern throughout that doesn't change than something like this might make it easier for your.
SingleWordName or Multiple Word Name
AbilityType
AttackType
DamageType
NextName
AbilityType
AttackType
DamageType
Then if you structured it this way you know that each line contains a string, the first line in a set would be the name variable to your Entity class, and the next three would fill the Attributes Structure within that class. Then a blank line that can be ignored. This blank line is only for visual reading references for the human reader to easily distinguish one entity to the next.
You could even structure your file like this:
Entity Name Single Or Multiple Words
AbilityType AttackType DamageType
Next Entity
AbilityType AttackType DamageType
This kind of structure would take the first line of text or string and sets the name of the entity, then the second line contains all of the fields for the Attributes structure.
This case would work easily enough if all of your Attributes are all Single Words Only. If You have attributes that are multiple words and don't like the idea of enclosing them in quotes, brackets, braces, etc. you could just use an underscore between each word such as:
Baron_Samedi
Then once you have this word, you can look for any _ in that word and remove it from the string and replace it with a ' '.
There are multiple ways to parse string data, and it all depends on two major things: first your data or class structures and second your file structure that you are using to represent that data structure. Once you have these two in place you have your foundation, then it is a matter of using that information and building your parsing function from it.
Edit - follow up on the OPs comment about the confusion of parsing a string between quotes:
If you have a string in quotes " "; The issue here is you have to do more than one search on that string for the single character " and you need to keep an index of where you found the first_occurence_of " as well as the next_occurence_of ". After finding the first occurrence and saving its index location you then have to iterate through that sequence of characters like an array until you find the next " and again you will need to save that index location as well. Then you have to get the difference between the two.
As for a simple example we will use "Hello" with the quotes being our string that has 7 total characters. The first " is at index 0 and the next is at index 6. You then would need the substring [(first+1), (next-1)] from this original string.
[0][1][2][3][4][5][6]
["][H][e][l][l][o]["]
As you can see above the first " is at index 0 and the next is at index 6. The total length of the string is 7. We can use this information and the provided string functions from the stl to construct a substring from this original string. But we have to search the string from beginning to end to find both locations of our beginning and ending delimiters.
// Pseudo Code
substring = [(original.firstIndexFound(") + 1) to (original.nextIndexFound(")-1)];
// Where the [] indicates inclusive at that position...
substring = [(0+1) to (6-1)] = [1,2,3,4,5]
// broken down into individual array indices..
substring[0] = original[1]
substring[1] = original[2]
substring[2] = original[3]
substring[3] = original[4]
substring[4] = original[5]
// Visual Translation:
// Original:
[0][1][2][3][4][5][6]
["][H][e][l][l][o]["]
// Sub
[0][1][2][3][4]
[H][e][l][l][o]
This will still work if there are spaces between words because the function that you call with your delimiter is not looking for a , but a " or any other character that you are determining to be your delimiter.
Here is a simple program to demonstrate parsing a string between " ".
#include <string>
#include <iostream>
int main() {
std::string str = { "\"Hello\"" }; // need the \" for quote in the string
std::cout << "Original String = " << str << '\n';
size_t first = str.find_first_of('"');
size_t next = str.find_first_of('"', first + 1);
std::cout << "First index at: " << first << "\nNext index at: " << next << '\n';
std::string sub = str.substr(first + 1, next - 1);
std::cout << "Substring = " << sub << '\n';
return 0;
}
-Output-
Original String = "Hello"
First index at: 0
Next index at: 6
Substring = Hello
-Note-
There is no sanity check in the above to determine if there was no " character in the string at all. This is easy enough to do, all you have to do is check first if the index or iterator to the string is not at the end position, if it is at the end position then just return back the original string, otherwise just perform the above calculations without any changes.

map inserting keys but not value

I am playing with c++ code today. Learning about std containers. I'm trying to insert and update data in a std::map but for some reason I can't insert values into a map. Keys will insert but not values. The code at the bottom will print the following if you enter something into the terminal that opens. In this example I entered "test". Anyway, my questions are, why is the insert returning false, why in the value not inserting?
test
first
failed
Context1 :
Here is the code:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <map>
#include <random>
static std::map<std::string, std::string> currentFullState;
static const std::string sDEFAULT_STRING = "";
void PringCurrentState()
{
std::map<std::string, std::string>::iterator stateData = currentFullState.begin();
while (stateData != currentFullState.end())
{
std::cout << stateData->first << " : ";
std::cout << stateData->second << std::endl;
stateData++;
};
}
void UpdateState(std::string context, std::string data)
{
if (currentFullState[context] == sDEFAULT_STRING)
{
// first entry, possibly special?
std::cout << "first" << std::endl;
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
if (result.second == false)
std::cout << "failed" << std::endl;
else
std::cout << "good" << std::endl;
}
else if (data != currentFullState[context])
{
// change in value
}
else
{
currentFullState[context] == data;
}
}
void DoWork()
{
if (rand() % 2)
{
UpdateState("Context1", "Data1");
}
else
{
UpdateState("Context2", "Data2");
}
}
int main()
{
std::string command = "";
for (;;)
{
PringCurrentState();
std::cin >> command;
DoWork();
if (command == "q")
{
break;
}
}
return 0;
}
Why does the insert not work?
Certainly would help if you wrote
currentFullState[context] = data;
instead of
currentFullState[context] == data;
Also
auto result = currentFullState.insert(std::make_pair(context, data));
should be preferred to
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
Slightly surprised that the second one compiles.
=========================================================================
The real reason the insert fails is that you are adding that key for the second time. This is the first time
if (currentFullState[context] == sDEFAULT_STRING)
operator[] on a map always adds the key to the map. This is why your second attempt to add with
auto result = currentFullState.insert(std::make_pair(context, data.c_str()));
fails, the key is already present. If you had written
currentFullState[context] = data;
Then it would work.

Difficulties with string declaration/reference parameters (c++)

Last week I got an homework to write a function: the function gets a string and a char value and should divide the string in two parts, before and after the first occurrence of the existing char.
The code worked but my teacher told me to do it again, because it is not well written code. But I don't understand how to make it better. I understand so far that defining two strings with white spaces is not good, but i get out of bounds exceptions otherwise. Since the string input changes, the string size changes everytime.
#include <iostream>
#include <string>
using namespace std;
void divide(char search, string text, string& first_part, string& sec_part)
{
bool firstc = true;
int counter = 0;
for (int i = 0; i < text.size(); i++) {
if (text.at(i) != search && firstc) {
first_part.at(i) = text.at(i);
}
else if (text.at(i) == search&& firstc == true) {
firstc = false;
sec_part.at(counter) = text.at(i);
}
else {
sec_part.at(counter) = text.at(i);
counter++;
}
}
}
int main() {
string text;
string part1=" ";
string part2=" ";
char search_char;
cout << "Please enter text? ";
getline(cin, text);
cout << "Please enter a char: ? ";
cin >> search_char;
divide(search_char,text,aprt1,part2);
cout << "First string: " << part1 <<endl;
cout << "Second string: " << part2 << endl;
system("PAUSE");
return 0;
}
I would suggest you, learn to use c++ standard functions. there are plenty utility function that can help you in programming.
void divide(const std::string& text, char search, std::string& first_part, std::string& sec_part)
{
std::string::const_iterator pos = std::find(text.begin(), text.end(), search);
first_part.append(text, 0, pos - text.begin());
sec_part.append(text, pos - text.begin());
}
int main()
{
std::string text = "thisisfirst";
char search = 'f';
std::string first;
std::string second;
divide(text, search, first, second);
}
Here I used std::find that you can read about it from here and also Iterators.
You have some other mistakes. you are passing your text by value that will do a copy every time you call your function. pass it by reference but qualify it with const that will indicate it is an input parameter not an output.
Why is your teacher right ?
The fact that you need to initialize your destination strings with empty space is terrible:
If the input string is longer, you'll get out of bound errors.
If it's shorter, you got wrong answer, because in IT and programming, "It works " is not the same as "It works".
In addition, your code does not fit the specifications. It should work all the time, independently of the current value which is stored in your output strings.
Alternative 1: your code but working
Just clear the destination strings at the beginning. Then iterate as you did, but use += or push_back() to add chars at the end of the string.
void divide(char search, string text, string& first_part, string& sec_part)
{
bool firstc = true;
first_part.clear(); // make destinations strings empty
sec_part.clear();
for (int i = 0; i < text.size(); i++) {
char c = text.at(i);
if (firstc && c != search) {
first_part += c;
}
else if (firstc && c == search) {
firstc = false;
sec_part += c;
}
else {
sec_part += c;
}
}
}
I used a temporary c instead of text.at(i) or text\[i\], in order to avoid multiple indexing But this is not really required: nowadays, optimizing compilers should produce equivalent code, whatever variant you use here.
Alternative 2: use string member functions
This alternative uses the find() function, and then constructs a string from the start until that position, and another from that position. There is a special case when the character was not found.
void divide(char search, string text, string& first_part, string& sec_part)
{
auto pos = text.find(search);
first_part = string(text, 0, pos);
if (pos== string::npos)
sec_part.clear();
else sec_part = string(text, pos, string::npos);
}
As you understand yourself these declarations
string part1=" ";
string part2=" ";
do not make sense because the entered string in the object text can essentially exceed the both initialized strings. In this case using the string method at can result in throwing an exception or the strings will have trailing spaces.
From the description of the assignment it is not clear whether the searched character should be included in one of the strings. You suppose that the character should be included in the second string.
Take into account that the parameter text should be declared as a constant reference.
Also instead of using loops it is better to use methods of the class std::string such as for example find.
The function can look the following way
#include <iostream>
#include <string>
void divide(const std::string &text, char search, std::string &first_part, std::string &sec_part)
{
std::string::size_type pos = text.find(search);
first_part = text.substr(0, pos);
if (pos == std::string::npos)
{
sec_part.clear();
}
else
{
sec_part = text.substr(pos);
}
}
int main()
{
std::string text("Hello World");
std::string first_part;
std::string sec_part;
divide(text, ' ', first_part, sec_part);
std::cout << "\"" << text << "\"\n";
std::cout << "\"" << first_part << "\"\n";
std::cout << "\"" << sec_part << "\"\n";
}
The program output is
"Hello World"
"Hello"
" World"
As you can see the separating character is included in the second string though I think that maybe it would be better to exclude it from the both strings.
An alternative and in my opinion more clear approach can look the following way
#include <iostream>
#include <string>
#include <utility>
std::pair<std::string, std::string> divide(const std::string &s, char c)
{
std::string::size_type pos = s.find(c);
return { s.substr(0, pos), pos == std::string::npos ? "" : s.substr(pos) };
}
int main()
{
std::string text("Hello World");
auto p = divide(text, ' ');
std::cout << "\"" << text << "\"\n";
std::cout << "\"" << p.first << "\"\n";
std::cout << "\"" << p.second << "\"\n";
}
Your code will only work as long the character is found within part1.length(). You need something similar to this:
void string_split_once(const char s, const string & text, string & first, string & second) {
first.clear();
second.clear();
std::size_t pos = str.find(s);
if (pos != string::npos) {
first = text.substr(0, pos);
second = text.substr(pos);
}
}
The biggest problem I see is that you are using at where you should be using push_back. See std::basic_string::push_back. at is designed to access an existing character to read or modify it. push_back appends a new character to the string.
divide could look like this :
void divide(char search, string text, string& first_part,
string& sec_part)
{
bool firstc = true;
for (int i = 0; i < text.size(); i++) {
if (text.at(i) != search && firstc) {
first_part.push_back(text.at(i));
}
else if (text.at(i) == search&& firstc == true) {
firstc = false;
sec_part.push_back(text.at(i));
}
else {
sec_part.push_back(text.at(i));
}
}
}
Since you aren't handling exceptions, consider using text[i] rather than text.at(i).