是否可以仅使用function-member属性创建回调接口? [英] Is that possible to make a callback interface with only function-member attribute?

查看:54
本文介绍了是否可以仅使用function-member属性创建回调接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文:

没有堆使用的嵌入式c ++。

Embedded c++ with no heap use.

I想掌握我的代码(包括它的大小),所以我不想使用标准的库,例如std :: function。

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

第一种方法:

让我们以修改后的 CRTP

注意:这2个签名: bool(ChildCrtp :: *)(void); void(ChildCrtp :: *)(int)(一个代表行动,一个代表条件)。

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

此设计存在几个问题:


  • 回调对象存储两次

  • 该接口具有非函数成员属性:回调。

  • 编写一个lib很痛苦,因为您需要编写方法和回调,并且必须在所有使用回调的类中对其进行定义!

  • 也许使用CRTP不适合。为什么要使用CRTP?请参见[here]。(如何定义可以继承的模板特定类型?

  • 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?)

解决方案?

那有可能吗?

我在正确的轨道上吗?如果没有,那么什么是正确的工具?

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

我在Google周围搜索并找到了几条音轨,但仍然不知道该怎么做:

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

1)使用模板typedef

1) using template typedef

不知道如何

2)函数作为模板参数

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

我收到以下错误

error: taking address of temporary [-fpermissive]

由于不可能获取临时对象的地址,因此在C ++中是不合法的。

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

此回调接口的问题在于它需要一个指向存储对象 FooGenericCallback,该对象不能是 FooSpecificCallback,因为在父类中对象类型未知。

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)其他将回调实现为接口的方式

3) other way 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 ...

4) Lambdas...

我知道lambda会简单在我的生活中,的确,我确实首先使用lambda做到了这一点,并且由于lambda的存储方式:std :: function,所以代码大小从60kB倍增至120kB(!)。答案不应该是 lambda:)

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" :)

推荐答案

此解决方案受Jarod42的回答启发,可以编译和工作。

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

将MotherA的属性 x _ 更改为 null 并检查结果。

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.
}

这篇关于是否可以仅使用function-member属性创建回调接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