mardi 9 juillet 2019

How to make a CRTP based callback interface?

I'm trying to implement the CRTP pattern for a callback interface:

My mother interface, which defines a callback type.

template<typename ChildCrtp>
class MotherInterface
{
protected:
    //Callback types
    using SomethingBooleanCallback = Callback<ChildCrtp, bool>;
protected:
    //Helper methods
    bool AlwaysTrue(void) { return true; };
};

note that the method of my callback as only one possible signature which is bool &ChildCrtp::foo(void);

A specialization of this interface:

template<typename ChildCrtp>
class SpecializedInterfaceA : public MotherInterface<ChildCrtp>
{
public:
    /// methods to be overridden in child methods where the callbacks need to be bound
    virtual int GetValue (void) const = 0;

protected:
    ///another helper methods
    bool IsPositive(void) { return (GetValue() > 0); };
    bool IsNegative(void) { return (GetValue() < 0); };
    bool IsEven(void) { return ((GetValue() % 2) == 0); };
    bool IsOdd(void) { return ((GetValue() % 2) == 1); };

public://part that I want to remove because it repeats itself withe the helper methods
   SomethingBooleanCallback alwaysTrue = SomethingBooleanCallback(static_cast<ChildCrtp*>(this), &SpecializedInterfaceA::AlwaysTrue);
   SomethingBooleanCallback isPositive = SomethingBooleanCallback(static_cast<ChildCrtp*>(this), &SpecializedInterfaceA::IsPositive);
   SomethingBooleanCallback isNegative = SomethingBooleanCallback(static_cast<ChildCrtp*>(this), &SpecializedInterfaceA::IsNegative);

   //...
};

Then in my child, I define the methods to be bound

class ChildA1 : public SpecializedInterfaceA<ChildA1>
{
public:
    //implements the interface 
    virtual int GetValue (void) const final override { return value;} ;

    //bind the interfaces' callback by a reference to the object "isPositive", which contains a pointer to the desired method and a pointer to the object that owns the method)
    void BindPositive(void) { UseCallback(&isPositive); };

private:
    //an attribute
    int value;
};

I want to change the use of the interface by not having to define the callbacks in the interface, because it doubles the lines, and this is not an interface anymore as it has some attribute.

I'm looking for something like, removing the part where callbacks are defined, and calling directly the method like this, a typedef template (which is not correct but you get the idea).

pseudo code:

template<function Foo>
using SomethingBooleanFromMethod<Foo> = static_cast<&SomethingBooleanCallback>(&SomethingBooleanCallback(static_cast<ChildCrtp*>(this), &SpecializedInterfaceA::Foo));

void BindPositive(void) { UseCallback(static_cast<SomethingBooleanFromMethod<IsPositive>; };

Is that even possible? Am I on the right track? If not, what is the right tool?

I know that lambdas would have simplified my life, indeed I did it first with lambdas and the code size was doubled from 60kB to 120kB! Shall the answer not be "lambda" :)

I've googled around and found several tracks but still cannot figure out how to do it:

PS: Why am I using CRTP? see here

PSS: embedded C++ so only static allocation = no heap

PSSS: my interface should only contain function member (no other attribute)


In other words, I want this compiling code:

#include <iostream>
#include <stdint.h>

using namespace std;

template <typename FunctionType = void, typename... ArgumentType>
class GenericCallback
{
public:
    virtual ~GenericCallback(){}
    virtual FunctionType    Execute(ArgumentType... arg) = 0;       //!< execute callback
    virtual bool            IsValid() const = 0;                    //!< check if callback is valid
};

template <typename ObjectType, typename FunctionType = void, typename... ArgumentType>
class Callback : public GenericCallback<FunctionType, ArgumentType...>
{
public:
    Callback() ://!< Default constructor
        pObject_m(0),
        pFunction_m(0)
    {
    }
    Callback(ObjectType* pObject_m, FunctionType(ObjectType::*pFunction_m)(ArgumentType...))//!< Constructor
    {
        this->pObject_m = pObject_m;
        this->pFunction_m = pFunction_m;
    }
    virtual FunctionType Execute(ArgumentType... arg)//!< execute callback implementation
    {
        return (pObject_m->*pFunction_m)(arg...);
    }
    virtual bool IsValid(void) const//!< callback validity check implementation
    {
        return (pObject_m != 0) && (pFunction_m != 0);
    }
private:
    ObjectType* pObject_m;                                          //!< pointer to object where the callback is defined
    FunctionType(ObjectType::* pFunction_m)(ArgumentType...);       //!< pointer to the callback (function-member) of the object
};

