2

Context:

Embedded c++ with no heap use.

I want to master my code (its size included), so I would prefer not to use standard lib such as std::function.

1st Approach:

Let's take this example (which is a simplified version of my code) using a modified version of the CRTP:

Note: the method of my callback could have theses 2 signatures: bool (ChildCrtp::*)(void); and void (ChildCrtp::*)(int) (one for action, one for condition).

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

using namespace std;

void* operator new(size_t size)
{
    cout << "ERROR HEAP USED" << endl;
}

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); };
    bool IsNegative(void) { return (getValue() < 0); };
    bool IsEven(void) { return ((getValue() % 2) == 0); };
    bool IsOdd(void) { return ((getValue() % 2) == 1); };

    FooSpecificCallback isPositive_ = FooSpecificCallback(static_cast<ChildCrtp*>(this), &Interface::IsPositive);//line to be removed
    FooSpecificCallback isNegative_ = FooSpecificCallback(static_cast<ChildCrtp*>(this), &Interface::IsNegative);//line to be removed
    FooSpecificCallback isEven_ = FooSpecificCallback(static_cast<ChildCrtp*>(this), &Interface::IsEven);//line to be removed
    FooSpecificCallback isOdd_ = FooSpecificCallback(static_cast<ChildCrtp*>(this), &Interface::IsOdd);//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 << "Is " << c.getValue() << " positive? " << c.callCallback() << endl;
    return 0;
}

This design has several problems:

  • the callback objects are stored twice
  • the interface has non function-member attributes: the callbacks.
  • it is painful to write a lib because you need to write the method and the callback, and you have to define it in all the classes that uses your callbacks!
  • maybe the use of CRTP is not suited. Why am I using CRTP? See [here].(How to define a template specific type that can be inherited?)

Solution?

Is that even possible?

Am I on the right track? If not, what is the right tool?

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

1) using template typedef

Do not see how

2) function as template argument

I know that passing a function as a template argument is possible/valid

But my attempt was not successful:

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

using namespace std;

void* operator new(size_t size)
{
    cout << "ERROR HEAP USED" << endl;
}

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(Interface::*)();

    template<FooPrototype op>
    FooSpecificCallback* checkIf(void)
    {
        //I'm trying to take the address of this temporary object, which is not legal in C++.
        return &FooSpecificCallback(static_cast<ChildCrtp*>(this), 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(checkIf<&Child::IsNegative>());}

};


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

I get the following error

error: taking address of temporary [-fpermissive]

As it is not possible take the address of a temporary object, which is not legal in C++.

The problem with this callback interface is that it needs a pointer to store the object "FooGenericCallback", which cannot be a "FooSpecificCallback" because the object type is not known in the mother class.

3) other way to implement callback as an interface

how to implement callback as an interface

But the solution still uses object to store the function-members in the interface (or in the interface's children).

4) Lambdas...

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 (!), because of the way the lambdas were stored: in std::function. Shall the answer not be "lambda" :)

