1

I want to create a universal ring buffer class for taking different types of connections (uart, can, etc). The problem is in different command types/styles/etc in them. So I decided to work only with pointers (I mean number, not c-pointer) in ring buffer and call functions belonging to different connection types from it.

So I've created this class and typedef for function pointer:

typedef bool(*bufCommandProcessor)(uint8_t pointer);

class rbuf
{
    uint8_t bufsize;
    uint8_t psize;
    uint8_t readPointer;
    uint8_t writePointer;

    bufCommandProcessor processPut;
    bufCommandProcessor processRead;

public:
    rbuf();
    rbuf(uint8_t bsize, bufCommandProcessor putpr, bufCommandProcessor readpr);

    uint8_t cyclicPlus(uint8_t num, uint8_t maxval);
    bool checkEmpty();
    bool checkFull();
    bool putCommand();
    bool readCommand();
};

and here are put/read commands from it

bool rbuf::putCommand() 
{
    if (checkFull()) 
    {
        readPointer = cyclicPlus(readPointer, psize);
        this->processPut(writePointer % bufsize);
        writePointer = cyclicPlus(writePointer, psize);
    }
    else {
        this->processPut(writePointer % bufsize);
        writePointer = cyclicPlus(writePointer, psize);
    }
    return true;
}


bool rbuf::readCommand() 
{
    if (checkEmpty()) {
        return false;
    }
    this->processRead(readPointer % bufsize);
    readPointer = cyclicPlus(readPointer, psize);
    return true;
}

and also (I'm testing logic in Visual Studio, so I use some command that have sense only on desktops) I've created a test UART class:

typedef bool(*putcmd)();

class UART 
{
    uint8_t RingBuffer[BUFSIZE][STRINGSIZE] {};
    uint8_t Command[STRINGSIZE];
    uint8_t Len;
    putcmd uput;
    UART();
    UART(putcmd cput);
    void cmdcm(uint8_t* cmd, uint8_t len);
    bool uartRBput(uint8_t wp);
    bool uartRBget(uint8_t rp);
};

and its commands are:

UART::UART(putcmd cput) 
{
    Command[0] = 0;
    Len = 0;
    RingBuffer[0][0] = 0;
    uput = cput;
}

void UART::cmdcm(uint8_t* cmd, uint8_t len)
{
    Len = len;
    memcpy(Command, cmd, Len);
    memset(&Command[Len], 0, STRINGSIZE-Len);
    uput();
}

bool UART::uartRBput(uint8_t wp)
{
    memcpy(RingBuffer[wp], Command, STRINGSIZE);
    return true;
}

And I want to create instances of it like

UART uart;
rbuf uartRB(BUFSIZE, uart.uartRBput, uart.uartRBget);
uart = UART(rbuf.putCommand);

As an idea, it seems nice, but I cannot figure out how to realize it. Is it even possible?

JeJo
  • 30,635
  • 6
  • 49
  • 88
lazba
  • 129
  • 9
  • 2
    `bufCommandProcessor` is a type-alias for a ***non*** member function. Those can't be used as pointers to (non-static) member function. You might want to read more about [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function), [`std::bind`](https://en.cppreference.com/w/cpp/utility/functional/bind), and [lambdas](https://en.cppreference.com/w/cpp/language/lambda). – Some programmer dude May 05 '22 at 15:41

1 Answers1

2

Is it even possible?

There are ways to work this out. As @Someprogrammerdude pointed in the comments, you could use std::function, std::bind, and lambdas.

Note that the bufCommandProcessor and putcmd are normal function pointers which will not work with the member function pointers.

The solution is std::function with some type-eraser overhead. For instance, wrapping the instace to call and its member function via a lambdas function or std::bind_front (since ) you can do as follows:

Iin rbuf class:

// type aliases for callable function object
using bufCommandProcessor = std::function<bool(uint8_t)>;

class rbuf
{
    // ....
    bufCommandProcessor processPut;
    bufCommandProcessor processRead;

public:
    rbuf() {}
    rbuf(uint8_t bsize, bufCommandProcessor putpr, bufCommandProcessor readpr)
        : processPut{ putpr }
        , processRead{ readpr }
    {}
    // ...
};

In UART class

// type aliases for callable function object
using putcmd = std::function<bool()>;

class UART 
{
    // ...
    std::function<bool()> uput;

public:
    UART() = default;
    UART(putcmd cput)
        : uput{ cput }
    {}
    // ...
};

Now in caller side

UART uart;
rbuf uartRB{ 10u
    , [uart](uint8_t wp) mutable { return uart.uartRBput(wp); }
    , [uart](uint8_t rp) mutable { return uart.uartRBget(rp); }
};
// alternatively using std::bind_front
uart = UART{ std::bind_front(&rbuf::putCommand, uartRB) };

(See a Demo)

JeJo
  • 30,635
  • 6
  • 49
  • 88