Based on previous question, The code now is relied on std::regex
for validate and split the input. For current improvement, I removed all duplicates functionality by reading the input and applies conversion in-place.
How can I improve it further.
#include <iostream>
#include <string>
#include <regex>
#include <cctype>
#include <limits>
#include <sstream>
#include <cassert>
#include <type_traits>
#include <stdexcept>
unsigned int string_to_int(const std::string& str)
{
unsigned int value;
std::stringstream ss(str);
if (!(ss >> value) || !ss.eof())
{
throw std::invalid_argument("Oops\n");
}
return value;
}
unsigned int convert_to_numeric(const std::string& str)
{
unsigned int value = 0;
for (const auto& ch : str)
{
value = value * 26 + (ch - 'A' + 1);
}
return value;
}
std::string convert_to_alpha(unsigned int value)
{
std::string result;
do
result = static_cast<char>('A' + value % 26 - 1) + result;
while (value /= 26);
return result;
}
bool is_within_range(const std::string& row, unsigned int colVal)
{
static const unsigned int Min = 1;
static const unsigned int Max = 999999;
const auto rowVal = string_to_int(row);
return rowVal >= Min
&& rowVal <= Max
&& colVal >= Min
&& colVal <= Max;
}
bool RC_to_Cell(const std::string& row, const std::string& col, std::ostream& os)
{
bool is_valid = false;
auto value = string_to_int(col);
is_valid = is_within_range(row, value);
if (is_valid)
{
const auto& result = convert_to_alpha(value);
os << "\n\nResult: " << result << row << "\n\n";
}
return is_valid;
}
bool Cell_to_RC(const std::string& row, const std::string& col, std::ostream& os)
{
bool is_valid = false;
auto value = convert_to_numeric(col);
is_valid = is_within_range(row, value);
if (is_valid)
{
os << "\n\nResult: R" << row << "C" << value << "\n\n";
}
return is_valid;
}
bool convert(const std::string& in, std::ostream& os)
{
static const std::regex Pattern{"^(?:R([^0]\\d*+)C([^0]\\d*+))$|^(?:([A-Z]+)([^0]\\d*+))$"};
std::smatch matches;
bool is_valid = false;
if (!std::regex_match(in, matches, Pattern)) return is_valid;
if (matches[1].matched && matches[2].matched)
is_valid = RC_to_Cell(matches[1].str(), matches[2].str(), os);
else
is_valid = Cell_to_RC(matches[4].str(), matches[3].str(), os);
return is_valid;
}
template <typename Input, typename Prompt, typename Handle>
Input read_input(Prompt&& prompt, Handle&& handle_input)
{
std::istream& is = std::cin;
std::ostream& os = std::cout;
Input input{};
bool is_not_valid = false;
static const std::string Error{"Not valid input.\n"};
do
{
os << std::forward<Prompt>(prompt) << std::flush;
is_not_valid = std::forward<Handle>(handle_input)(input, is, os);
if (is_not_valid)
{
os << Error;
}
is.clear();
is.ignore(std::cin.rdbuf()->in_avail());
} while (is_not_valid);
return input;
}
template <typename Prompt>
unsigned int get_count(Prompt&& prompt)
{
static auto int_input = [] (auto& in, std::istream& is, std::ostream&)
{
std::string s;
std::getline(is, s);
std::stringstream ss{s};
return is.fail() || !(ss >> in) || !ss.eof() || (in <= 0 || in >= 100000);
};
return read_input<unsigned int>(std::forward<Prompt>(prompt), int_input);
}
template <typename Prompt>
void read_and_convert(Prompt&& prompt)
{
static auto string_input = [] (auto& in, std::istream& is, std::ostream& os)
{
std::getline(is, in);
return is.fail() || !convert(in, os);
};
read_input<std::string>(std::forward<Prompt>(prompt), string_input);
}
int main()
{
auto n = get_count("Enter a number of entries: ");
std::cout << "\n\n\tWelcome\n\n";
do
read_and_convert("Enter your choice: ");
while (--n);
}