Guillaume D
  • 2,202
  • 2
  • 10
  • 37
  • PS is fine, PSS is acceptable, but PSSS and PSSSS scare me. It might be better to incorporate the additional information into the text instead of providing them separately. – L. F. Jul 08 '19 at 10:27
  • agreed, edit made. – Guillaume D Jul 08 '19 at 10:33
  • Oops, apparently I was being unclear, and you took my sentence too literally ... What I meant to say is that edits don't have to go at the end, not that exactly two PS's should be retained ... But it is not that important anyway :) – L. F. Jul 08 '19 at 10:37
  • I don't understand what you trying to achieve. What is `Callback` template and why you can't use something like `std::mem_fn`? – sklott Jul 08 '19 at 10:53
  • i don't want to use std library for two main reasons: 1/ I want to master the code I'm writing, so making my own lib, 2/ don't want to depend on std:function because that is what made my code double when using lambdas. – Guillaume D Jul 08 '19 at 11:32
  • OK. Still, your overall goal is unclear. What function `BindPositive()` should achieve? What function `UseCallback()` actually doing? Why you need to pass address of variable rather than address of function? – sklott Jul 08 '19 at 11:50
  • I think [in this link](https://stackoverflow.com/questions/12387740/how-to-implement-callbacks-with-interface) it is quite close to what I want. BindPositive is just a public function to be called when I want to define the child class behavior by adding a new command. Maybe I should rename "UseCallback" into "AddCommand"? Thank you for your patience :D – Guillaume D Jul 08 '19 at 11:55

4 Answers4

1

I might have oversimplified your needs, but what's wrong with:

template<typename Base>
class Interface : public Base
{
public:
    static bool IsNegative(Base* userData)
    {
        auto that = static_cast<Base*>(userData);
        return that->getValue() < 0;
    }
};

class Mother
{
public:
    using Callback = bool (*) (Mother*);

    int getValue() { return x_; }
    void storeCallback(Callback pCallback) { pCallback_ = pCallback; }
    bool callCallback() {return pCallback_ ? (*pCallback_)(this) : throw 42;}
private:
    int x_ = 3; 
    Callback pCallback_;
};

class Child : public Interface<Mother>
{
public:
    void setup(){ storeCallback(&Interface::IsNegative); }
};

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

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I like the alternative approach. I might have mislead you by telling that I had only one type of callback. Indeed, I have two distinct possible signature (one for a condition and one for an action as I'm making an array of callbacks). I edited my post so it says that I need the second signature as well. Anyway I'll have to investigate with your proposition! I give few a feedback after it. – Guillaume D Jul 11 '19 at 08:04
  • is it from a design pattern or something? does this technique have a name? – Guillaume D Jul 11 '19 at 11:49
  • Prior to `std::function` which can handle "capture", the C-way is to register callback with userData pointer. Here, it seems we can have even stronger type. – Jarod42 Jul 12 '19 at 08:13
0

I'm still not sure if I understand your intention correctly. But following code compile without errors, though I didn't test it any further:

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

    SomethingBooleanCallback callback;
public:
    void UseCallback(SomethingBooleanCallback a) {callback = a;}
    bool CallCallback() {return ((ChildCrtp *)this->*callback)();}
};

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); };

};

template<typename ChildCrtp>
class ChildA1 : public SpecializedInterfaceA<ChildCrtp>
{
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) { this->UseCallback(&ChildA1::IsPositive); };

