函数延迟绑定的C++实现

概述
有时候我们会对相同的数据做不同的操作,例如:

int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
int do_sth(int a, int b, const std::string& function_name) {
        if (function_name == "add") return add(a, b);
        if (function_name == "mul") return mul(a, b);
}
这种做法是可行的,但是当我们还需要添加sub(a, b),div(a, b)等等的多个函数时,每添加一个函数,我们都要在do_sth中添加一个if,很容易出错,也不符合开闭原则。

另一种实现方法是将每个操作单独封装成一个类,然后使用工厂模式创建。这种做法是符合开闭原则的,但是每添加一个函数就要添加一个类未免也太繁琐了。

理想情况下,倘若有一门语言同时结合了c++, Java,Python,应该这样添加函数:

// 函数管理类FunctionManager
//     @Register():将函数注册到这个类中
//     getFunction(): 将已注册的函数返回
class FunctionManager;

@Register("add") // 将函数add注册到FunctionManager,通过字符串"add"能够找到这个函数
int add(int a, int b) { return a + b; }
@Register("mul") // 将函数mul注册到FunctionManager,通过字符串"mul"能够找到这个函数
int mul(int a, int b) { return a * b; }
int do_sth(int a, int b, const std::string& function_name) {
        // 函数管理类根据function_name返回一个std::function
        std::function<int(int, int)> Function = FunctionManager.getFunction(function_name);
        return Function(a, b);
}
FunctionManager: 管理函数的类
@Register("Add"): 将函数指针和函数签名(能够唯一标识该函数的字符串)添加到FunctionManager中
FunctionManager.getFunction(): 根据函数签名返回函数指针
很明显,当前的c++不支持@Register,退而求其次我们使用宏进行注册,最终本文实现的效果是(在最后提供了可运行的完整程序):

// xxx.h
int add(int a, int b);
// xxx.cpp
// 注册函数,参数为:变量名(ADD),函数签名("ADD"),函数指针(add)
_REGISTER_FUNCTION(ADD, "ADD", add);
int add(int a, int b) { return a + b; }
// main.cpp
int do_sth(int a, int b, const std::string& func_sig) {
        // FunctionManager是一个单例
        auto p_function_manager = FunctionManager<decltype(add)>::getInstance();
        // 此处返回已注册的函数
        auto func = p_function_manager->getFunction(func_sig);
        return
}
实现
我们将管理函数的类命名为FunctionManager。仔细分析我们的需求,不难发现实际上我们需要根据字符串查找已注册的函数对象。

查找的实现
STL中已经有了能够满足我们需求的std::map<std::string, FunctionType>容器,因此查找功能已经实现了。需要注意的有:

FunctionType需要外部提供,而函数的返回值、参数都会改变都会影响FunctionType,我们不可能把FunctionType硬编码到程序中,这时候就需要模板。
由于函数指针不能够作为函数的返回值,获取函数的接口只能返回std::function,因此map中存储的也应当是std::function。
考虑到程序中仅有一个FunctionManager,将FunctionManager设置为单例更方便(同时也能满足注册函数的需求)。
template <typename FunctionType>
class FunctionManager {
        // 没有delete这个指针,考虑到这是demo,忽略这个问题
        inline static FunctionManager<FunctionType>* p_function_manager = nullptr;

        std::map<std::string, FunctionType> m_sig_func_map;

public:
        static FunctionManager<FunctionType>* get_instance() {
                if (!p_function_manager) p_function_manager = new FunctionManager<FunctionType>;
                return p_function_manager;
        }
       
        // 实际上不能使用运算符[],因为当map中sig不存在时会自动创建一个<sig, empty>对象,demo中忽略这个问题
        FunctionType get_function(const std::string& sig) { return m_sig_func_map[sig]; }
};
注册的实现
注册实际上就是将函数签名(字符串)和函数(std::function对象)添加到FunctionManager::m_sig_func_map中,只需要添加一个接口:

        // 也可以使用insert,二者间存在一点区别
        void register_function(const std::string& sig, FunctionType function) { m_sig_func_map[sig] = function; }
尝试使用
到这里我们已经可以使用FunctionManager了:

int add(int a, int b) { return a + b; }
void use() {
        std::function<int(int, int)> a(add);
        FunctionManager<std::function<decltype(add)>>::get_instance()->register_function("add", a);
        auto another_add = FunctionManager<std::function<decltype(add)>>::get_instance()->get_function("add");
        std::cout << another_add(1, 3) << std::endl;
}
我们看到FunctionManager的使用其实是很不方便的:

注册时需要提供对应函数的std::function对象,实际上我们在使用的时候只希望提供函数的指针
注册和获取函数时都需要需要获取单例
这些冗杂的代码可以交给单独的接口进行封装:

template <typename FunctionPtr>
void register_function(const std::string& function_sig, FunctionPtr function_ptr) {
        auto function_obj = static_cast<std::function<FunctionPtr>>(function_ptr);
        auto p_function_manager = FunctionManager<decltype(function_obj)>::get_instance();
        p_function_manager->register_function(function_sig, function_obj);
}

template <typename FunctionType>
FunctionType get_function(const std::string& function_sig) {
        auto p_function_manager = FunctionManager<FunctionType>::get_instance();
        return p_function_manager

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值