patterncppMinor
Giveth me thy easier user input in C++ - follow up
Viewed 0 times
userthyinputfollowgivetheasier
Problem
I've decided to take some of the many suggestions for improvement on my previous question, Easier user input in C++, and actually get it to work as expected. This time around, a few things are different, namely:
I do have a few concerns about my code though, and I'd like to hear your opinions on them:
easy_input.h
```
#if HAVE_PRAGMA_ONCE
#pragma once
#endif
#ifndef EASY_INPUT_H_
#define EASY_INPUT_H_
#include
#include
#include
namespace easy_input
{
const std::string input_error_message = "an I/O error was encountered.";
static std::string default_prompt = "";
template
TInput get_input(
const TPrompt& prompt = default_prompt,
std::istream& input_stream = std::cin
);
}
/**
* This function serves as a useful wrapper for getting user input.
* Rather than forcing the user to type out multiple lines every
* time they want to get input, they only have to type one line.
* @tparam TInput - The type of the input to obtain.
*
- The function will now accept string input with spaces, like
"Ethan Bierlein", and not just spit back the first "word" in the input.
- The function now also accepts a different input stream rather than just
std::cin, if, for some reason you do something like that.
- The function now allows for a "no-prompt" option, as well, which just means that the argument
prompthas a default value of"".
- There is an optional way to specify the type of the prompt as well now. It's default type is
std::string.
- The function, now named
get_input, has it's own namespace,easy_input, rather than being patched intostd.
I do have a few concerns about my code though, and I'd like to hear your opinions on them:
- Is it a good idea to declare a static variable, just so I can have a default value for
prompt? Is it a good idea to have a default value for prompt?
- How necessary is it to provide an option to get input from a different input stream?
- Am I writing good C++, or am I doing certain things horribly wrong?
- Is there anything else that stands out for improvment?
easy_input.h
```
#if HAVE_PRAGMA_ONCE
#pragma once
#endif
#ifndef EASY_INPUT_H_
#define EASY_INPUT_H_
#include
#include
#include
namespace easy_input
{
const std::string input_error_message = "an I/O error was encountered.";
static std::string default_prompt = "";
template
TInput get_input(
const TPrompt& prompt = default_prompt,
std::istream& input_stream = std::cin
);
}
/**
* This function serves as a useful wrapper for getting user input.
* Rather than forcing the user to type out multiple lines every
* time they want to get input, they only have to type one line.
* @tparam TInput - The type of the input to obtain.
*
Solution
Redefinition of default arguments
First of all, you cannot redefine default arguments. From [dcl.fct.default], slightly abridging the example:
A default argument shall not be redefined by a later declaration (not even to the same value). [ Example:
—end example ]
So the definition of
Otherwise, the code is ill-formed and should not compile (though apparently earlier versions of gcc happily allow this).
Include guards AND pragma
Given that you have include guards, it's unnecessary to additionally have the conditional
Default Prompt and Error
You use the error message in a single place - it'd be better to just write out the string literal there. It'll be easier to find when grepping through code, and there's no advantage I see in having the global constant.
With the default prompt, rather than all the additional complexity of default arguments, I think it'd be simpler to just add an overload:
Ultimately though...
My biggest takeaway from 5gon12eder's excellent answer to your original question was that there really isn't anything easy about this input, and it may just be easier to avoid it and stick to being explicit with the streams.
Here's something this solution can't handle. Let's say I want to input two ints. I could write:
And end up doing something like:
And that works. But if I tried to do:
and tried to input both on the same line... I'd get a lexical error since "42 15" isn't an
First of all, you cannot redefine default arguments. From [dcl.fct.default], slightly abridging the example:
A default argument shall not be redefined by a later declaration (not even to the same value). [ Example:
void m() {
void f(int, int); // has no defaults
f(4); // error: wrong number of arguments
void f(int, int = 5); // OK
f(4); // OK, calls f(4, 5);
void f(int, int = 5); // error: cannot redefine, even to
// same value
}—end example ]
So the definition of
get_input should just be:template
TInput easy_input::get_input(
const TPrompt& prompt,
std::istream& input_stream)
{ ... }Otherwise, the code is ill-formed and should not compile (though apparently earlier versions of gcc happily allow this).
Include guards AND pragma
Given that you have include guards, it's unnecessary to additionally have the conditional
#pragma once. If you don't want to unconditionally use the pragma, just stick with the include guards.Default Prompt and Error
You use the error message in a single place - it'd be better to just write out the string literal there. It'll be easier to find when grepping through code, and there's no advantage I see in having the global constant.
With the default prompt, rather than all the additional complexity of default arguments, I think it'd be simpler to just add an overload:
template
TInput get_input(std::istream& input_stream = std::cin);
template
TInput get_input(const TPrompt& prompt,
std::istream& input_stream = std::cin)
{
std::cout (input_stream);
}Ultimately though...
My biggest takeaway from 5gon12eder's excellent answer to your original question was that there really isn't anything easy about this input, and it may just be easier to avoid it and stick to being explicit with the streams.
Here's something this solution can't handle. Let's say I want to input two ints. I could write:
int a, b;
std::cout > a >> b;And end up doing something like:
$42 15And that works. But if I tried to do:
int a = get_input("$");
int b = get_input();and tried to input both on the same line... I'd get a lexical error since "42 15" isn't an
int. Maybe you're OK with that? But it's limiting and sadly not easy. Then again, nothing IO related in C++ is really easy anyway.Code Snippets
void m() {
void f(int, int); // has no defaults
f(4); // error: wrong number of arguments
void f(int, int = 5); // OK
f(4); // OK, calls f(4, 5);
void f(int, int = 5); // error: cannot redefine, even to
// same value
}template <typename TInput, typename TPrompt>
TInput easy_input::get_input(
const TPrompt& prompt,
std::istream& input_stream)
{ ... }template <typename TInput>
TInput get_input(std::istream& input_stream = std::cin);
template <typename TInput, typename TPrompt>
TInput get_input(const TPrompt& prompt,
std::istream& input_stream = std::cin)
{
std::cout << prompt;
return get_input<TInput>(input_stream);
}int a, b;
std::cout << "$";
std::cin >> a >> b;int a = get_input<int>("$");
int b = get_input<int>();Context
StackExchange Code Review Q#109062, answer score: 3
Revisions (0)
No revisions yet.