read.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. //read.h - a tiny header-only library to allow input in C++ to be more sensible
  2. //by ShakaUVM
  3. #ifndef __READ_SHAKAUVM_H
  4. #define __READ_SHAKAUVM_H
  5. #include <iostream>
  6. #include <fstream>
  7. #include <string>
  8. //Read a T from cin, reprompting if they type in something wrong
  9. //Examples:
  10. //int x = read<int>();
  11. //const int x = read<int>("Please enter an int");
  12. //string s = read<string>("What is your name?");
  13. template<class T>
  14. inline T read(const std::string prompt = "") {
  15. while (true) {
  16. if (std::cin.eof()) //We reached the end of file, or the user hit ctrl-d
  17. return T(); //Alternatively, we could throw an exception
  18. T retval;
  19. std::cout << prompt;
  20. std::cin >> retval;
  21. if (!std::cin) {
  22. std::cin.clear(); //Clear error code
  23. std::string s;
  24. std::cin >> s; //Remove the word that caused the error
  25. continue;
  26. }
  27. return retval;
  28. }
  29. }
  30. //Read a T from a file, so no prompt
  31. //Example:
  32. //auto d = read<double>(file);
  33. template<class T>
  34. inline T read(std::istream &ins) {
  35. while (true) {
  36. if (ins.eof()) //We reached the end of file, or the user hit ctrl-d
  37. return T();
  38. T retval;
  39. ins >> retval;
  40. if (!ins) {
  41. ins.clear(); //Clear error code
  42. std::string s;
  43. ins >> s; //Remove the word that caused the error
  44. continue;
  45. }
  46. return retval;
  47. }
  48. }
  49. //Reads a whole line of text, analogue to getline
  50. inline std::string readline(const std::string prompt = "", char delimiter = '\n') {
  51. //Eliminate a common bug when switching from >> to getline, the >> will leave a newline in the input buffer
  52. std::string retval;
  53. std::cout << prompt;
  54. std::cin >> std::ws;
  55. std::getline(std::cin,retval,delimiter);
  56. if (std::cin.eof()) //We reached the end of file, or the user hit ctrl-d
  57. return retval;
  58. //if (!std::cin)
  59. //throw std::runtime_error("Error within the readline function.");
  60. return retval;
  61. }
  62. //Getline equivalent for reading from a file
  63. inline std::string readline(std::istream &ins, char delimiter = '\n') {
  64. std::string retval;
  65. ins >> std::ws;
  66. std::getline(ins,retval,delimiter);
  67. if (ins.eof()) //We reached the end of file, or the user hit ctrl-d
  68. return retval;
  69. //if (!ins)
  70. //throw std::runtime_error("Error within the readline function.");
  71. return retval;
  72. }
  73. //To use read_opt requires C++17 and above
  74. #if __cplusplus >= 201703L
  75. #include <optional>
  76. //Returns an optional. So if your specified type is not read, the caller will know this rather than silently discarding the error
  77. //Recommended for more advanced programmers than read()
  78. //If an error occurs, it does not affect the input stream at all and clears the failbit
  79. // Example:
  80. // auto a = read_opt<int>();
  81. // if (!a) exit(EXIT_FAILURE);
  82. // cout << *a << endl;
  83. template<class T>
  84. inline std::optional<T> read_opt(const std::string prompt = "") {
  85. if (std::cin.eof()) //We reached the end of file, or the user hit ctrl-d
  86. return std::nullopt; //Return that nothing was read
  87. T retval{};
  88. std::cout << prompt;
  89. std::cin >> retval;
  90. if (!std::cin) {
  91. std::cin.clear(); //Clear error code, so the user can try again when they like
  92. return std::nullopt; //Return that nothing was read
  93. }
  94. return retval;
  95. }
  96. //This version reads from a file, so no prompt
  97. //Like the other read_opt, returns nullopt if it didn't read what was expected
  98. template<class T>
  99. inline std::optional<T> read_opt(std::istream &ins) {
  100. if (ins.eof()) //We reached the end of file, or the user hit ctrl-d
  101. return std::nullopt; //Return that nothing was read
  102. T retval{};
  103. ins >> retval;
  104. if (!ins) {
  105. ins.clear(); //Clear error code, so the user can try again when they like
  106. return std::nullopt; //Return that nothing was read
  107. }
  108. return retval;
  109. }
  110. //End requiring C++17 and above
  111. #endif
  112. //Simplest read possible: int x = read();
  113. //Credit: /u/9cantthinkofgoodname, modified to support C++98 by Mikadore
  114. //However, int x = read(ins) is about 20% slower than using read<int>(ins), though
  115. //There's probably some template tricks we can use to eliminate the while loop when reading from a file
  116. struct Reader {
  117. Reader(std::istream& ins_, const std::string& prompt_) : ins(ins_), prompt(prompt_) {}
  118. template<class T>
  119. operator T() {
  120. while(true) {
  121. if(ins.eof()) //We reached the end of file, or the user hit ctrl-d
  122. return T(); //Alternatively, we could throw an exception
  123. T retval;
  124. std::cout << prompt;
  125. ins >> retval; //If this fails, it's because you need a operator>> defined for your type
  126. if(!ins) {
  127. ins.clear(); //Clear error code
  128. std::string s;
  129. ins >> s; //Remove the word that caused the error
  130. continue;
  131. }
  132. return retval;
  133. }
  134. }
  135. std::istream &ins;
  136. const std::string prompt;
  137. };
  138. inline Reader read(const std::string prompt = "") {
  139. return Reader(std::cin,prompt);
  140. }
  141. inline Reader read(std::istream &ins) {
  142. return Reader(ins,"");
  143. }
  144. #endif