template<typename ChildCrtp>
class Interface
{
public:

    using FooSpecificCallback = Callback<ChildCrtp, bool>;

    virtual int getValue(void) = 0;
    bool IsPositive() { return (getValue() > 0); };
    FooSpecificCallback isPositive_ = FooSpecificCallback(static_cast<ChildCrtp*>(this), &Interface::IsPositive);//line to be removed
};

class Mother
{
public:
    using FooGenericCallback = GenericCallback<bool>* ;
    int getValue(){return x_;};
    void storeCallback(FooGenericCallback pCallback){pCallback_ = pCallback;};
    bool callCallback(){return (pCallback_->IsValid() == false)?:pCallback_->Execute();};
private:
    int x_ = 3; 
    FooGenericCallback pCallback_;
};

class Child : public Mother, public Interface<Child>
{
public:
    int getValue(){return Mother::getValue();}
    void setup(void){storeCallback(&isPositive_);}
};


int main()
{
    Child c;
    c.setup();
    cout << std::boolalpha << "expectFalse: " << c.callCallback() << endl;
    return 0;
}

to be changed in that idea, but with no compilation error :)

#include <iostream>
#include <stdint.h>

using namespace std;

template <typename FunctionType = void, typename... ArgumentType>
class GenericCallback
{
public:
    virtual ~GenericCallback(){}
    virtual FunctionType    Execute(ArgumentType... arg) = 0;       //!< execute callback
    virtual bool            IsValid() const = 0;                    //!< check if callback is valid
};

template <typename ObjectType, typename FunctionType = void, typename... ArgumentType>
class Callback : public GenericCallback<FunctionType, ArgumentType...>
{
public:
    Callback() ://!< Default constructor
        pObject_m(0),
        pFunction_m(0)
    {
    }
    Callback(ObjectType* pObject_m, FunctionType(ObjectType::*pFunction_m)(ArgumentType...))//!< Constructor
    {
        this->pObject_m = pObject_m;
        this->pFunction_m = pFunction_m;
    }
    virtual FunctionType Execute(ArgumentType... arg)//!< execute callback implementation
    {
        return (pObject_m->*pFunction_m)(arg...);
    }
    virtual bool IsValid(void) const//!< callback validity check implementation
    {
        return (pObject_m != 0) && (pFunction_m != 0);
    }
private:
    ObjectType* pObject_m;                                          //!< pointer to object where the callback is defined
    FunctionType(ObjectType::* pFunction_m)(ArgumentType...);       //!< pointer to the callback (function-member) of the object
};

template<typename ChildCrtp>
class Interface
{
public:

    using FooSpecificCallback = Callback<ChildCrtp, bool>;
    using FooPrototype = bool(*)();
    template<FooPrototype op>
    bool checkIf(void)
    {
        return FooSpecificCallback(static_cast<ChildCrtp*>(this), &Interface::op);;
    }

    virtual int getValue(void) = 0;
    bool IsNegative() { return (getValue() < 0); };

};

class Mother
{
public:
    using FooGenericCallback = GenericCallback<bool>* ;
    int getValue(){return x_;};
    void storeCallback(FooGenericCallback pCallback){pCallback_ = pCallback;};
    bool callCallback(){return (pCallback_->IsValid() == false)?:pCallback_->Execute();};
private:
    int x_ = 3; 
    FooGenericCallback pCallback_;
};

class Child : public Mother, public Interface<Child>
{
public:
    int getValue(){return Mother::getValue();}
    void setup(void){storeCallback(&Interface::checkIf<IsNegative>);}

};


int main()
{
    Child c;
    c.setup();
    cout << std::boolalpha << "expectFalse: " << c.callCallback() << endl;
    return 0;
}

I get the following error

main.cpp: In member function ‘void Child::setup()’:
main.cpp:77:67: error: no matching function for call to ‘Child::storeCallback()’
     void setup(void){storeCallback(&Interface::checkIf<IsNegative>);}
                                                                   ^
main.cpp:66:10: note: candidate: void Mother::storeCallback(Mother::FooGenericCallback)
     void storeCallback(FooGenericCallback pCallback){pCallback_ = pCallback;};
          ^~~~~~~~~~~~~
main.cpp:66:10: note:   no known conversion for argument 1 from ‘’ to ‘Mother::FooGenericCallback {aka GenericCallback*}’

Aucun commentaire:

Enregistrer un commentaire