如何在C++中写入输入流(即cin)?

3

我有一个程序(游戏),它设计成通过键盘从人类用户那里接收输入。然而,在某些时候,我希望从用户那里夺取控制权,并为他们做出某些决定。虽然可以编写特殊情况代码以模拟事件强制执行用户输入的效果,但我更喜欢覆盖输入流(在这种情况下为cin),以便程序对于强制决策的响应与用户自主决策无异。

我尝试像输出流一样将其写入(例如cin<<'z'),但是<<运算符未定义为cin,我不知道如何定义它。

是否最好将其写入键盘缓冲区?如果是这样,如何以系统无关的方式进行操作?

3个回答

7

写入输入是一种很巧妙的方法。更好的设计是在实际输入(如cin)和处理该输入的游戏之间放置一个抽象层。然后,您可以仅在必要时重新配置此抽象层以响应程序生成的命令,而不是响应cin


那么,将所有来自cin的输入都放入具有自定义功能的缓冲区中,然后将其作为程序的输入流进行操作?我担心您使用的“抽象层”不是我熟悉的方式。不过,这听起来确实可以工作,所以谢谢! - Lurker
@Lurker 是的,就像这样。或者有一个InputSource接口,一个实现为cin,一个实现为“程序生成”,在必要时切换它们——对cin进行抽象化处理。 - Angew is no longer proud of SO
@Lurker,如果抽象模拟了std::istream,那么它被称为装饰器(decorator)。(事实上,你想要装饰的是由std::cin使用的streambuf。) - James Kanze

2
您可以将一个streambuf插入到std::cin中,例如:
class InjectedData : public std::streambuf
{
    std::istream* myOwner;
    std::streambuf* mySavedStreambuf;
    std::string myData;
public:
    InjectedData( std::istream& stream, std::string const& data )
        : myOwner( &stream )
        , mySavedStreambuf( stream.rdbuf() )
        , myData( data )
    {
        setg( myData.data(), myData.data(), myData.data() + myData.size() );
    }
    ~InjectedData()
    {
        myOwner->rdbuf(mySavedStreambuf);
    }
    int underflow() override
    {
        myOwner->rdbuf(mySavedStreambuf);
        return mySavedStreambuf->sgetc();
    }
};

(由于我没有测试过,所以可能会有错误。但基本原则应该是可行的。)
使用std :: cin 作为参数构造此实例将返回 data 中的字符,直到该实例被销毁或所有字符都已被消耗。

有时候展示 X-Y 问题中 Y 部分的答案也是有用的。+1 - Angew is no longer proud of SO
非常好。特别是,它似乎可以很好地处理任意(长)长度的字符串,因为您可以随时从人工输入中获取输出。 - Lurker
@Lurker 如果需要的话,您甚至可以暂时将输入切换到文件;例如,我曾经使用类似的方法来处理宏扩展和包含功能。我还用它来重新格式化输入:去除注释,处理连续行等。 - James Kanze

-1

我相信向cin 写入数据是一个设计不良的程序的症状。

你可能实际上想要有一些事件循环,当然这是特定于操作系统的。在Posix和Linux上,你可以在poll(2)系统调用之上构建它,并且你可能想要设置一个管道(7)从自己的进程到自己。

有几个库提供了一些事件循环:libeventlibev,还有像QtGtkPOCOlibsdl这样的框架...(其中一些已经移植到多个操作系统,因此可以为您提供操作系统之上的抽象层...)


1
实际上,cin.putback(char)是标准的必要部分,并在C++11中进行了更新以更好地工作,表明它仍得到支持。我认为添加一个抽象层是解决我的个人问题的最佳方案,但我认为重写输入流不是一个坏主意,而cin也不例外。 - Lurker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接