Intercept all writes to a std::streambuf

蹲街弑〆低调 提交于 2019-12-07 19:16:05

问题


I am making a class that inherits std::streambuf (to asynchronously write to a UART). I need to be able to tell in my class whenever the std::ostream that holds the streambuf writes characters to it (so I can enable the "write ready" interrupt and actually write the data). I was under the impression that I just needed to override xsputn(), but that does not seem to get called.

I could:

  • Keep the interrupt enabled (inefficient)
  • Use std::endl to call std::streambuf::sync() (blocking, ugly)
  • Expose another function to start the write (very ugly)
  • Inherit std::ostream (a lot of work)
  • Something else

From a design standpoint, what is the "right" way to do this?

Code:

#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:


You need to override overflow(), and nothing else. On initialization, std::streambuf sets the buffer pointers to nullptr; if you don't actively change this, overflow will be called for every character which is output. (You use setp to set the buffer.)

Whether you want to activate input for every character or not, I don't know. You mention "asynchrously write" at one point. This would suggest more than one buffer. In sync, you start the output for the current buffer, and set things up to use the next. At some point, you will also need to check that the asynchronous output has finished, in order to recycle the buffer.



来源:https://stackoverflow.com/questions/23295693/intercept-all-writes-to-a-stdstreambuf

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!