#c #c 11 #error-handling
#c #c 11 #обработка ошибок
Вопрос:
Есть ли лучший способ сделать это? Я чувствую, что это будет ужасно поддерживать.
const unsigned int set_max {32};
std::vector<unsigned int> base;
base.reserve(argc-1);
for (int i = 1; i < argc; i) {
unsigned long tmp;
try { tmp = std::stoul(argv[i], nullptr, 10); }
catch (std::invalid_argument) { std::cerr << "Invalid base! Conversion failed.n"; return -2; }
catch (std::out_of_range) { std::cerr << "Invalid base! Too large.n"; return -2; }
if (tmp > std::numeric_limits<unsigned int>::max()) { std::cerr << "Invalid base! Too large.n"; return -2; }
if (tmp < 2) { std::cerr << "Invalid base! Must at least be 2.n"; return -2; }
if (tmp > set_max) { std::cerr << "Invalid base! Must be within " << set_max << ".n"; return -2; }
base.push_back(static_cast<unsigned int>(tmp));
}
Редактировать: перепечатано здесь
Комментарии:
1. Почему бы не попробовать и не опубликовать его codereview.stackexchange.com
2. Если вы используете
std::strtoul()
вместо этого, вы можете избавиться от обработки исключений, по крайней мере. Независимо от того, используете ли выstd::strtoul()
илиstd::stoul()
, я бы переместил всю логику в отдельную функцию, которая принимает строку в качестве входных данных и возвращает целое число и bool в качестве выходных данных, и помещает целое число в вектор, только если bool сообщает true . Это упростит обслуживание кода.3. Почему
return -2;
? Почему бы не создать исключение? Тогда вы можете просто позволить вызывающей стороне обрабатывать все исключения, включая тот, который был вызванstoul
.4. @JoaoAlmeida-Domingues Спасибо, я не знал, что форум существует.
5. @WernerHenze Потому что это из функции main. И он напрямую взаимодействует с пользователем.
Ответ №1:
Я бы предложил разделить логику на служебные функции, чтобы упростить управление каждой операцией, например:
enum class eConvFailure {NoError, ConvFailed, OutOfRange, TooSmall, TooLarge};
std::pair<unsigned int, eConvFailure> my_stoui(const std::string amp;s)
{
unsigned long tmp;
try
{
tmp = std::stoul(s, nullptr, 10);
if (tmp > std::numeric_limits<unsigned int>::max()) {
throw std::out_of_range("");
}
}
catch (const std::invalid_argument amp;) {
return std::make_pair(0, eConvFailure::ConvFailed);
}
catch (const std::out_of_range amp;) {
return std::make_pair(0, eConvFailure::OutOfRange);
}
/* alternatively:
errno = 0;
tmp = std::stroul(s.c_str(), nullptr, 10);
if (errno != 0) {
if (tmp == ULONG_MAX amp;amp; errno == ERANGE)
return std::make_pair(0, eConvFailure::OutOfRange);
else
return std::make_pair(0, eConvFailure::ConvFailed);
}
if (tmp > std::numeric_limits<unsigned int>::max()) {
return std::make_pair(0, eConvFailure::TooLarge);
}
*/
return std::make_pair(static_cast<unsigned int>(tmp), eConvFailure::NoError);
}
std::pair<unsigned int, eConvFailure> my_stoui(const std::string amp;s, unsigned int max_value)
{
auto res = my_stoui(s);
if (res.second != eConvFailure::NoError) {
return res;
}
if (res.first < 2) {
return std::make_pair(0, eConvFailure::TooSmall);
}
if (res.first > max_value) {
return std::make_pair(0, eConvFailure::TooLarge);
}
return res;
}
const unsigned int set_max {32};
std::string reason(eConvFailure err) {
switch (err) {
case eConvFailure::ConvFailure: return "Conversion Failed";
case eConvFailure::OutOfRange: return "Too Large";
case eConvFailure::TooSmall: return "Must at least be 2";
case eConvFailure::TooLarge: return "Must be within " std::to_string(set_max);
default: return "Error " std::to_string(static_cast<int>(eConvFailure));
}
}
std::vector<unsigned int> base;
base.reserve(argc-1);
for (int i = 1; i < argc; i) {
auto res = my_stoui(argv[i], set_max);
if (res.second != eConvFailure::NoError) {
std::cerr << "Invalid base! " << reason(res.second) << ".n";
return -2;
}
base.push_back(res.first);
}
Или вместо этого вы можете использовать обработку исключений в своих интересах, например:
unsigned int my_stoui(const std::string amp;s)
{
unsigned long tmp = std::stoul(s, nullptr, 10);
if (tmp > std::numeric_limits<unsigned int>::max()) {
throw std::out_of_range("");
}
return static_cast<unsigned int>(tmp);
}
unsigned int my_stoui(const std::string amp;s, unsigned int max_value)
{
unsigned int tmp = my_stoui(s);
if (tmp < 2) {
throw std::runtime_error("Must at least be 2");
}
if (tmp > max_value) {
return std::runtime_error("Must be within " std::to_string(max_value));
}
return tmp;
}
const unsigned int set_max {32};
std::vector<unsigned int> base;
base.reserve(argc-1);
try
{
for (int i = 1; i < argc; i) {
base.push_back(my_stoui(argv[i], set_max));
}
}
catch (const std::exception amp;ex)
{
std::cerr << "Invalid base! ";
if (dynamic_cast<const std::invalid_argument*>(amp;ex)) {
std::cerr << "Conversion failed";
}
else if (dynamic_cast<const std::out_of_range*>(amp;ex)) {
std::cerr << "Too large";
}
else {
std::cerr << ex.what();
}
std::cerr << ".n";
return -2;
}
Комментарии:
1. Спасибо! Похоже, что вывод логики из цикла — лучший способ сделать это в конце концов.
2. Мне нравится последнее, используя обработку исключений, поскольку это проще.