拦截对std :: streambuf的所有写入

时间:2014-04-25 14:16:47

标签: c++ iostream

我正在创建一个继承std::streambuf的类(异步写入UART)。每当持有std::ostream streambuf的{​​{1}}向其写入字符时,我都需要能够在课堂上讲话(因此我可以启用“写入就绪”中断并实际写入数据)。我的印象是我只需要覆盖xsputn(),但似乎没有被调用。

我可以:

  • 保持中断启用(效率低下)
  • 使用std::endl来呼叫std::streambuf::sync()(阻止,丑陋)
  • 公开另一个功能来开始写(非常难看)
  • 继承std::ostream(很多工作)
  • 其他东西

从设计的角度来看,做到这一点的“正确”方法是什么?

代码:

#include <algorithm>
#include <ostream>
extern "C" void UART0_IRQHandler(void);

#define UART_BUFLEN 128
class uartbuf : public std::streambuf
{
public:
    uartbuf(/*hardware stuff*/);
    ~uartbuf() {};

protected:
    int sync();
    std::streambuf::int_type overflow(std::streambuf::int_type ch);
    std::streamsize xsputn(const char* s, std::streamsize count);

private:
    UART_MemMapPtr regs;
    char buffer[UART_BUFLEN];

    uint8_t fifo_depth;

    bool empty() {return pbase() == pptr();}

    uint8_t fifo_space() {return /*hardware stuff*/;}
    void adjust();
    void write_some();

    uartbuf(const uartbuf&);

    friend void UART0_IRQHandler(void);
    friend void UART_Init();
};

//global instances
uartbuf uart0_sb(/*hardware stuff*/);
std::ostream uart0(&uart0_sb);

//buffer management...

uartbuf::uartbuf(/*hardware stuff*/)
    : regs(r), fifo_depth(1)
{
    setp(buffer, buffer, buffer + UART_BUFLEN);

    //A bunch of hardware setup
}

//move back to the start of the buffer
void uartbuf::adjust()
{
    if (pbase() == buffer)
        return;

    //move unwritten characters to beginning of buffer
    std::copy(pbase(), pptr(), buffer);
    //(pptr - pbase) stays the same, same number of characters
    setp(buffer, buffer + (pptr() - pbase()), epptr());
}

//flush the entire buffer
int uartbuf::sync()
{
    while (!empty())
        write_some();

    return 0; //always succeeds
}

std::streambuf::int_type uartbuf::overflow(std::streambuf::int_type ch)
{
    //entirely full, can't adjust yet
    if (pbase() == buffer)
        write_some();

    adjust();

    //this is guaranteed to not call overflow again
    if (ch != std::streambuf::traits_type::eof())
        sputc(ch);

    return 1; //always succeeds
}

//hardware management...

//writes at least one character
void uartbuf::write_some()
{
    //spin until there is some room
    while(!fifo_space()) ;

    while(!empty() && fifo_space())
    {
        //clear interrupt flag
        clear_interrupt();

        write_char_to_fifo(*pbase());
        setp(pbase() + 1, pptr(), epptr());
    }

    //don't generate any more TDRE interrupts if the buffer is empty
    if (empty())
       turn_off_interrupt();
}

std::streamsize uartbuf::xsputn(const char* s, std::streamsize count)
{
    //don't need to do anything special with the data
    std::streamsize result = std::streambuf::xsputn(s, count);

    //start the TDRE interrupt cycling
    turn_on_interrupt();

    return result;
}

extern "C" void UART0_IRQHandler(void)
{
    //it's a TDRE interrupt
    if (/*it's the interrupt we want*/)
        uart0_sb.write_some(); //this won't block
}

1 个答案:

答案 0 :(得分:1)

您需要覆盖overflow(),而不是其他任何内容。上 初始化,std::streambuf设置缓冲区指针 nullptr;如果你不积极改变这一点,overflow将是。setp 要求输出的每个字符。 (您使用sync来 设置缓冲区。)

是否要为每个角色激活输入, 我不知道。你提到&#34; asynchrously写&#34;在一个点上。 这表明不止一个缓冲区。在{{1}}中,您将开始 当前缓冲区的输出,并设置使用 下一个。在某些时候,你还需要检查一下 异步输出已经完成,以便回收 缓冲。