private:
    //an attribute
    int value;
};
sklott
  • 2,634
  • 6
  • 17
  • thank you but: with CRTP, ChildA1 shall use the template ```class ChildA1 : public SpecializedInterfaceA``` . I'll use it as is: ```int main() { ChildA1 c; c.BindPositive(); c.CallCallback(); return 0; }``` – Guillaume D Jul 08 '19 at 12:59
  • furthermore, the interface does have an attribute "callback" which is what I want to avoid. But anyway you helped me clarify my problem. :) – Guillaume D Jul 08 '19 at 13:04
  • my callback template class does the job, but I did'nt want to put it here to not overload the question. – Guillaume D Jul 08 '19 at 13:06
  • If you want to use template parameter instead of attribute, then you need to add it to base class! PS: ChildA1 declaration is copied from your code. – sklott Jul 08 '19 at 13:08
  • "ChildA1 declaration is copied from your code" .yes I corrected it, just pointing it for you. – Guillaume D Jul 08 '19 at 13:16
  • the template parameter is used for the [CRTP](https://stackoverflow.com/questions/56834197/how-to-define-a-template-specific-type-that-can-be-inherited) – Guillaume D Jul 08 '19 at 13:19
  • You can have more than one template parameter. Anyway, if you want dynamic control, i.e. calling functions like `BindPositive()` you will need attribute. If you want static control, i.e. declare type that will always work as if `BindPositive()` was called you need to use template. – sklott Jul 09 '19 at 06:23
  • I edited my post so it contains the implementation that works and the one that I want to be working but is not compiling – Guillaume D Jul 09 '19 at 07:43
  • 1
    You almost did it. You have few errors that is fixable. Setup should look like: `void setup(void){storeCallback(checkIf<&Child::IsNegative>());}`, checkIf should return pointer to callback, not bool and so on... One more important thing is that FooPrototype should be pointer to member function, not just any function `using FooPrototype = bool(Interface::*)();` – sklott Jul 09 '19 at 08:34
  • BTW, I don't see such big difference between `storeCallback(checkIf<&Child::IsNegative>());` vs `storeCallback(checkIf(&Child::IsNegative));` vs `storeCallback(&Child::IsNegative);`, so I'm not sure if you need to complicate things so much... – sklott Jul 09 '19 at 08:45
  • why? it's all the point: I don't have any attribute in the interface, and I don't have to double the lines ```bool myMethod()``` and ```myCallback = ThisCallback(this,myMethod)``` (pseudocode). BTW, despite your advices, it is still not compiling – Guillaume D Jul 09 '19 at 08:58
0

Here are fixed version.

#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(Interface::*)();
    template<FooPrototype op>
    FooSpecificCallback* checkIf(void)
    {
        return new FooSpecificCallback(static_cast<ChildCrtp*>(this), 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(checkIf<&Child::IsNegative>());}

};


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

PS: This code leaks pointer to callback, so it you need to add code to properly handle it.

sklott
  • 2,634
  • 6
  • 17
  • ```PSS: embedded C++ so only static allocation.``` = no heap – Guillaume D Jul 09 '19 at 09:15
  • Oh. Then either remove virtual functions (you always can call directly, since they instatiated to same type anyway) and pass by value or allocate callback instances in some other way, e.g from object pool... – sklott Jul 09 '19 at 09:22
  • which virtual? getValue? I need the getValue virtual function, because I need my interface to provide some methods based on the getValue implemented in my child class – Guillaume D Jul 09 '19 at 09:46
  • Virtual in Callback template. Basically you either need to eliminate need to have pointer to Callback object or somehow allocate Callback objects. There is no way around this. As a minimal solution you can add `FooSpecificCallback myCallback;` as member of `Interface` and re-initialize it in every call to `checkIf`. – sklott Jul 09 '19 at 11:07
0

This solution, inspired from Jarod42's answer, compiles and works.

Change MotherA's attribute x_ to null, negative and positive and check the results.

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

using namespace std;

static constexpr int STORE_SIZE = 4;

void* operator new(size_t size)
{
    cout << "ERROR HEAP USED" << endl;
}

template<typename T, size_t storeSize>
class CallbackStore
{
public:

    CallbackStore() : that_(nullptr) {};
    CallbackStore(T* that) : that_(that) {};

    using CallbackCondition = bool (*) (T*);
    using CallbackAction = void (*) (T*,int);
    struct Step
    {
        CallbackCondition pCallbackCondition;
        CallbackAction pCallbackAction;
    };
    void setStep(int stepId,CallbackCondition pCallbackCondition, CallbackAction pCallbackAction)
    {
        if(stepId<storeSize)
        {
            store[stepId].pCallbackCondition = pCallbackCondition; 
            store[stepId].pCallbackAction = pCallbackAction; 
        }
        else
        {
            cout << "pointer error" << endl;
        }
    }
    void callStep(int stepId, int param) 
    {
        if((stepId<storeSize) &&
        (store[stepId].pCallbackCondition != nullptr) &&
        (store[stepId].pCallbackAction != nullptr) &&
        (that_ != nullptr))
        {
            bool isActive =  (*(store[stepId].pCallbackCondition))(that_);
            if(isActive) {(*(store[stepId].pCallbackAction))(that_,param);}
        }
        else
        {
            cout << "pointer error" << endl;
        }

    }
    Step store[storeSize];
    T* that_;
};

template<typename Base>
class Interface : public Base // interface
{
public:
    static bool True(Base* baseInstance)
    {
        return true;
    }
    static bool IsNegative(Base* baseInstance)
    {
        return ((static_cast<Base*>(baseInstance))->getValue() < 0);
    }
    static bool IsNull(Base* baseInstance)
    {
        return ((static_cast<Base*>(baseInstance))->getValue() == 0);
    }
    static void PrintValue(Base* baseInstance, int value)
    {
        cout << "print this value : " << value << "." << endl;
    }
};

class MotherA
{
public:
    int getValue() { return x_; }
    void setValue(int x) { x_ = x; }

private:
    int x_ = -3; 
};

class ChildA : public Interface<MotherA>, public CallbackStore<MotherA, STORE_SIZE>
{
public:
    ChildA():Interface<MotherA>(), CallbackStore<MotherA, STORE_SIZE>(this){};
    void setup()
    { 
        setStep(0, &Interface::IsNegative, &Interface::PrintValue ); 
        setStep(1, &Interface::IsNull, &Interface::PrintValue ); 
        setStep(2, &Interface::IsNull, &Interface::PrintValue ); 
        setStep(3, &Interface::True, &Interface::PrintValue ); 
    }

};

int main()
{
    ChildA c;
    c.setup();
    for(int i = 0; i < STORE_SIZE; i++)
    {
        c.callStep(i,8);
    }
    // shall print "print this value : 8." 3 times if x_ is null, twice if x_ is negative.
}
Guillaume D
  • 2,202
  • 2
  • 10
  • 37