c++ - 是否可以只使用 function-member 属性创建回调接口(interface)?

标签 c++ templates interface callback embedded

上下文:

不使用堆的嵌入式 C++。

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

第一种方法:

让我们以使用 CRTP 的修改版本为例(这是我的代码的简化版本) :

注意:我的回调方法可以有以下 2 个签名:bool (ChildCrtp::*)(void);void (ChildCrtp::*)(int)(一个用于操作,一个用于条件)。

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

这个设计有几个问题:

  • 回调对象存储了两次
  • 接口(interface)具有非函数成员属性:回调。
  • 写库很痛苦,因为你需要写方法和回调,而且你必须在所有使用你的回调的类中定义它!
  • 可能不适合使用 CRTP。我为什么要使用 CRTP?请参阅[此处]。(How to define a template specific type that can be inherited?)

解决方案?

这可能吗?

我走在正确的轨道上吗?如果不是,什么是正确的工具?

我用谷歌搜索并找到了几个轨道,但仍然不知道如何去做:

1) 使用模板 typedef

怎么看不出来

2) 函数作为模板参数

我知道将函数作为模板参数传递是 possible/valid

但是我的尝试没有成功:

#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++ 中是不合法的。

这个回调接口(interface)的问题是它需要一个指针来存储对象“FooGenericCallback”,它不能是“FooSpecificCallback”,因为对象类型在母类中是未知的。

3) 将回调实现为接口(interface)的其他方式

how to implement callback as an interface

但解决方案仍然使用对象来存储接口(interface)(或接口(interface)的子级)中的函数成员。

4) Lambdas...

我知道 lambda 会简化我的生活,事实上,我首先使用 lambda 做到了,代码大小从 60kB 增加了一倍到 120kB(!),因为 lambda 的存储方式:在 std::function 中。答案不应该是“lambda”吗:)

最佳答案

我可能过于简化了您的需求,但有什么问题:

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

关于c++ - 是否可以只使用 function-member 属性创建回调接口(interface)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56932216/

相关文章:

C++ 与 Qt4 插件开发

c# - 接口(interface)作为c#中方法的参数

c++ - sqlite select 语句抛出的异常

c++ - wcout与cout、wstring与string的混淆

c++ - 专门化可变参数模板的返回类型

python - Django:如何在语法错误上向用户获取模板调试信息

具有接收指定接口(interface)参数的函数的 Golang 接口(interface)

c++ - 具体语法问题

c++ - 关于volatile关键字的问题

c++ - Clang 和 GCC 误推模板参数