简体   繁体   中英

C++ template class with callback function

Even though I read quite some about templates, this is the first time I try to actually use them. Thus my problem might be just a missing understanding of templates.

I have a class which is supposed to independently and periodically generate some data. When there is new data, it calls the parent via callback function and sends the data. The class is supposed to be portable, thus the templated parent.

But if I use the template on my data class, I have to drag the template with me into every function and it just felt unnecessary complex for just being able to call one function. So I created a small callback class in a global namespace, which I wanted to use as a interface.

namespace wrapper
{
    //Callback class
    template<typename ParentClass>
    class Data_Callback
    {
    public:
        ParentClass* m_parent = nullptr;
        Data_Callback<ParentClass>(ParentClass* parent)
        {
            m_parent = parent;
        };

        void DoCallback(DataClass* data)
        {
            if (m_parent)
                m_parent->callback_func(*data);
        }
    };

    //global object I want to use for callback
    template<typename ParentClass>
    std::unique_ptr<Data_Callback<ParentClass>> m_parentCallback;

    template<typename ParentClass>
    PatientenErkennung GetNewDataClass(ParentClass* parent)
    {
        m_parentCallback = std::make_unique<Data_Callback<ParentClass>>(parent);

        std::shared_ptr<DataClass> obj =  std::make_shared<DataClass>();
        return obj;
    };
}

I can then call my global "GetNewDataClass" function like this from my parent class:

m_data = wrapper::GetNewDataClass(this);

But when I want to call the callback from the DataClass, the template gets in the way:

wrapper::m_parentCallback<I_DONT_HAVE_THIS>->DoCallback(data);

I can't call that global object, without knowing the parent class, can I? Thus the callback can only be initialized from the Data_Callback class, but that class doesn't contain the data.

How do you solve this? Inheritance maybe? Or is the design per se not usable like this? As I said before, I want to avoid to have my larger DataClass drag a template around DataClass, though this would work...

You will need type erasure, which you could roll by hand. But the standard already provides a universal interface for functions (including callbacks) of any kind: std::function (which internally does the type erasure you'd need here).

namespace wrapper
{
    //global object I want to use for callback
    std::function<void(DataClass&)> m_parentCallback;

    template<typename ParentClass>
    auto GetNewDataClass(ParentClass* parent)
    {
        m_parentCallback = [parent](DataClass& dc){ parent->callback_func(dc); };

        std::shared_ptr<DataClass> obj =  std::make_shared<DataClass>();
        return obj;
    };
}

m_parentCallback is declared as a std::function that takes a DataClass reference and returns nothing. We initialize it in GetNewDataClass with a lambda which captures the value of the parent pointer and calls callback_func on that pointer with the DataClass instance passed to it.

(You could actually skip the lambda here and just write

 m_parentCallback = parent->callback_func;

but lambdas are more flexible and thus "canonically" used for this. Plus you will probably get better error messages if parent doesn't have a callback_func .)

To use it, just call it like a function:

wrapper::m_parentCallback(data);

https://godbolt.org/z/nUG9Bx

You probably are aware, but I would strongly discourage using global variables in so tightly linked ways. For now (and possibly for the "lifetime" of your project) it'll work fine, but this really does not scale. wrapper could probably be a class in itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM