There is so much to respond to. How I love these little design chats :)
They give such a lot of feedback. I will not incorporate any more changes until I've decided at a proper design, by listening to feedback and incorporating it into my design.
Quote:
Originally Posted by
phantomotap
Why are you using 'std::getline' by default?
Because of the usual approach - read a string, convert it to appropriate type. If it fails, there is no need to clear the stream or other complications. It's my preferred method of doing inputs.
Quote:
Why make one class responsible for so much work?
I don't know. I don't see how I would place it all inside different classes and still maintain the easy solution of use for a (single) object.
Although you do give me the sense that there can be several versions of the input class, each tailored to a somewhat different approach.
Quote:
Why use a simple string as an argument related to prompting a user for input?
The prompt could be of any type, so long as it can be outputted by an appropriate stream. That, too, could be changed, though, if one provided custom traits.
Quote:
Why use a simple string as an argument related to providing an error message?
For error, a function or functor or an error message could be passed, depending on the design. That is my planning.
Quote:
Why does the error message appear unrelated to the validation function?
The code is not perfect here, either, of course.
Either it should output an error and do the input process over or it should call a functor or function that handles the error process and probably starting the process over again.
Quote:
Why not decouple this "dumb user handler" class mechanic from types and constructs not necessarily related to input and output?
You want to put the conversion and validation process as part of separate class(es)? IS that what you mean or did you have something else in mind?
Quote:
Have you studied many of the applications of expression templates?
I don't know what expression templates are, so no, unfortunately...
Quote:
Originally Posted by
citizen
>> I don't think a function will cut it. <<
Why? I don't see a class when I look at your proposed steps, I see a procedure. For reasons forthcoming, you haven't quite given a rationale for the design of this class and the example as I read it fulfills all of the requirements you've listed.
Quote:
Originally Posted by
CornedBee
All I can say is, "Huh?"
You lost me at
As citizen said, why? No, let me rephrase that. Why?
What do you mean by "repeat that input"? Why would you want to repeat the input?
When I do user input, I care about three things:
1) I want to prompt the user.
2) I want to read a value.
3) I want to validate it, and if necessary, emit an error and start over.
In particular, I want the value to end up in a normal variable of the value type, not hidden inside an input object. The moment I have validated the value, I no longer care that it was user input.
What I meant is as in your example. You read the input. Then you begin to validate it. You find out that it doesn't cut the requirements, so you ask again.
So instead of typing ask(msg, error, ...) all the time, you could bind those things at the time of creation. Then all you would have to do is call ask().
I don't know if it has many advantages, it just seems natural to me that an object should be able to do that. In sense that it may be a someone whom you task for getting input: say this, and the answer you get must conform to these requirements. And then later you can say: ask again.
Quote:
Often the extraction operator does all three of these:
- Convert std::tstring to T.
- Validate input.
- Store input.
In particular, if the conversion from string to T is valid then the input is valid. Granted this is not necessarily the case for all extractors, but a number of them. Why doesn't that fit into your rationale?
Generally, it does, but what if I want the answer to be no less than 1 or greater than 3? I have to check manually, but I want to be able to pass this off to the input validation, if possible.
Perhaps you may have some other exotic requirements, as well, mostly with custom classes. I think this applies mostly to built-in types, I suppose.
Quote:
>> This will eliminate the need for a separate variable for each answer, aside from the inputter and gives a greater feel for the class <<
I wanted to respond to this first. Why is that a good thing? C++ programmers are quite comfortable with the concept of streams and extracting individual datum from them. Any reason for going against the grain?
I don't know. It seemed more natural to make the class the actual answer itself.
I can do either way, though.
Quote:
>> Now, if I had to type this on many occasions, I felt it was too much, too much general code that I had to re-type over and over. So why not provide a generic way to solve this? <<
Because not all I/O operations are interactive? Because input and output are fundamental parts of the problem-solving process and there are a diverse number of problems? I agree that you can abstract away a lot of the repetitive solutions, (see the proposed procedure as an example - if you ever agree they in fact "cut it" at some point) which is why I don't outright bash the attempt. I just never agreed with the design, and you asked for my opinion. Perhaps it's my inexperience talking but I have to resolve I/O program by program at this point because they're all somewhat different. I feel like I'm changing horses midstream and I apologize to readers who might be confused.
I realize that all uses may not be the same, and as such, you need great control over what happens. The original idea was to remove repeating code, by placing the work inside a class and building a flexible but easy interface.
That is the very reason I want to provide more control over how it works, to customize certain steps of the way without the need to specialize a lot of template classes for many types, and preferably also not having to derive the class, though that could be a solution, too.
I want to create a piece of code that can easily incorporate and simplify the input process, so you don't have to repeat code all over the place. With specialized functors and stuff, it could be a simple way of customizing the class for different uses over many projects. That's how I feel.
Quote:
I feel as though you are trying to design an all-encompassing solution to interactive input, but at the moment I do not see how this saves me any work apart from the fact that I would not need to use control structures if I used an inputter instead. Let me tell you that the solution with control structures is easier to document, debug, and maintain--especially for other C++ programmers.
I do feel the same, and I believe it will have advantages, if only for myself.
As to the maintain and understanding part... If you provide functors that validate your inputer, pretty much all you would not need is a loop. Less typing, less lines. And I feel it is no different from using an algorithm to perform tasks (such as std::for_each instead of a for loop).
Of course, some may like this, and some may not. That is why I also want to give control to the programmer to use it as they see naturally. I don't know if this is a bad idea.
The idea is that you can or can not have the validation inside the class. All it can do for you, if you want, is get the input, convert and store it. You can then retrieve it and validate it on your own.
Quote:
You probably agree, since if I wanted to extend this class for any complicated data type then I need to abstract away validation methods and shove them in your class somehow ...
Well, I found it easier to use a more abstract way instead of a way after retrieving the input. But you might just as well just a big number of If statements, if you wish.
Quote:
>> This is not Java, so everything that is not an object does not have to be a class. <<
To wit, everything which is an object is an instantiation of a class by definition.
With all the problems class design seems to have in regards to the problem I cannot agree that it is the best route to take. Part of what makes C++ nice is that you can solve it naturally.
The function actually gave me something to think about, but the more I do think about it, the more I seem to think it does not cut the problem, from my view. Especially not if I go on to make it work in a different number of ways. Flexibility is always the key in C++!