Как сообщать о пользовательских ошибках istream - PullRequest
0 голосов
/ 18 ноября 2018

Я хотел бы использовать оператор >> () для чтения линейных алгебраических данных из консоли. Я хотел бы, чтобы оператор >> () вел себя так же, как и для встроенных данных (например, int, double), но я также хотел бы сообщать о соответствующих сообщениях, когда ввод не может быть проанализирован.

Я наконец-то создал класс custom_istream_failure, но все вместе это было довольно хлопотно. Теперь я задаюсь вопросом: это путь, или существует другой механизм для этой цели? Это в духе стандарта?

Я включил небольшую тестовую программу, которая сообщает о пользовательском сбое в функцию «ожидание». Кроме того, я включил заголовочный файл «custom_istream_failure.h», о котором этот вопрос.

#include <iostream>
#include "custom_istream_failure.h"

struct vector_t { int x, y, z; };

bool expect(std::istream& is, char e)
{
    if (is.get() != e)
    {
        custom_istream_failure(is) 
            << "Expected '" << e << '\'' 
            << and_throw;
        return false;
    }
    return true;
}

std::istream& operator>>(
    std::istream& is, vector_t& v)
{
    expect(is, '(') &&
    (is >> v.x)     &&
    expect(is, ',') &&
    (is >> v.y)     &&
    expect(is, ',') &&
    (is >> v.z)     &&
    expect(is, ')');
    return is;
}

int main()
{
    try
    {
        std::cin.exceptions(std::istream::failbit);
        vector_t vector;
        std::cin >> vector;
    }
    catch (std::exception& e)
    {
        std::cout << e.what() << std::endl;
    }
    return 0;
}

#ifndef CUSTOM_ISTREAM_FAILURE
#define CUSTOM_ISTREAM_FAILURE

#include <iostream>
#include <string>
#include <sstream>

class custom_istream_failure
    : protected std::stringstream
{
public:
    explicit custom_istream_failure(std::istream& is)
        : m_is(is)
    {}
    custom_istream_failure& operator<<(
        custom_istream_failure& 
            (*pf)(custom_istream_failure&))
    {
        return ((*pf)(*this));
    }
#define CUSTOM_ISTREAM_FAILURE_SOP(D) \
    custom_istream_failure& operator<<(D) \
    { \
        *static_cast<std::stringstream*>(this) << v; \
         return *this; \
    }
    CUSTOM_ISTREAM_FAILURE_SOP(bool v)
    CUSTOM_ISTREAM_FAILURE_SOP(short v)
    CUSTOM_ISTREAM_FAILURE_SOP(unsigned short v)
    CUSTOM_ISTREAM_FAILURE_SOP(int v)
    CUSTOM_ISTREAM_FAILURE_SOP(unsigned int v)
    CUSTOM_ISTREAM_FAILURE_SOP(long v)
    CUSTOM_ISTREAM_FAILURE_SOP(unsigned long v)
    CUSTOM_ISTREAM_FAILURE_SOP(long long v)
    CUSTOM_ISTREAM_FAILURE_SOP(unsigned long long v)
    CUSTOM_ISTREAM_FAILURE_SOP(float v)
    CUSTOM_ISTREAM_FAILURE_SOP(double v)
    CUSTOM_ISTREAM_FAILURE_SOP(long double v)
    CUSTOM_ISTREAM_FAILURE_SOP(void* v)
    CUSTOM_ISTREAM_FAILURE_SOP(std::streambuf* v)
    CUSTOM_ISTREAM_FAILURE_SOP(
        std::ostream& (*v)(std::ostream&))
    CUSTOM_ISTREAM_FAILURE_SOP(
        std::ios& (*v)(std::ios&))
    CUSTOM_ISTREAM_FAILURE_SOP(
        std::ios_base& (*v)(std::ios_base&))
    CUSTOM_ISTREAM_FAILURE_SOP(char v)
    CUSTOM_ISTREAM_FAILURE_SOP(signed char v)
    CUSTOM_ISTREAM_FAILURE_SOP(unsigned char v)
    CUSTOM_ISTREAM_FAILURE_SOP(const char* v)
    CUSTOM_ISTREAM_FAILURE_SOP(const signed char* v)
    CUSTOM_ISTREAM_FAILURE_SOP(const unsigned char*v);
#undef CUSTOM_ISTREAM_FAILURE_SOP
private:
    std::istream& m_is;
    friend custom_istream_failure& and_throw(
        custom_istream_failure&);
};

inline custom_istream_failure& and_throw(
    custom_istream_failure& cif)
{
    try { throw std::ios_base::failure(cif.str()); }
    catch (...)
    { 
        cif.m_is.setstate(std::ios::failbit, true);
    }
    return (cif);
}

#endif // CUSTOM_ISTREAM_FAILURE

1 Ответ

0 голосов
/ 18 ноября 2018

Один из подходов состоит в том, чтобы оператор ввода для вашего типа обрабатывал условия ошибки (пример ниже).Если пользователь хочет, чтобы исключения генерировались при ошибке, setstate вызовет соответствующее исключение.А пользователи, которые не хотят исключений, просто проверят состояние потока ввода обычным способом.

std::istream &operator>>(std::istream &is, vector_t &v) {
    char c;
    if (is >> c && c == '(') {
        int x = 0;
        if (is >> x >> c && c == ',') {
            int y = 0;
            if (is >> y >> c && c == ',') {
                int z = 0;
                if (is >> z >> c && c == ')') {
                    v = {x, y, z};
                    return is;
                }
            }
        }
    }
    is.setstate(std::ios_base::failbit);
    return is;
}
...