Thanks to a lot of help the other day I have a nice generic layout for getting console input ( Sorry JLBorges, I never did understand that "is_constructible"). I have edited the code to try and eliviate some memory usage when the user enters something wrong:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
// Recursive function returns itself on failure (Omits while loop)
template<typename T>
T& GetConsoleInput(T& value, const std::string& s)
{
static std::string input; // Static stops multiple objects being created and
static char extra; // pushed to the stack on failure due to recursion
static std::istringstream* ss; // Had to new due to bug (Not accepting legit value after multiple failures with static alone
std::cout << s << ": "; // Display prompt message
if(std::getline(std::cin,input)) // Get user input
{
ss = new std::istringstream(input);
if(*ss >> value && !(*ss >> extra))
{
delete ss; return value; // If value is of correct type and end of stream is reached
}
}
else throw std::runtime_error("Error: Input failure on stdin.");
std::cout << "Invalid input. Try again.\n"; delete ss;
return GetConsoleInput<T>(value,s); // Call itself continuously until it get's a correct input
}
// Cleaner efficient code for just strings
std::string& GetConsoleInput(std::string& value, const std::string& s)
{
std::cout << s << ": "; // Display prompt message
if(std::getline(std::cin,value)) return value;
else throw std::runtime_error("Error: Input failure on stdin.");
}
// This is the base function the user calls
template<typename T>
T GetConsoleInput(const std::string s)
{
T value;
return GetConsoleInput(value,s);
}
|
How would I return multiple values? If say, I wanted the user to enter 2
int
's I could easily return a vector of type T.
But what happens if I want them to enter say, an int, followed by a double, followed by a string? I cannot have a vector that can store multiple types. My only other suggestion would be to create a base class with all elementary values and return a vector of these classes, and hope the user knowns which value to get from each element in the vector. But i think that's pretty ugly.
Anyone got any idea's? Not looking for code as such ( but will be appreciated ) just general tips or a nudge in the right direction will help me out.
Thanks in advance.
EDIT: Spelling mistakes.
UPDATE:
I tried to be clever trying to overload the istream operator>> in a class, to see if that woked:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
#include <iostream>
#include <string>
#include "D:\mega\mega.h"
class c
{
private:
int i;
double d;
std::string s;
public:
c() {}
std::istream& operator>>(std::istream& is)
{
is >> i >> d >> s;
return is;
}
void display() { std::cout << i << d << s; }
};
using namespace mega;
int main()
{
int a = GetConsoleInput<int>("Enter an integer");
ConsolePrint("The integer you entered was ",a,"\n");
c a_class = GetConsoleInput<c>("Enter an integer, followed by a double, then a string: ");
a_class.display();
return 0;
}
|
However I get the error:
||=== Build: Debug in Functions (compiler: GNU GCC Compiler) ===|
D:\mega\mega.h||In instantiation of 'T& mega::GetConsoleInput(T&, const string&) [with T = c; std::string = std::basic_string<char>]':|
D:\mega\mega.h|48|required from 'T mega::GetConsoleInput(std::string) [with T = c; std::string = std::basic_string<char>]'|
main.cpp|26|required from here|
D:\mega\mega.h|25|error: cannot bind 'std::basic_istream<char>' lvalue to 'std::basic_istream<char>&&'|
|error: initializing argument 1 of 'std::basic_istream<_CharT, _Traits>& std::operator>>(std::basic_istream<_CharT, _Traits>&&, _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = c]'|
||=== Build failed: 2 error(s), 3 warning(s) (0 minute(s), 0 second(s)) ===|