1

I would like to have the following class setup in a program:

  • A class that implements a buffer. This buffer, when full, would spawn a thread that makes a callback to handle what to do with the full buffer.
  • A base class template that includes a buffer object. Implements the callback function, which makes a call to a virtual function defined in a derived class.
  • A derived class that inherits from base class and implements what to do with the data.

First, the minimal reproducible example:

#include <vector>
#include <iostream>
#include <thread>

template <typename T>
class CallbackBuffer
{
    public:

        std::vector<T> buffer;
        void (*callback)(std::vector<T>);
        std::thread writerThread;

        CallbackBuffer(int bufferSize = 10)
        {
            buffer.resize(bufferSize);
        }

        void setCallback(void (*cb)(std::vector<T>))
        {
            callback = cb;
        }

        void writeCall()
        {
            writerThread = std::thread(callback, buffer);
        }
};

template <typename T>
class Base
{
    public:

        CallbackBuffer<T> buffer;

        Base()
        {
            buffer.setCallback(bufferHandler);
        }

        void bufferHandler(std::vector<T> v)
        {
            for(auto &i : v)
            {
                write(i);
            }
        }

        virtual void write(T i) = 0;
};

class Derived : public Base<int>
{
    public:

        Derived()
        {
        }

        void write(int i)
        {
            std::cout << i << std::endl;
        }
};

int main()
{
    Derived d;
    return 0;
}

I'm getting the following compiler error:

error: invalid use of non-static member function ‘void Base<T>::bufferHandler(std::vector<T>) [with T = int]’

So the compiler needs bufferHandler to be static, but if I did that, then I would not have access to the object's members. Is there a way to sort this, or just a horrible idea?

dvilela
  • 1,200
  • 12
  • 29

2 Answers2

2

You are passing the class member function so you need to have in your CallbackBuffer class something like:

void (Base<T>::*callback)(std::vector<T>);

// ...

void setCallback(void (Base<T>::*cb)(std::vector<T>)) {
    callback = cb;
}

and in Base class:

Base() {
    buffer.setCallback(&Base<T>::bufferHandler);
}

Demo

NutCracker
  • 11,485
  • 4
  • 44
  • 68
  • Nice. How is that there's no problem with duplicate Base declarations? Would this cause a circular header dependecy if every class is defined in a different file? – dvilela Apr 01 '20 at 14:53
  • @derkomai forward declarations are a common thing and shouldn't cause any circular dependency – NutCracker Apr 01 '20 at 14:57
1

Member function pointers have different type than regular functions, that's why your code does not work. What you may want is using std::function instead of raw pointers:

#include <functional>

//...
std::function<void(std::vector<T>)> callback;

// ...

void setCallback(const std::function<void(std::vector<T>)>& cb) {
    callback = cb;
}

and pass it like that:

Base() {
    buffer.setCallback([this](auto& vec){ this->bufferHandler(vec); });
}

IMHO this is much more readable and flexible than passing around member pointers

bartop
  • 9,971
  • 1
  • 23
  • 54
  • This might be more readable but `std::function` are more expensive than the member pointers. – NutCracker Apr 01 '20 at 14:59
  • @NutCracker, could you elaborate a little more on this, please? Also, it seems that auto in lambdas require c++14 (not a problem here). – dvilela Apr 01 '20 at 15:04
  • @derkomai check this post [here](https://stackoverflow.com/a/9088690/5517378) – NutCracker Apr 01 '20 at 15:07
  • @NutCracker thanks, both solutions are 100% valid, but i'm certainly more into the cheapest one for now. – dvilela Apr 01 '20 at 15:10