Overloading istream>> to read comma-separated inputClass to read comma separated data from diskCalculator - C++ operator-overloadingSelecting results as a comma-separated stringCreating an istream peekerCustomized streambuffer for C++ istreamC++ operator overloading for matrix operationsC++ operator overloading for matrix operations - follow-upEncapsulation preserving operator= overloading in C++Operator Overloading Tricks in C++C++ Read istream into string with exceptions
Fine Tuning of the Universe
How do scammers retract money, while you can’t?
Method to test if a number is a perfect power?
What is paid subscription needed for in Mortal Kombat 11?
How did Arya survive the stabbing?
How can I get through very long and very dry, but also very useful technical documents when learning a new tool?
How easy is it to start Magic from scratch?
Closest Prime Number
Pre-amplifier input protection
Why didn't Theresa May consult with Parliament before negotiating a deal with the EU?
Did Dumbledore lie to Harry about how long he had James Potter's invisibility cloak when he was examining it? If so, why?
Is the destination of a commercial flight important for the pilot?
Why not increase contact surface when reentering the atmosphere?
Unreliable Magic - Is it worth it?
How can I kill an app using Terminal?
How do I find the solutions of the following equation?
Opposite of a diet
Sequence of Tenses: Translating the subjunctive
Why Were Madagascar and New Zealand Discovered So Late?
Tiptoe or tiphoof? Adjusting words to better fit fantasy races
What happens if you roll doubles 3 times then land on "Go to jail?"
Detecting if an element is found inside a container
Term for the "extreme-extension" version of a straw man fallacy?
Is a stroke of luck acceptable after a series of unfavorable events?
Overloading istream>> to read comma-separated input
Class to read comma separated data from diskCalculator - C++ operator-overloadingSelecting results as a comma-separated stringCreating an istream peekerCustomized streambuffer for C++ istreamC++ operator overloading for matrix operationsC++ operator overloading for matrix operations - follow-upEncapsulation preserving operator= overloading in C++Operator Overloading Tricks in C++C++ Read istream into string with exceptions
$begingroup$
I have the following very simple class:
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream&, accusation&);
;
I have overloaded my extraction from istream operator as follows:
std::istream& operator>>(std::istream& is, accusation& readable)
std::vector<std::string> accusation;
std::string token, word;
//divide by commas
while (std::getline(is, token, ','))
std::string pushable;
std::stringstream ss(token);
while (ss >> word) pushable += word + " ";
if (pushable.size() != 0) pushable.pop_back(); //remove that last white space
std::transform(pushable.begin(), pushable.end(), pushable.begin(), ::tolower);
accusation.push_back(pushable);
if (accusation.size() == 3)
is.clear();
bool valid false ;
//check it matches one of the clue::characters
for (const auto& character : clue::characters)
if (accusation[0] == character)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::weapons
for (const auto& weapon : clue::weapons)
if (accusation[1] == weapon)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::places
for (const auto& place : clue::places)
if (accusation[2] == place)
valid = true;
break;
if (valid)
readable.murderer = accusation[0];
readable.weapon = accusation[1];
readable.place = accusation[2];
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
return is;
I am reading input as green, dagger, kitchen
and storing it in my accusation. The first element has to be in clue::characters
(an array of possible game characters), second element in clue::weapons
, and third element in clue::places
.
Can somebody suggest a cleaner way to overload this operator? The code works as expected, but I believe that there is a lot of space for improvements. Any push into the right direction is highly appreciated.
c++ beginner parsing stream overloading
New contributor
$endgroup$
add a comment |
$begingroup$
I have the following very simple class:
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream&, accusation&);
;
I have overloaded my extraction from istream operator as follows:
std::istream& operator>>(std::istream& is, accusation& readable)
std::vector<std::string> accusation;
std::string token, word;
//divide by commas
while (std::getline(is, token, ','))
std::string pushable;
std::stringstream ss(token);
while (ss >> word) pushable += word + " ";
if (pushable.size() != 0) pushable.pop_back(); //remove that last white space
std::transform(pushable.begin(), pushable.end(), pushable.begin(), ::tolower);
accusation.push_back(pushable);
if (accusation.size() == 3)
is.clear();
bool valid false ;
//check it matches one of the clue::characters
for (const auto& character : clue::characters)
if (accusation[0] == character)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::weapons
for (const auto& weapon : clue::weapons)
if (accusation[1] == weapon)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::places
for (const auto& place : clue::places)
if (accusation[2] == place)
valid = true;
break;
if (valid)
readable.murderer = accusation[0];
readable.weapon = accusation[1];
readable.place = accusation[2];
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
return is;
I am reading input as green, dagger, kitchen
and storing it in my accusation. The first element has to be in clue::characters
(an array of possible game characters), second element in clue::weapons
, and third element in clue::places
.
Can somebody suggest a cleaner way to overload this operator? The code works as expected, but I believe that there is a lot of space for improvements. Any push into the right direction is highly appreciated.
c++ beginner parsing stream overloading
New contributor
$endgroup$
1
$begingroup$
Welcome to CR, Could you please change the title to show the requirement from business/exercise point of view rather than your concerns. Concerns should go into the body of the question. :)
$endgroup$
– 422_unprocessable_entity
11 hours ago
add a comment |
$begingroup$
I have the following very simple class:
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream&, accusation&);
;
I have overloaded my extraction from istream operator as follows:
std::istream& operator>>(std::istream& is, accusation& readable)
std::vector<std::string> accusation;
std::string token, word;
//divide by commas
while (std::getline(is, token, ','))
std::string pushable;
std::stringstream ss(token);
while (ss >> word) pushable += word + " ";
if (pushable.size() != 0) pushable.pop_back(); //remove that last white space
std::transform(pushable.begin(), pushable.end(), pushable.begin(), ::tolower);
accusation.push_back(pushable);
if (accusation.size() == 3)
is.clear();
bool valid false ;
//check it matches one of the clue::characters
for (const auto& character : clue::characters)
if (accusation[0] == character)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::weapons
for (const auto& weapon : clue::weapons)
if (accusation[1] == weapon)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::places
for (const auto& place : clue::places)
if (accusation[2] == place)
valid = true;
break;
if (valid)
readable.murderer = accusation[0];
readable.weapon = accusation[1];
readable.place = accusation[2];
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
return is;
I am reading input as green, dagger, kitchen
and storing it in my accusation. The first element has to be in clue::characters
(an array of possible game characters), second element in clue::weapons
, and third element in clue::places
.
Can somebody suggest a cleaner way to overload this operator? The code works as expected, but I believe that there is a lot of space for improvements. Any push into the right direction is highly appreciated.
c++ beginner parsing stream overloading
New contributor
$endgroup$
I have the following very simple class:
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream&, accusation&);
;
I have overloaded my extraction from istream operator as follows:
std::istream& operator>>(std::istream& is, accusation& readable)
std::vector<std::string> accusation;
std::string token, word;
//divide by commas
while (std::getline(is, token, ','))
std::string pushable;
std::stringstream ss(token);
while (ss >> word) pushable += word + " ";
if (pushable.size() != 0) pushable.pop_back(); //remove that last white space
std::transform(pushable.begin(), pushable.end(), pushable.begin(), ::tolower);
accusation.push_back(pushable);
if (accusation.size() == 3)
is.clear();
bool valid false ;
//check it matches one of the clue::characters
for (const auto& character : clue::characters)
if (accusation[0] == character)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::weapons
for (const auto& weapon : clue::weapons)
if (accusation[1] == weapon)
valid = true;
break;
if (valid)
valid = false;
//check it matches one of the clue::places
for (const auto& place : clue::places)
if (accusation[2] == place)
valid = true;
break;
if (valid)
readable.murderer = accusation[0];
readable.weapon = accusation[1];
readable.place = accusation[2];
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
else
is.setstate(std::ios_base::failbit);
return is;
I am reading input as green, dagger, kitchen
and storing it in my accusation. The first element has to be in clue::characters
(an array of possible game characters), second element in clue::weapons
, and third element in clue::places
.
Can somebody suggest a cleaner way to overload this operator? The code works as expected, but I believe that there is a lot of space for improvements. Any push into the right direction is highly appreciated.
c++ beginner parsing stream overloading
c++ beginner parsing stream overloading
New contributor
New contributor
edited 10 hours ago
Daniel Duque
New contributor
asked 12 hours ago
Daniel DuqueDaniel Duque
485
485
New contributor
New contributor
1
$begingroup$
Welcome to CR, Could you please change the title to show the requirement from business/exercise point of view rather than your concerns. Concerns should go into the body of the question. :)
$endgroup$
– 422_unprocessable_entity
11 hours ago
add a comment |
1
$begingroup$
Welcome to CR, Could you please change the title to show the requirement from business/exercise point of view rather than your concerns. Concerns should go into the body of the question. :)
$endgroup$
– 422_unprocessable_entity
11 hours ago
1
1
$begingroup$
Welcome to CR, Could you please change the title to show the requirement from business/exercise point of view rather than your concerns. Concerns should go into the body of the question. :)
$endgroup$
– 422_unprocessable_entity
11 hours ago
$begingroup$
Welcome to CR, Could you please change the title to show the requirement from business/exercise point of view rather than your concerns. Concerns should go into the body of the question. :)
$endgroup$
– 422_unprocessable_entity
11 hours ago
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
95 percent of programming is looking for redundancies and eliminating them.
For example, why do you bother with reading strings into accusations[]
first, and then later copying them into readable.murderer
et cetera? Why not just read them directly into readable.murderer
? This would have the bonus of eliminating those "magic number" indices 0, 1, and 2, and replacing them with readable (no pun intended) identifiers.
std::getline(is, readable.murderer, ',');
std::getline(is, readable.weapon, ',');
std::getline(is, readable.place, ','); // shouldn't this last one be 'n' not ','?
You should test your code and see if it does what you wanted.
std::istringstream iss(
"Mr Green, lead pipe, conservatoryn"
"Mrs Peacock, noose, kitchen"
);
accusation acc;
iss >> acc;
This reads 5 items into accusation
. Is this what you wanted to happen?
Reduce repetition. You have the following snippet repeated three times:
for (const auto& THING : THINGS)
if (accusation[INDEX] == THING)
valid = true;
break;
So, first of all, we wrap the loop body in curly braces to protect against goto fail; and then we factor it out into a function.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
for (auto&& elt : vec)
if (elt == value)
return true;
return false;
And then our main function's code can become simply
bool valid = vector_contains(clue::characters, readable.murderer)
&& vector_contains(clue::weapons, readable.weapon)
&& vector_contains(clue::places, readable.place);
if (!valid)
is.setstate(std::ios_base::failbit);
The body of vector_contains
could also be implemented simply by using an STL algorithm, e.g.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::count(vec.begin(), vec.end(), value);
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::find(vec.begin(), vec.end(), value) != vec.end();
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::any_of(vec.begin(), vec.end(), [&](const auto& elt)
return elt == value;
);
I named the function vector_contains
, rather than simply contains
, because in my estimation there is a very real possibility that C++2a might add std::contains
to the library and thus break any code using ADL calls to contains
.
Minor nits:
I strongly recommend making all your constructors
explicit
, to eliminate bugs from unintentional implicit conversions. (Yes, even your multi-argument constructors.)I strongly recommend making
operator>>
andoperator<<
into inline friend functions — define them right inside the body of your class. This will make them findable only via ADL, and is generally what you want. It'll look a lot more reasonable, too, once you've refactored youroperator>>
to be only five or six lines long! :)
You're also doing something weird with stringstream
to remove whitespace from the ends of each piece of the string. You should factor that out into a helper function, and then simplify it. Say,
std::string strip(const std::string& s)
int i = 0;
while (isspace(s[i])) ++i;
int j = s.size();
while (j >= 1 && isspace(s[j-1])) --j;
return s.substr(i, j-i);
https://wandbox.org/permlink/uVSolN0Nepk48Mgm
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
explicit accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream& is, accusation& a)
!vector_contains(clue::places, a.place))
is.setstate(std::ios_base::failbit);
return is;
;
Deciding whether your std::transform
lowercasing should be removed, kept, or folded into the helper function vector_contains
(renaming that function to indicate its new purpose, and using a non-mutating facility such as strcasecmp
) is left as an exercise for the reader.
$endgroup$
$begingroup$
Thanks for all your feedback, it has been very eye-opening reading all your suggestions. Regarding your 'strip' function, it only removes white spaces from the beginning and the end of a string; opposed to what I was doing which deleted extra white space between words as well. Nevertheless I get your point, and will improve my code from all your suggestions.
$endgroup$
– Daniel Duque
9 hours ago
1
$begingroup$
Given that you mademr green
acceptable as a synonym forMr Green
, maybe you should consider whethermrgreen
should be acceptable as well. Then you wouldn't even need to remove spaces; you could just write a non-mutating string comparison, similar tostrcasecmp
, that ignores all whitespace too. Personally, I would go the other direction and force the user to enterMr Green
using that one exact spelling, to increase simplicity and ease-of-teaching-the-interface. If you do want to do clever fuzzy matching, look at en.wikipedia.org/wiki/Approximate_string_matching for ideas.
$endgroup$
– Quuxplusone
9 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Daniel Duque is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216343%2foverloading-istream-to-read-comma-separated-input%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
95 percent of programming is looking for redundancies and eliminating them.
For example, why do you bother with reading strings into accusations[]
first, and then later copying them into readable.murderer
et cetera? Why not just read them directly into readable.murderer
? This would have the bonus of eliminating those "magic number" indices 0, 1, and 2, and replacing them with readable (no pun intended) identifiers.
std::getline(is, readable.murderer, ',');
std::getline(is, readable.weapon, ',');
std::getline(is, readable.place, ','); // shouldn't this last one be 'n' not ','?
You should test your code and see if it does what you wanted.
std::istringstream iss(
"Mr Green, lead pipe, conservatoryn"
"Mrs Peacock, noose, kitchen"
);
accusation acc;
iss >> acc;
This reads 5 items into accusation
. Is this what you wanted to happen?
Reduce repetition. You have the following snippet repeated three times:
for (const auto& THING : THINGS)
if (accusation[INDEX] == THING)
valid = true;
break;
So, first of all, we wrap the loop body in curly braces to protect against goto fail; and then we factor it out into a function.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
for (auto&& elt : vec)
if (elt == value)
return true;
return false;
And then our main function's code can become simply
bool valid = vector_contains(clue::characters, readable.murderer)
&& vector_contains(clue::weapons, readable.weapon)
&& vector_contains(clue::places, readable.place);
if (!valid)
is.setstate(std::ios_base::failbit);
The body of vector_contains
could also be implemented simply by using an STL algorithm, e.g.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::count(vec.begin(), vec.end(), value);
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::find(vec.begin(), vec.end(), value) != vec.end();
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::any_of(vec.begin(), vec.end(), [&](const auto& elt)
return elt == value;
);
I named the function vector_contains
, rather than simply contains
, because in my estimation there is a very real possibility that C++2a might add std::contains
to the library and thus break any code using ADL calls to contains
.
Minor nits:
I strongly recommend making all your constructors
explicit
, to eliminate bugs from unintentional implicit conversions. (Yes, even your multi-argument constructors.)I strongly recommend making
operator>>
andoperator<<
into inline friend functions — define them right inside the body of your class. This will make them findable only via ADL, and is generally what you want. It'll look a lot more reasonable, too, once you've refactored youroperator>>
to be only five or six lines long! :)
You're also doing something weird with stringstream
to remove whitespace from the ends of each piece of the string. You should factor that out into a helper function, and then simplify it. Say,
std::string strip(const std::string& s)
int i = 0;
while (isspace(s[i])) ++i;
int j = s.size();
while (j >= 1 && isspace(s[j-1])) --j;
return s.substr(i, j-i);
https://wandbox.org/permlink/uVSolN0Nepk48Mgm
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
explicit accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream& is, accusation& a)
!vector_contains(clue::places, a.place))
is.setstate(std::ios_base::failbit);
return is;
;
Deciding whether your std::transform
lowercasing should be removed, kept, or folded into the helper function vector_contains
(renaming that function to indicate its new purpose, and using a non-mutating facility such as strcasecmp
) is left as an exercise for the reader.
$endgroup$
$begingroup$
Thanks for all your feedback, it has been very eye-opening reading all your suggestions. Regarding your 'strip' function, it only removes white spaces from the beginning and the end of a string; opposed to what I was doing which deleted extra white space between words as well. Nevertheless I get your point, and will improve my code from all your suggestions.
$endgroup$
– Daniel Duque
9 hours ago
1
$begingroup$
Given that you mademr green
acceptable as a synonym forMr Green
, maybe you should consider whethermrgreen
should be acceptable as well. Then you wouldn't even need to remove spaces; you could just write a non-mutating string comparison, similar tostrcasecmp
, that ignores all whitespace too. Personally, I would go the other direction and force the user to enterMr Green
using that one exact spelling, to increase simplicity and ease-of-teaching-the-interface. If you do want to do clever fuzzy matching, look at en.wikipedia.org/wiki/Approximate_string_matching for ideas.
$endgroup$
– Quuxplusone
9 hours ago
add a comment |
$begingroup$
95 percent of programming is looking for redundancies and eliminating them.
For example, why do you bother with reading strings into accusations[]
first, and then later copying them into readable.murderer
et cetera? Why not just read them directly into readable.murderer
? This would have the bonus of eliminating those "magic number" indices 0, 1, and 2, and replacing them with readable (no pun intended) identifiers.
std::getline(is, readable.murderer, ',');
std::getline(is, readable.weapon, ',');
std::getline(is, readable.place, ','); // shouldn't this last one be 'n' not ','?
You should test your code and see if it does what you wanted.
std::istringstream iss(
"Mr Green, lead pipe, conservatoryn"
"Mrs Peacock, noose, kitchen"
);
accusation acc;
iss >> acc;
This reads 5 items into accusation
. Is this what you wanted to happen?
Reduce repetition. You have the following snippet repeated three times:
for (const auto& THING : THINGS)
if (accusation[INDEX] == THING)
valid = true;
break;
So, first of all, we wrap the loop body in curly braces to protect against goto fail; and then we factor it out into a function.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
for (auto&& elt : vec)
if (elt == value)
return true;
return false;
And then our main function's code can become simply
bool valid = vector_contains(clue::characters, readable.murderer)
&& vector_contains(clue::weapons, readable.weapon)
&& vector_contains(clue::places, readable.place);
if (!valid)
is.setstate(std::ios_base::failbit);
The body of vector_contains
could also be implemented simply by using an STL algorithm, e.g.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::count(vec.begin(), vec.end(), value);
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::find(vec.begin(), vec.end(), value) != vec.end();
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::any_of(vec.begin(), vec.end(), [&](const auto& elt)
return elt == value;
);
I named the function vector_contains
, rather than simply contains
, because in my estimation there is a very real possibility that C++2a might add std::contains
to the library and thus break any code using ADL calls to contains
.
Minor nits:
I strongly recommend making all your constructors
explicit
, to eliminate bugs from unintentional implicit conversions. (Yes, even your multi-argument constructors.)I strongly recommend making
operator>>
andoperator<<
into inline friend functions — define them right inside the body of your class. This will make them findable only via ADL, and is generally what you want. It'll look a lot more reasonable, too, once you've refactored youroperator>>
to be only five or six lines long! :)
You're also doing something weird with stringstream
to remove whitespace from the ends of each piece of the string. You should factor that out into a helper function, and then simplify it. Say,
std::string strip(const std::string& s)
int i = 0;
while (isspace(s[i])) ++i;
int j = s.size();
while (j >= 1 && isspace(s[j-1])) --j;
return s.substr(i, j-i);
https://wandbox.org/permlink/uVSolN0Nepk48Mgm
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
explicit accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream& is, accusation& a)
!vector_contains(clue::places, a.place))
is.setstate(std::ios_base::failbit);
return is;
;
Deciding whether your std::transform
lowercasing should be removed, kept, or folded into the helper function vector_contains
(renaming that function to indicate its new purpose, and using a non-mutating facility such as strcasecmp
) is left as an exercise for the reader.
$endgroup$
$begingroup$
Thanks for all your feedback, it has been very eye-opening reading all your suggestions. Regarding your 'strip' function, it only removes white spaces from the beginning and the end of a string; opposed to what I was doing which deleted extra white space between words as well. Nevertheless I get your point, and will improve my code from all your suggestions.
$endgroup$
– Daniel Duque
9 hours ago
1
$begingroup$
Given that you mademr green
acceptable as a synonym forMr Green
, maybe you should consider whethermrgreen
should be acceptable as well. Then you wouldn't even need to remove spaces; you could just write a non-mutating string comparison, similar tostrcasecmp
, that ignores all whitespace too. Personally, I would go the other direction and force the user to enterMr Green
using that one exact spelling, to increase simplicity and ease-of-teaching-the-interface. If you do want to do clever fuzzy matching, look at en.wikipedia.org/wiki/Approximate_string_matching for ideas.
$endgroup$
– Quuxplusone
9 hours ago
add a comment |
$begingroup$
95 percent of programming is looking for redundancies and eliminating them.
For example, why do you bother with reading strings into accusations[]
first, and then later copying them into readable.murderer
et cetera? Why not just read them directly into readable.murderer
? This would have the bonus of eliminating those "magic number" indices 0, 1, and 2, and replacing them with readable (no pun intended) identifiers.
std::getline(is, readable.murderer, ',');
std::getline(is, readable.weapon, ',');
std::getline(is, readable.place, ','); // shouldn't this last one be 'n' not ','?
You should test your code and see if it does what you wanted.
std::istringstream iss(
"Mr Green, lead pipe, conservatoryn"
"Mrs Peacock, noose, kitchen"
);
accusation acc;
iss >> acc;
This reads 5 items into accusation
. Is this what you wanted to happen?
Reduce repetition. You have the following snippet repeated three times:
for (const auto& THING : THINGS)
if (accusation[INDEX] == THING)
valid = true;
break;
So, first of all, we wrap the loop body in curly braces to protect against goto fail; and then we factor it out into a function.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
for (auto&& elt : vec)
if (elt == value)
return true;
return false;
And then our main function's code can become simply
bool valid = vector_contains(clue::characters, readable.murderer)
&& vector_contains(clue::weapons, readable.weapon)
&& vector_contains(clue::places, readable.place);
if (!valid)
is.setstate(std::ios_base::failbit);
The body of vector_contains
could also be implemented simply by using an STL algorithm, e.g.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::count(vec.begin(), vec.end(), value);
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::find(vec.begin(), vec.end(), value) != vec.end();
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::any_of(vec.begin(), vec.end(), [&](const auto& elt)
return elt == value;
);
I named the function vector_contains
, rather than simply contains
, because in my estimation there is a very real possibility that C++2a might add std::contains
to the library and thus break any code using ADL calls to contains
.
Minor nits:
I strongly recommend making all your constructors
explicit
, to eliminate bugs from unintentional implicit conversions. (Yes, even your multi-argument constructors.)I strongly recommend making
operator>>
andoperator<<
into inline friend functions — define them right inside the body of your class. This will make them findable only via ADL, and is generally what you want. It'll look a lot more reasonable, too, once you've refactored youroperator>>
to be only five or six lines long! :)
You're also doing something weird with stringstream
to remove whitespace from the ends of each piece of the string. You should factor that out into a helper function, and then simplify it. Say,
std::string strip(const std::string& s)
int i = 0;
while (isspace(s[i])) ++i;
int j = s.size();
while (j >= 1 && isspace(s[j-1])) --j;
return s.substr(i, j-i);
https://wandbox.org/permlink/uVSolN0Nepk48Mgm
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
explicit accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream& is, accusation& a)
!vector_contains(clue::places, a.place))
is.setstate(std::ios_base::failbit);
return is;
;
Deciding whether your std::transform
lowercasing should be removed, kept, or folded into the helper function vector_contains
(renaming that function to indicate its new purpose, and using a non-mutating facility such as strcasecmp
) is left as an exercise for the reader.
$endgroup$
95 percent of programming is looking for redundancies and eliminating them.
For example, why do you bother with reading strings into accusations[]
first, and then later copying them into readable.murderer
et cetera? Why not just read them directly into readable.murderer
? This would have the bonus of eliminating those "magic number" indices 0, 1, and 2, and replacing them with readable (no pun intended) identifiers.
std::getline(is, readable.murderer, ',');
std::getline(is, readable.weapon, ',');
std::getline(is, readable.place, ','); // shouldn't this last one be 'n' not ','?
You should test your code and see if it does what you wanted.
std::istringstream iss(
"Mr Green, lead pipe, conservatoryn"
"Mrs Peacock, noose, kitchen"
);
accusation acc;
iss >> acc;
This reads 5 items into accusation
. Is this what you wanted to happen?
Reduce repetition. You have the following snippet repeated three times:
for (const auto& THING : THINGS)
if (accusation[INDEX] == THING)
valid = true;
break;
So, first of all, we wrap the loop body in curly braces to protect against goto fail; and then we factor it out into a function.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
for (auto&& elt : vec)
if (elt == value)
return true;
return false;
And then our main function's code can become simply
bool valid = vector_contains(clue::characters, readable.murderer)
&& vector_contains(clue::weapons, readable.weapon)
&& vector_contains(clue::places, readable.place);
if (!valid)
is.setstate(std::ios_base::failbit);
The body of vector_contains
could also be implemented simply by using an STL algorithm, e.g.
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::count(vec.begin(), vec.end(), value);
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::find(vec.begin(), vec.end(), value) != vec.end();
or
template<class T>
bool vector_contains(const std::vector<T>& vec, const T& value)
return std::any_of(vec.begin(), vec.end(), [&](const auto& elt)
return elt == value;
);
I named the function vector_contains
, rather than simply contains
, because in my estimation there is a very real possibility that C++2a might add std::contains
to the library and thus break any code using ADL calls to contains
.
Minor nits:
I strongly recommend making all your constructors
explicit
, to eliminate bugs from unintentional implicit conversions. (Yes, even your multi-argument constructors.)I strongly recommend making
operator>>
andoperator<<
into inline friend functions — define them right inside the body of your class. This will make them findable only via ADL, and is generally what you want. It'll look a lot more reasonable, too, once you've refactored youroperator>>
to be only five or six lines long! :)
You're also doing something weird with stringstream
to remove whitespace from the ends of each piece of the string. You should factor that out into a helper function, and then simplify it. Say,
std::string strip(const std::string& s)
int i = 0;
while (isspace(s[i])) ++i;
int j = s.size();
while (j >= 1 && isspace(s[j-1])) --j;
return s.substr(i, j-i);
https://wandbox.org/permlink/uVSolN0Nepk48Mgm
class accusation
private:
std::string murderer;
std::string weapon;
std::string place;
public:
accusation() = default;
explicit accusation(std::string, std::string, std::string);
friend std::ostream& operator<<(std::ostream&, const accusation&);
friend std::istream& operator>>(std::istream& is, accusation& a)
!vector_contains(clue::places, a.place))
is.setstate(std::ios_base::failbit);
return is;
;
Deciding whether your std::transform
lowercasing should be removed, kept, or folded into the helper function vector_contains
(renaming that function to indicate its new purpose, and using a non-mutating facility such as strcasecmp
) is left as an exercise for the reader.
answered 11 hours ago
QuuxplusoneQuuxplusone
12.8k12062
12.8k12062
$begingroup$
Thanks for all your feedback, it has been very eye-opening reading all your suggestions. Regarding your 'strip' function, it only removes white spaces from the beginning and the end of a string; opposed to what I was doing which deleted extra white space between words as well. Nevertheless I get your point, and will improve my code from all your suggestions.
$endgroup$
– Daniel Duque
9 hours ago
1
$begingroup$
Given that you mademr green
acceptable as a synonym forMr Green
, maybe you should consider whethermrgreen
should be acceptable as well. Then you wouldn't even need to remove spaces; you could just write a non-mutating string comparison, similar tostrcasecmp
, that ignores all whitespace too. Personally, I would go the other direction and force the user to enterMr Green
using that one exact spelling, to increase simplicity and ease-of-teaching-the-interface. If you do want to do clever fuzzy matching, look at en.wikipedia.org/wiki/Approximate_string_matching for ideas.
$endgroup$
– Quuxplusone
9 hours ago
add a comment |
$begingroup$
Thanks for all your feedback, it has been very eye-opening reading all your suggestions. Regarding your 'strip' function, it only removes white spaces from the beginning and the end of a string; opposed to what I was doing which deleted extra white space between words as well. Nevertheless I get your point, and will improve my code from all your suggestions.
$endgroup$
– Daniel Duque
9 hours ago
1
$begingroup$
Given that you mademr green
acceptable as a synonym forMr Green
, maybe you should consider whethermrgreen
should be acceptable as well. Then you wouldn't even need to remove spaces; you could just write a non-mutating string comparison, similar tostrcasecmp
, that ignores all whitespace too. Personally, I would go the other direction and force the user to enterMr Green
using that one exact spelling, to increase simplicity and ease-of-teaching-the-interface. If you do want to do clever fuzzy matching, look at en.wikipedia.org/wiki/Approximate_string_matching for ideas.
$endgroup$
– Quuxplusone
9 hours ago
$begingroup$
Thanks for all your feedback, it has been very eye-opening reading all your suggestions. Regarding your 'strip' function, it only removes white spaces from the beginning and the end of a string; opposed to what I was doing which deleted extra white space between words as well. Nevertheless I get your point, and will improve my code from all your suggestions.
$endgroup$
– Daniel Duque
9 hours ago
$begingroup$
Thanks for all your feedback, it has been very eye-opening reading all your suggestions. Regarding your 'strip' function, it only removes white spaces from the beginning and the end of a string; opposed to what I was doing which deleted extra white space between words as well. Nevertheless I get your point, and will improve my code from all your suggestions.
$endgroup$
– Daniel Duque
9 hours ago
1
1
$begingroup$
Given that you made
mr green
acceptable as a synonym for Mr Green
, maybe you should consider whether mrgreen
should be acceptable as well. Then you wouldn't even need to remove spaces; you could just write a non-mutating string comparison, similar to strcasecmp
, that ignores all whitespace too. Personally, I would go the other direction and force the user to enter Mr Green
using that one exact spelling, to increase simplicity and ease-of-teaching-the-interface. If you do want to do clever fuzzy matching, look at en.wikipedia.org/wiki/Approximate_string_matching for ideas.$endgroup$
– Quuxplusone
9 hours ago
$begingroup$
Given that you made
mr green
acceptable as a synonym for Mr Green
, maybe you should consider whether mrgreen
should be acceptable as well. Then you wouldn't even need to remove spaces; you could just write a non-mutating string comparison, similar to strcasecmp
, that ignores all whitespace too. Personally, I would go the other direction and force the user to enter Mr Green
using that one exact spelling, to increase simplicity and ease-of-teaching-the-interface. If you do want to do clever fuzzy matching, look at en.wikipedia.org/wiki/Approximate_string_matching for ideas.$endgroup$
– Quuxplusone
9 hours ago
add a comment |
Daniel Duque is a new contributor. Be nice, and check out our Code of Conduct.
Daniel Duque is a new contributor. Be nice, and check out our Code of Conduct.
Daniel Duque is a new contributor. Be nice, and check out our Code of Conduct.
Daniel Duque is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216343%2foverloading-istream-to-read-comma-separated-input%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
$begingroup$
Welcome to CR, Could you please change the title to show the requirement from business/exercise point of view rather than your concerns. Concerns should go into the body of the question. :)
$endgroup$
– 422_unprocessable_entity
11 hours ago