使用std::function模板和std::bind不破坏c++封装性使用c风格回调

1、问题

在c++调用一些使用回调的c代码库时,因为回调函数要声明为static方式,而static成员是在类的每个对象中共享的,这就破坏了c++的封装性,且静态方法无法调用非静态成员,代码就会变得拧巴,使用c++11的一些新特性我们可以解决这个问题,现记录下常用的c++使用回调的方式。

2、解决方式

2.1 使用静态方法或非成员函数

这种方法是比较常规的,也是受限较大的,使用静态方法就不能使用类的非静态成员,破坏了类的封装性,具体实现方法如下:

#include 

class MyClass {
public:
	// This is our application callback handler for when a message is received, we will
  // pass this into the library which deals with message parsing
  // The "static" keyword makes it easy, as now this function does not
  // take a this pointer and has the same signature as a plain C function
	static void onMsg(int num1, int num2) {
    printf("onMsg() called with num1=%i, num2=%i\n", num1, num2);
    // NOTE: Can't call any non-static method functions here!
  }
};

class LibraryClass {
public:
	// For the library class to call the onMsg, it has to be passed both an instance
  // of MyClass and a pointer to the member function to call
	// Note that MyClass has to be known here! This creates undesired coupling...in
  // reality your library should never have to know about MyClass
	void passACallbackToMe(void (*onMsg)(int num1, int num2)) {
		// Call the callback function
		onMsg(1, 2);
	}
};

int main() {
	MyClass myClass;
	LibraryClass libraryClass;

	// Provide the instance and function to call
	libraryClass.passACallbackToMe(&myClass.onMsg);
}

2.2 静态变量,带模板

这种方法略微复杂,使用了c++的模板函数等特性,但是灵活性非常好,以下为代码

  1. 引用c代码
/*fun.h*/
#pragma once
#include 

#define MY_CALLBACK
typedef void (MY_CALLBACK* m_CallBack)(int,int);

static m_CallBack p_callback = NULL;
void fun1(int a, int b)
{
	p_callback(a, b);
}

void setCallback(m_CallBack p)
{
	p_callback = p;
}
  1. c++调用代码
/*main.cpp*/
#include 
#include 
#include 
#include "fun1.h"
using namespace std;


template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
    template <typename... Args>
    static Ret callback(Args... args) {
        return func(args...);
    }
    static std::function<Ret(Params...)> func;
};

template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

class ClassWithCallback {
public:
    void init() {
		Callback<void(int, int)>::func = std::bind(&ClassWithCallback::method_to_callback, this, std::placeholders::_1, std::placeholders::_2);
        myCallBack func = static_cast<myCallBack>(Callback<void(int, int)>::callback);
        setCallback(func);
    }
    void dosomething() {
		fun1(5, 2);
    }
    void method_to_callback(int num1, int num2) {
        int o = num1 + num2;
        printf("--Value: %i\n", o);
    }
};


int main() {
    ClassWithCallback my_class;

    // Now we can pass this function to a C API which just wants a standard function callback 
    my_class.init();
    my_class.dosomething();
}

2.3 使用 std::function

这一种有个前提就是回调函数可修改,且回调函数语言为c++,受限也比较大,但是对于c++语言编程优化是一种提高,代码如下:

#include 
#include 

class LibraryClass {
public:
	void passACallbackToMe(std::function<int(int, int)> callback) {
	    // Now invoke (call) the callback
		int o = callback(1, 2);
        printf("Value: %i\n", o); // We might be on an embedded system, use printf() and not std::cout
	}
};

class MyClass {
public:
      int methodToCallback(int num1, int num2) {
          return num1 + num2;
      }
};

int main()
{
    MyClass myClass;
    
    LibraryClass libraryClass;
    // Use a lambda to capture myClass and call the member method
    libraryClass.passACallbackToMe([&myClass](int num1, int num2) -> int {
        return myClass.methodToCallback(num1, num2);
    });
}

因为想要实现在classWithCallback中调用lib效果,我对上述代码做了以下修改:

/*fun1.h*/
#pragma once
#include 
#include 
#include 
using namespace std;

class callbackLib {
public:
    void passACallbackToMe(std::function<void(int, int)> callback) {
        m_callback = callback;
    }
    
    void dosomething(int a,int b) {
        if (m_callback) {
            m_callback(a, b);
        }
    }
private:
    function<void(int, int)> m_callback = nullptr;
};

回调实现类和main实现:

/*main.cpp*/
#include 
#include 
#include 
#include "fun1.h"
using namespace std;

class ClassWithCallback {
public:
    void init() {
        pLib = new callbackLib();
		pLib->passACallbackToMe(std::bind(&ClassWithCallback::method_to_callback, this, std::placeholders::_1, std::placeholders::_2));
    }
    void method_to_callback(int num1, int num2) {
        int o = num1 + num2;
        printf("--Value: %i\n", o);
    }
	void dosomething() {
		pLib->dosomething(6, 7);
	}
private:
    callbackLib* pLib = nullptr;
};


int main() {
    ClassWithCallback my_class;

    // Now we can pass this function to a C API which just wants a standard function callback 
    my_class.init();
    my_class.dosomething();
}

这三种是我找到的比较常见的c++调用回调的方式,不足之处请大家指教。
参考链接

你可能感兴趣的:(语言学习,c++,c语言,开发语言)