Возможно ли сделать интерфейс обратного вызова только с атрибутом члена-функции? - PullRequest
2 голосов
/ 08 июля 2019

Контекст:

Встроенный c ++ без использования кучи.

Я хочу освоить свой код (включая его размер), поэтому я бы предпочел не использовать стандартныйlib, такой как std :: function.

1-й подход:

Давайте рассмотрим этот пример (который является упрощенной версией моего кода) с использованием модифицированной версии 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;
}

В этом проекте есть несколько проблем:

  • объекты обратного вызова сохраняются дважды
  • интерфейс имеет атрибуты, не являющиеся членами-функциями: обратные вызовы.
  • больно писать библиотеку, потому что вам нужно написать метод и обратный вызов, и вы должны определить его во всех классах, которые используют ваши обратные вызовы!
  • возможно использование CRTP не подходит,Почему я использую CRTP?См. [Здесь]. ( Как определить шаблонный тип, который может быть унаследован? )

Решение?

Этодаже возможно?

Я на правильном пути?Если нет, то какой инструмент подходит?

Я гуглил и нашел несколько треков, но все еще не могу понять, как это сделать:

1) используя шаблонtypedef

Не понимаю, как

2) работает как аргумент шаблона

Я знаю, чтоПередача функции в качестве аргумента шаблона возможно / допустимо

Но моя попытка не удалась:

#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 ++.

Проблема этого интерфейса обратного вызова заключается в том, что для хранения объекта "FooGenericCallback" требуется указатель, которыйне может быть "FooSpecificCallback", потому что тип объекта не известен в родительском классе.

3) другой способ реализовать обратный вызов в качестве интерфейса

как реализовать обратный вызов в качестве интерфейса

Но решение все еще используетобъект для хранения функций-членов в интерфейсе (или в дочерних элементах интерфейса).

4) Lambdas ...

Я знаючто лямбды упростили бы мою жизнь, на самом деле я сначала сделал это с лямбдами, а размер кода был удвоен с 60 кБ до 120 кБ (!) из-за способа хранения лямбд: в std :: function.Разве ответ не будет "лямбда":)

Ответы [ 4 ]

1 голос
/ 11 июля 2019

Я мог бы упростить ваши потребности, но что не так с:

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

Демо

0 голосов
/ 11 июля 2019

Это решение, основанное на ответе Jarod42, компилируется и работает.

Измените атрибут MotherA x_ на null, negative и positive и проверьте результаты.

#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.
}
0 голосов
/ 09 июля 2019

Вот исправленная версия.

#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: этот код пропускает указатель на обратный вызов, поэтому вам необходимо добавить код для правильной обработки.

0 голосов
/ 08 июля 2019

Я все еще не уверен, правильно ли я понимаю ваше намерение.Но следующий код компилируется без ошибок, хотя я больше не тестировал его:

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;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...