环境:

  win7_x64旗舰版、VS2015企业版

场景:

  C++标准库提供std::function类来将一个对象的调用操作封装在一个对象内部,然后可以委托调用,但是有一些弊端,例如下面的需求:

    我们需要将调用操作封装存储到一个map中,来实现观察者模式或信号槽,由于std::function是在编译期确定类型,导致你无法将不同类型的std::function(例如std::function<void()>和std::function<void(int)>)放入同一个map中。

  function_delegate里就是为了解决上面的问题而写的。

实现代码:

function_delegate.h

/**
* @file function_delegate.h
* @brief 函数委托
* @author DC
* @date 2019-04-16
* @note
* @example
* class my_class
* {
* public:
* void test(int a, double b)
* {
* }
* };
*
* my_class my;
* function_delegate<> delegate(&my, &my_class::test);
* delegate(1, 2.0);
*
*/ #ifndef FUNCTION_DELEGATE_H_
#define FUNCTION_DELEGATE_H_ namespace function_delegate_pri
{
///< 对象成员函数指针
class member_ptr
{
public:
///< 哈希函数
typedef std::_Bitwise_hash<member_ptr> hash;
///< 构造函数
template<class T, class Func>
member_ptr(T* obj, Func func)
{
obj_ = obj;
*(Func*)&func_ = func;
}
///< 小于函数
bool operator <(const member_ptr& ptr) const
{
if (func_[] != ptr.func_[]) {
return func_[] < ptr.func_[];
}
if (func_[] != ptr.func_[]) {
return func_[] < ptr.func_[];
}
return obj_ < ptr.obj_;
}
///< 相等函数
bool operator ==(const member_ptr& ptr) const
{
if (func_[] != ptr.func_[]) {
return false;
}
if (func_[] != ptr.func_[]) {
return false;
}
return obj_ == ptr.obj_;
}
///< 调用函数
template
<
typename T,
typename U,
typename Result,
typename ... Args
>
Result invoke(Args... args)
{
typedef Result(U::*Call)(Args...);
Call call = *(Call*)&func_;
return (((T*)obj_)->*call)(args...);
} void* obj_{nullptr}; ///< 对象指针
///< 对象成员函数需要使用三个指针,C++多重虚拟继承导致
void* func_[3]{nullptr}; ///< 类成员函数指针
}; ///< 函数委托
template<class Strategy = void>
class delegate_impl
{
public:
///< 设置对象和成员函数
template
<
typename T,
typename U,
typename Result,
typename ... Args
>
delegate_impl(T* ptr, Result(U::*fn)(Args...)) : refcnt_(ptr), ptr_(ptr, fn)
{
typedef Result(*Invoke)(member_ptr*, Args...);
Invoke call = &delegate_impl::invoke<T, U, Result, Args...>;
invoke_ = call;
} bool operator <(const delegate_impl& func) const
{
if (invoke_ != func.invoke_) {
return invoke_ < func.invoke_;
}
return ptr_ < func.ptr_;
} ///< 调用成员函数
template
<
typename Result = void,
typename ... Args
>
Result operator()(Args... args) const
{
typedef Result(*Invoke)(member_ptr*, Args...);
Invoke call = (Invoke)invoke_;
return call((member_ptr*)&ptr_, args...);
} Strategy* object() const { return refcnt_; } private:
template
<
typename T,
typename U,
typename Result,
typename ... Args
>
static Result invoke(member_ptr* ptr, Args... args)
{
return ptr->invoke<T, U, Result>(args...);
} Strategy* refcnt_{ nullptr }; ///<
member_ptr ptr_; ///< 成员函数指针
void* invoke_{ nullptr }; ///< invoke函数地址
}; template<>
class delegate_impl<void>
{
public:
///< 设置对象和成员函数
template
<
typename T,
typename U,
typename Result,
typename ... Args
>
delegate_impl(T* ptr, Result(U::*fn)(Args...)) : ptr_(ptr, fn)
{
typedef Result(*Invoke)(member_ptr*, Args...);
Invoke call = &delegate_impl::invoke<T, U, Result, Args...>;
invoke_ = call;
} bool operator <(const delegate_impl& func) const
{
if (invoke_ != func.invoke_) {
return invoke_ < func.invoke_;
}
return ptr_ < func.ptr_;
} ///< 调用成员函数
template
<
typename Result = void,
typename ... Args
>
Result operator()(Args... args) const
{
typedef Result(*Invoke)(member_ptr*, Args...);
Invoke call = (Invoke)invoke_;
return call((member_ptr*)&ptr_, args...);
} private:
template
<
typename T,
typename U,
typename Result,
typename ... Args
>
static Result invoke(member_ptr* ptr, Args... args)
{
return ptr->invoke<T, U, Result>(args...);
} member_ptr ptr_; ///< 成员函数指针
void* invoke_{ nullptr }; ///< invoke函数地址
};
} using function_ptr = function_delegate_pri::member_ptr;
template<class Strategy = void>
using function_delegate = function_delegate_pri::delegate_impl<Strategy>; #endif ///< !FUNCTION_DELEGATE_H_

