C++实现可变参数回调函数

本文介绍了如何使用C++的可变参数模板和std::function实现可变参数回调函数的注册与调用。通过定义类A,利用模板方法注册不同参数类型的回调函数,并通过枚举类型来区分。在类B中定义具体回调函数,然后在main函数中进行注册和调用。此实现方式使得在不同场景下可以根据需要灵活地注册和调用不同参数类型的回调函数。
摘要由CSDN通过智能技术生成

        在日常编码过程中,一般使用的回调函数的参数类型和数目是固定的,但是在需要根据不同枚举类型注册不同回调函数的场景下,会存在参数类型和数目不固定的情况,本文根据可变参数模板和std::function,实现可变参数回调函数的注册与调用。

        首先定义一个类A,包含:注册回调函数(registCB),第一个参数是枚举类,第二个参数是利用template<typename... Args> 定义的一个std::function函数包装模板,参数不同的回调在此函数中进行注册。回调获取函数(getCB),通过传入的枚举获取对应回调。回调测试函数(callBackTest),是我用来进行测试的函数。这里需要注意的是:类A并没有定义成模板类,所以私有变量callBackFuncs_ 是变量模板,这是从C++14开始支持的特性。

#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <memory>
#include <thread>
#include <functional>
#include <windows.h>
using namespace std;
using namespace std::placeholders;

class A {
public:
	A() {
		std::cout << "create A" << std::endl;
	}

	~A() {
		std::cout << "delete A" << std::endl;
	}

	// 此处用模板,注册不同的回调函数 Args...
	template<typename... Args>
	void registCB(int32_t CBType, std::function<void(Args...)> callBackFunc);

	// 此处用模板,获取不同的回调函数 Args...
	template<typename... Args>
	std::function<void(Args...)> getCB(int32_t CBType);

	// 调用回调函数
	void callBackTest();

private:
	// 注册上的回调函数,非模板类情况下设置为静态成员变量
	// 单独的模板变量是C++14开始支持的
	template<typename... Args>
	static std::map<int32_t, std::function<void(Args...)> > callBackFuncs_;
};

        然后编写类A中函数的具体实现,在注册和获取回调的时候,要注意写上可变参数模板,这与实际调用这两个函数时,所需要设置的实际参数类型是对应的。可以看到callBackTest 在调用具体回调的时候,就要把此处的参数个数和类型进行明确了。

template<typename... Args>
std::map<int32_t, std::function<void(Args...)> > A::callBackFuncs_;

template<typename... Args>
void A::registCB(int32_t CBType, std::function<void(Args...)> callBackFunc) {
	callBackFuncs_<Args...>[CBType] = (callBackFunc);
}

template<typename... Args>
std::function<void(Args...)> A::getCB(int32_t CBType) {
	return callBackFuncs_<Args...>[CBType];
}

void A::callBackTest() {
	auto twoCBFunc = getCB<std::string, int32_t>(2);
	if (twoCBFunc) {
		std::cout << "execute two callback func!" << endl;
		twoCBFunc("laotie", 666);
	}

	auto oneCBFunc = getCB<int32_t>(1);
	if (oneCBFunc) {
		std::cout << "execute one callback func!" << endl;
		oneCBFunc(2333);
	}
}

        我又定义了一个类B,这个类用于编写具体的回调函数,这里写了两个回调函数用于之后的测试,它们具有不同的入参数量、类型和相同的返回。因为回调函数是类B的成员函数,所以在注册回调的时候可以使用std::bind进行绑定。

class B {
public:
	B() {
		std::cout << "create B" << std::endl;
	}

	~B() {
		std::cout << "delete B" << std::endl;
	}

	// 回调函数1,入参为int
	void testCBOne(int32_t testInt);
	// 回调函数2,入参为string和int
	void testCBTwo(std::string testStr, int32_t testInt);
};

void B::testCBOne(int32_t testInt) {
	std::cout << "HAHAHA: " << testInt << std::endl;
}

void B::testCBTwo(std::string testStr, int32_t testInt) {
	std::cout << testStr << " double-click " << testInt << std::endl;
}

        最后在main函数中进行回调的注册和调用,可以看到,在根据枚举值注册具体的回调时,要明确对应的参数类型和个数,不然在编译过程中,编译器是无法找到具体的回调函数,会直接进行报错。

int main() {

	shared_ptr<A> testA = make_shared<A>();
	shared_ptr<B> testB = make_shared<B>();
	// 注册不同的回调函数
	auto oneFunc = std::bind(&B::testCBOne,
		testB,
		::_1);
	testA->registCB(1, std::function<void(
		int32_t)>(oneFunc));

	auto decodeFunc = std::bind(&B::testCBTwo,
		testB,
		::_1, ::_2);
	testA->registCB(2, std::function<void(
		std::string,
		int32_t)>(decodeFunc));

	testA->callBackTest();

	getchar();
	return 0;
}

        将整段代码进行运行,可以看到输入不同的枚举类型时,可以调用对应的回调进行不同参数类型和个数的处理。虽然注册回调函数是同一个,但是在实际注册的时候还是要指定具体的参数类型和个数的,不会实现完全的可变,不然编译器是无法进行对应操作的。

记录一个C++回调的小tip:

        之前写C的回调函数,或者与别的语言进行接口调用的时候,都是通过函数指针的形式实现。但是C++有类的概念,其中通过“继承”这一面向对象的特性,可以实现C++风格的灵活回调。

        我们首先定义一个回调函数抽象类AbstractCB,这个类中包含一个纯虚函数,即回调函数onAbstractCB(int32_t num):

class AbstractCB {
public:
    AbstractCB() {}
    virtual ~AbstractCB() {}

public:
    virtual void onAbstractCB(int32_t num) = 0;
};

        然后定义一个操作类Operation,这个类对数据进行一些操作,并对结果进行回调。Operation类包含注册回调函数和数据处理函数:

class Operation{
public:
    Operation() {
        is_run_ = true;
    }

    ~Operation() {
        is_run_ = false;
    }

public:
    void StartOperation() {
        operation_thread_ = new std::thread([this]() {this->OperationData();});
        operation_thread_.detach();
    }

    void RegistOperationCB(AbstractCB* callback) {
        operation_CB_ = callback;
    }

private:
    void OperationData() {
        uint32_t i = 0;
        while(is_run_) {
            if(0 == i%100) {
                operation_CB_->onAbstractCB();
            }
            i++;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }

private:
    bool is_run_;
    // 数据操作线程
    std::thread operation_thread_;
    // 回调函数类
    AbstractCB* operation_CB_;
};

        这样当我们想要注册回调函数到Operation类的时候,只需要创建一个新的类,继承AbstractCB 这个基类,然后在需要注册的地方,通过调用RegistOperationCB将自身类对象的指针传给Operation 类即可:

class SubClass : public AbstractCB {
public:
    SubClass() {}
    ~SubClass() {}

public:
    void Start() {
        Operation_ = std::make_shared<Operation>();
        Operation_->RegistOperationCB(this);
    }

    void onAbstractCB(int32_t num) {
        std::cout << "Test callback result: " << num << std::endl;
    }

    shared_ptr<Operation> operation_;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值