Как создать фабрику, которая ведет себя как объект потока - PullRequest
0 голосов
/ 10 мая 2019

Я пытаюсь создать фабричный класс, который можно читать, как будто это объект потока (sstream кажется наиболее похожим, но я могу ошибаться), иметь возможность предоставить заранее определенное количество значений и затем остановить (состояние отказа, eof), и быть совместимым с cin.Я хочу избежать использования дополнительных библиотек, таких как boost.

#include <sstream>
#include <iostream>

class Factory : public std::stringstream {
    int count;
public:
    Factory() : count{ 0 } {}
    Factory & operator>>(int &x) {
        if (count < 2)
            x = count++;
        else
            this->setstate(std::ios::failbit);
        return *this;
    }
};

int main(int argc, char *argv[]) {
    int a;
    Factory f;
    char choice;
    std::cout << "Read from (c)in or (f)actory? ";
    std::cin >> choice;
    std::istream &ss = (choice == 'f') ? f : std::cin;
    while (ss >> a)
        std::cout << "a = " << a << std::endl;
    return 0;
}

Когда вводится «f», ничего не читается.Если я изменю цикл while для чтения непосредственно из f вместо ss, он будет работать как положено (читает 0, 1 и останавливается).

1 Ответ

0 голосов
/ 11 мая 2019

Помимо здоровой критики в комментариях, давайте рассмотрим, что здесь происходит:

std::istream &ss = (choice == 'f') ? f : std::cin;
while (ss >> a)

istreame ссылка ss вызывает operator>>(int &) (ADL в стороне).Таким образом, даже если ss указывает на ваш класс Factory (который по идее не является фабрикой), он все равно будет вызывать свой базовый оператор, поэтому выбор f никогда не будет работать.

Если вы хотеличтобы достичь того, что вы намеревались, тогда operator>> должно быть определено полиморфно (объявлено как virtual) в базовом классе.Но это не так.

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

#include <sstream>
#include <iostream>

class Factory {
    int count;
    std::istream * is_ptr;
    static std::stringstream dummy;
public:
    Factory(std::istream &is = dummy): count{0}, is_ptr(&is) {}
    operator bool(void) { return count < 5; }
    Factory & operator>>(int &x) { 
        if(is_ptr == &dummy)                   // it's me being called
         { x = count++; return *this; }
        *is_ptr >> x;                          // otherwise, calling a proxy 
        return *this;
    }
};
std::stringstream Factory::dummy;


int main(int argc, char *argv[]) {
    int a;
    Factory f, c(std::cin);
    char choice;
    std::cout << "Read from (c)in or (f)actory?\n";
    std::cin >> choice;
    //choice = 'f';
    Factory &ss = (choice == 'f') ? f : c;
    while (ss >> a)
        std::cout << "a = " << a << std::endl;
    return 0;
}

Выводыс выбором 'f':

Read from (c)in or (f)actory?
f
a = 0
a = 1
a = 2
a = 3
bash $ 

Выходы с выбором 'c':

Read from (c)in or (f)actory?
c
123
a = 123
098765
a = 98765
^C
bash $ 
...