测试代码:

function_delegate_test.h

#include <iostream>
#include <vector>
#include <functional>
#include "time_stamp.h"
#include "function_delegate.h" namespace function_delegate_unit_test
{
class testa
{
public:
virtual int testa1(int n)
{
std::cout << "testa::testa1\tparam:" << n << ", data:";
for (int v : avec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
}
int testa2(int n)
{
std::cout << "testa::testa2\tparam:" << n << ", data:";
for (int v : avec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
} protected:
std::vector<int> avec_{ , , };
}; class testb
{
public:
virtual int testb1(int n)
{
std::cout << "testb::testb1\tparam:" << n << ", data:";
for (int v : bvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
}
int testb2(int n)
{
std::cout << "testb::testb2\tparam:" << n << ", data:";
for (int v : bvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
} protected:
std::vector<int> bvec_{ , , };
}; class testc : public testa, public testb
{
public:
int testa1(int n) override
{
std::cout << "testc::testa1\tparam:" << n << ", data:";
for (int v : bvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return testa::testa1(n);
} int testb1(int n) override
{
std::cout << "testc::testb1\tparam:" << n << ", data:";
for (int v : bvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return testb::testb1(n);
} int test(int n)
{
std::cout << "testc::testb1\tparam:" << n << ", data:";
for (int v : avec_) {
std::cout << v << ",";
}
for (int v : bvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
}
}; class testd
{
public:
virtual int test(int n)
{
std::cout << "testd::test\tparam:" << n << ", data:";
for (int v : dvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
}
int testd1(int n)
{
std::cout << "testd::testd1\tparam:" << n << ", data:";
for (int v : dvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
} protected:
std::vector<int> dvec_{ , , };
}; class teste
{
public:
virtual int test(int n)
{
std::cout << "teste::test\tparam:" << n << ", data:";
for (int v : evec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
}
int teste1(int n)
{
std::cout << "teste::teste1\tparam:" << n << ", data:";
for (int v : evec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
} protected:
std::vector<int> evec_{ , , };
}; class testf : public virtual testd, public virtual teste
{
public:
virtual int test(int n) override
{
std::cout << "teste::test\tparam:" << n << ", data:";
for (int v : dvec_) {
std::cout << v << ",";
}
for (int v : evec_) {
std::cout << v << ",";
}
std::cout << std::endl; testd::test(n);
teste::test(n);
return n;
}
int testf1(int n)
{
std::cout << "testf::testf1\tparam:" << n << ", data:";
for (int v : dvec_) {
std::cout << v << ",";
}
for (int v : evec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
}
}; class testg
{
public:
int test(int n)
{
std::cout << "testg::test\tparam:" << n << ", data:";
for (int v : gvec_) {
std::cout << v << ",";
}
std::cout << std::endl;
return n;
} protected:
std::vector<int> gvec_{ , , };
}; ///< 单元测试
void test()
{
std::cout << "function_delegate_unit_test start" << std::endl;
///< TEST testa
testa a;
{
function_delegate<> delegate(&a, &testa::testa1);
delegate();
}
{
function_delegate<> delegate(&a, &testa::testa2);
delegate();
} ///< TEST testb
testb b;
{
function_delegate<> delegate(&b, &testb::testb1);
delegate();
}
{
function_delegate<> delegate(&b, &testb::testb2);
delegate();
} ///< TEST testc
testc c;
{
function_delegate<> delegate(&c, &testa::testa1);
delegate();
}
{
function_delegate<> delegate(&c, &testa::testa2);
delegate();
}
{
function_delegate<> delegate(&c, &testb::testb1);
delegate();
}
{
function_delegate<> delegate(&c, &testb::testb2);
delegate();
}
{
function_delegate<> delegate(&c, &testc::testa1);
delegate();
}
{
function_delegate<> delegate(&c, &testc::testa2);
delegate();
}
{
function_delegate<> delegate(&c, &testc::testb1);
delegate();
}
{
function_delegate<> delegate(&c, &testc::testb2);
delegate();
}
{
function_delegate<> delegate(&c, &testc::test);
delegate();
} ///< TEST testd
testd d;
{
function_delegate<> delegate(&d, &testd::testd1);
delegate();
}
{
function_delegate<> delegate(&d, &testd::test);
delegate();
} ///< TEST teste
teste e;
{
function_delegate<> delegate(&e, &teste::teste1);
delegate();
}
{
function_delegate<> delegate(&e, &teste::test);
delegate();
} ///< TEST testf
testf f;
{
function_delegate<> delegate(&f, &testd::testd1);
delegate();
}
{
function_delegate<> delegate(&f, &testd::test);
delegate();
}
{
function_delegate<> delegate(&f, &teste::teste1);
delegate();
}
{
function_delegate<> delegate(&f, &teste::test);
delegate();
}
{
function_delegate<> delegate(&f, &testf::testd1);
delegate();
}
{
function_delegate<> delegate(&f, &testf::teste1);
delegate();
}
{
function_delegate<> delegate(&f, &testf::test);
delegate();
} ///< TEST testg
testg g;
{
function_delegate<> delegate(&g, &testg::test);
delegate();
}
std::cout << "function_delegate_unit_test end" << std::endl;
}
} namespace function_delegate_efficiency_test
{
class testa
{
public:
int test(int n)
{
return n + ;
}
}; class testb
{
public:
virtual int test(int n)
{
return n + ;
}
};
class testc : public testb
{
public:
int test(int n) override
{
return n + ;
}
}; ///< 性能测试
void test(int count)
{
std::cout << "function_delegate_efficiency_test start" << std::endl;
testa a;
{
time_stamp ts;
for (int i = ; i < count; ++i) {
a.test(i);
}
double tm = ts.milliseconds();
std::cout << "count:" << count << "\tc++ object call:\t\t" << tm << "(ms)" << std::endl;
}
{
time_stamp ts;
std::function<int(int)> func = std::bind(&testa::test, &a, std::placeholders::_1);
for (int i = ; i < count; ++i) {
func(i);
}
double tm = ts.milliseconds();
std::cout << "count:" << count << "\tstd::function call:\t\t" << tm << "(ms)" << std::endl;
}
{
time_stamp ts;
function_delegate<> delegate(&a, &testa::test);
for (int i = ; i < count; ++i) {
delegate(i);
}
double tm = ts.milliseconds();
std::cout << "count:" << count << "\tdelegate call:\t\t\t" << tm << "(ms)" << std::endl;
} testc c;
testb* pb = &c;
{
time_stamp ts;
for (int i = ; i < count; ++i) {
pb->test(i);
}
double tm = ts.milliseconds();
std::cout << "count:" << count << "\tc++ pointer virtual call:\t" << tm << "(ms)" << std::endl;
}
{
time_stamp ts;
std::function<int(int)> func = std::bind(&testb::test, pb, std::placeholders::_1);
for (int i = ; i < count; ++i) {
func(i);
}
double tm = ts.milliseconds();
std::cout << "count:" << count << "\tstd::function virtual call:\t" << tm << "(ms)" << std::endl;
}
{
time_stamp ts;
function_delegate<> delegate(pb, &testb::test);
for (int i = ; i < count; ++i) {
delegate(i);
}
double tm = ts.milliseconds();
std::cout << "count:" << count << "\tdelegate virtual call:\t\t" << tm << "(ms)" << std::endl;
}
std::cout << "function_delegate_efficiency_test end" << std::endl;
}
}

  1)其中time_stamp.h包含一个计时类time_stamp的实现,这里没贴代码,可以自己实现。

  2)function_delegate类使用成员data_[0]、data_[1]和data_[2]存储成员函数指针是为了处理被委托的类存在多重继承的情况(用于动态调整this指针,编译器自动调整),如果只使用一个void*存储会发生崩溃。

  注意:这里有个奇怪的现象,对于使用虚拟继承类成员函数,在我的台式机上测试只需要使用data_[0]和data_[1]即可,而在我的笔记本上测试需要使用data_[0]、data_[1]和data_[2] ,所以我统一使用data_[3]存储。

扩展:

  1)function_delegate类可以用于实现观察者模式和信号槽等,后面会有单独的文章说明。

  2)关于C++ 成员函数指针使用两个void*存储的原因:https://www.oschina.net/translate/wide-pointers?cmp

C++函数委托的更多相关文章

  1. 转载 C#匿名函数 委托和Lambda表达式

    转载原出处: http://blog.csdn.net/honantic/article/details/46331875 匿名函数 匿名函数(Anonymous Function)是表示“内联”方法 ...

  2. C#编程.函数.委托

    注:委托最重要的用途最讲到事件和事件处理时才能说清,这里先简单介绍一下关于委托的一些内容 委托是一种可以把引用存储为函数的类型.这听起来相当棘手,但其机制是非常简单的. 1)委托的声明非常类似与函数, ...

  3. [Effective JavaScript 笔记]第26条:使用bind方法实现函数的柯里化

    bind方法的作用,除了有绑定函数到对象外,我们来看看bind方法的一些其它应用. 简单示例 例子:假设有一个装配URL字符串的简单函数.代码如下 function simpleURL(protoco ...

  4. C# 代理/委托 Delegate

    本文转载自努力,努力,努力 1. 委托的定义:委托是函数的封装,它代表一"类"函数.他们都符合一定的签名:拥有相同的参数列表,返回值类型.同时,委托也可以看成是对函数的抽象,是函数 ...

  5. C#调用C++的DLL函数另一则(delegate) z

    使用DLLImport进行导入函数的事. C#调用C++的函数其实不止这一种方法, 还有一种方法是用delegate申明函数委托进行调用,这种方法略显麻烦,但是可以进行回调并应用指针. 在C#中,首先 ...

  6. Delphi中的“委托”

    .NET中有委托(Delegate)的概念,其声明形式如下所示:     public delegate void MyDelegate(int aIntParam, string aStringPa ...

  7. 读书笔记—CLR via C#委托和attribute

    前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可 ...

  8. C#事件与委托详解

    from https://www.cnblogs.com/sjqq/p/6917497.html C#事件与委托详解[精华 多看看] Delegatedelegate是C#中的一种类型,它实际上是一个 ...

  9. JavaScript 事件委托详解

    基本概念 事件委托,通俗地来讲,就是把一个元素响应事件(click.keydown......)的函数委托到另一个元素: 一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事 ...

随机推荐

  1. 解决 下载额外数据文件失败 以下软件包要求安装后下载附加数据,但其数据无法下载或无法处理 ttf-mscorefonts-installer

    ubuntu 14.04 今天安装完 wine,之后会出现这个问题 原因应该是需要的字体无法下载 那你需要手动下载, 到这个地址下载 http://sourceforge.net/projects/c ...

  2. React.js 小书 Lesson15 - 实战分析:评论功能(二)

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson15 转载请注明出处,保留原文链接和作者信息. 上一节我们构建了基本的代码框架,现在开始完善其 ...

  3. gcc链接非标准(non-standard)命名库

    标准命名库: -lnamespace 标准链接库以lib开头, 并以so/a结尾. example gcc test.c -o test -L. -lhello 非标准命名库: -l:libname ...

  4. 【转载】BaseDao设计

    BaseDao接口设计 1 import java.util.List; /** * Dao接口,定义Dao基本操作 由BaseDaoImpl实现 * @author wht * @param < ...

  5. 课堂笔记&总结与遇错纠错篇

    一.课堂笔记 二.个人总结 在学习和工作JDK是必不可少的程序员必备工具,遇到问题可以在帮助文档寻找答案! 接受能力不足,老师讲的知识点过去了,我经常还在想上一个知识点.希望老师有时候重点可以讲慢点哈 ...

  6. 对SNMP4J的一些封装

    SNMP4J是一个开源的,用Java实现的snmp协议.其中提供了一下API,在这些API上面封装了一些方法,比如SNMP的get-request请求,get-next-request请求等 如果不了 ...

  7. Spring课程 Spring入门篇 5-3 配置切入点 pointcut

    1 解析 1.1 xml常见的配置切入点写法 2 代码演练 2.1 xml配置切入点   1 解析 1.1 xml常见的配置切入点写法 2 代码演练 2.1 xml配置切入点 xml配置: <? ...

  8. Facebook 爬虫

    title: Facebook 爬虫 tags: [python3, facebook, scrapy, splash, 爬虫] date: 2018-06-02 09:42:06 categorie ...

  9. sql and csharp: Split Function

    T-SQL: declare @int int,@prov int,@city int,@str nvarchar(500) set @str='天河麗特青春:中國廣東省廣州市天河區天河路623號天河 ...

  10. Regular Expression 正则表达式

    1. "^"表示以什么字符开始,"$"表示以什么字符结束: 2. \w表示字符类,包括大小写字母和数字: 3. “+”表示一个或多个,"*" ...