声明:本博客做者与此博客https://blog.csdn.net/cjw_soledad/article/details/105585806相同ios
c++17
支持(可自行修改以兼容c++11
)有时候咱们会对相同的数据作不一样的操做,例如: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
,退而求其次咱们使用宏进行注册,最终本文实现的效果是(在最后提供了可运行的完整程序):ui
// 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
。仔细分析咱们的需求,不难发现实际上咱们须要根据字符串查找已注册的函数对象。this
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
中,只须要添加一个接口:spa
// 也可使用insert,两者间存在一点区别 void register_function(const std::string& sig, FunctionType function) { m_sig_func_map[sig] = function; }
到这里咱们已经可使用FunctionManager
了:.net
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->get_function(function_sig); } int main(int argc, char* argv[]) { register_function<decltype(add)>("add", add); // 只提供指针 auto another_add = get_function<std::function<int(int, int)>>("add"); std::cout << another_add(1, 4) << std::endl; return 0; }
相比之下使用起来方便多了,实际上到这里FunctionManager
的实现就已经完成了,可是还要让FunctionManager
更好用,以及让FunctionManager
适用于更多类型的函数,下面才是文章的重点
现有的FunctionManager
若是直接使用还会存在一个问题:须要由用户保证在get_function
前register_function
。在demo中这并非大问题,可是放在大型项目中,get_function
在多个文件中被屡次调用,须要由用户保证register_function
在全部get_function
前执行,这实在是太危险了。稳妥一点的方法是:
void register_all_function() { auto p_function_manager = /* get singleton instance */ ; p_function_manager->register_function(sig_1, func_1); p_function_manager->register_function(sig_2, func_2); // ... } int main { register_all_function(); }
在main
最开始的时候进行注册是最安全的作法,可是每添加一个函数,用户就要在register_all_function
中进行注册,这也违背了开闭原则。
所以咱们有了新的需求:main函数执行前完成对象的注册。有两种方法可以在main
前执行一段函数:静态成员变量和全局变量,须要注意的是两者均须要在cpp
文件中定义,不可以在头文件中。经过register_function
为全局变量赋值可以在main
前执行register_function
,所以咱们还须要为register_function
添加一个返回值:
template <typename FunctionPtr> bool register_function(const std::string& function_sig, FunctionPtr function_ptr) { // ... return true; }
经过定义全局变量可以在main
执行前将须要的函数注册至FunctionManager
。为了在使用更方便、代码可读性更高,FunctionManager
提供了一个宏_REGISTER_FUNCTION
,用于封装注册函数。其原理以下:
#define _REGISTER_FUNCTION(FunctionSig, FunctionPtr) \ bool b = register_function<decltype(FunctionPtr)>(FunctionSig, FunctionPtr);
这样带来了新的问题:在实际使用时,一个.cpp
文件内一般会有多个类型相同的函数的实现,_REGISTER_FUNCTION
将被调用屡次:
_REGISTER_FUNCTION("ADD", add); int add(int a, int b) { return a + b; } _REGISTER_FUNCTION("MUL", mul); // 编译错误,重复定义的变量b int mul(int a, int b) { return a * b; }
屡次调用_REGISTER_FUNCTION
会致使全局变量b
被重复定义。所以须要用户手动提供不重复的变量名(VariableName
)以防止编译错误,最终_REGISTER_FUNCTION
的实现以下:
#define _REGISTER_FUNCTION(VariableName, FunctionSig, FunctionPtr) \ bool Bool##VariableName = register_function<decltype(FunctionPtr)>(FunctionSig, FunctionPtr); // xxx.h int add(int, int); // xxx.cpp _REGISTER_FUNCTION(ADD, "ADD", add); int add(int a, int b) { return a + b; }
float add(float a, float b) { return a + b; } _REGISTER_FUNCTION(ADD, "ADD", add); auto new_add = get_function<decltype(add)>("ADD");
namespace FunctionManagerTest { float add(float a, float b) { return a + b; } } _REGISTER_FUNCTION(ADD, "ADD", FunctionManagerTest::add); auto new_add = get_function<std::function<decltype(FunctionManagerTest::add)>>("ADD");
template<typename T> T addT(T a, T b) { return a + b; } _REGISTER_FUNCTION(ADD, "ADD", addT<int>); auto new_add = get_function<std::function<decltype(addT<int>)>>("ADD");
class Real { public: static float add(float a, float b) { return a + b; } }; _REGISTER_FUNCTION(ADD, "ADD", Real::add); auto new_add = get_function<std::function<decltype(Real::add)>>("ADD");
template<typename T> class Add { public: static float add(T a, T b) { return a + b; } }; _REGISTER_FUNCTION(ADD, "ADD", Add<int>::add); auto new_add = get_function<std::function<decltype(Add<int>::add)>>("ADD");
如今的FunctionManager
可以支持的函数少了很重要的一类:成员函数。由于成员函数在被调用时会有一个this
指针做为隐式参数,显然直接经过&Real::add
是没法得到this
指针的。这意味着咱们须要添加新的接口。
template<typename FunctionPtr, typename ObjectPtr> bool register_member_function(FunctionPtr func_ptr, ObjectPtr obj_ptr) { return true; }
回看需求,咱们但愿可以在FunctionManager
中获取到函数后,可以直接调用;同时FunctionManager
中管理的也只是可以直接调用的std::function
对象,并未区分红员函数或者非成员函数。如今问题简化为如何为某一个函数提供一个默认的参数(对象指针),提供后,咱们就能像调用普通函数同样调用成员函数了。
咱们知道std::bind
可以将函数与参数绑定,返回一个Callable
对象;该对象可以使用对应的std::funtion
接收;结合std::placeholder
还可以在调用返回的Callable
时提供参数:
class Real { public: int add(int a, int b) { return a + b; } } Real real; std::function<int(int,int)> binded_add = std::bind(&Real::add, &real, std::placeholders::_1, std::placeholders::_2); std::cout << binded_add(2, 1) << std::endl;
如今如何提供默认参数的问题解决了,可是另外一个问题又出现了:代码中的std::function<int(int,int)>
是硬编码进去的,确定不能实装,咱们须要一种可以自动填充std::function<>
内模板参数的方法。在网上找了大半天后,终于有了一种基于模板的解决方案:
template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr> bool register_memeber_function(const std::string& sig, Ret(Struct::* func_ptr)(Args...) const, ObjectPtr obj_ptr) { std::function<Ret(Args...)> func = std::bind(func_ptr, obj_ptr); auto p_funciton_manager = FunctionManager<decltype(func)>::get_instance(); p_function_manager->register_function(sig, func); return true; } class Real { public: int add(int a, int b) { return a + b; } } Real real; bool b = register_member_function("ADD", &Real::add, &real);
将&Real::add
做为参数传入后可以自动推导出Ret, Struct
以及可变参数Args
;因为咱们会绑定对象指针,所以咱们只须要返回值Ret
,参数Args
做为std::function
的模板参数。这样一来std::function
的模板参数问题终于解决了。
然而如今的代码是没法经过编译的,由于std::bind
中没有添加正确数量的std::placeholder
。这个问题的解决须要用到一点元编程,StackOverflow上有人用自定义placeholder
以及std::make_integer_sequence
的方法来实现参数可变的std::bind
:
// https://stackoverflow.com/questions/26129933/bind-to-function-with-an-unknown-number-of-arguments-in-c template<int N> struct my_placeholder { static my_placeholder ph; }; template<int N> my_placeholder<N> my_placeholder<N>::ph; namespace std { template<int N> struct is_placeholder<::my_placeholder<N>> : std::integral_constant<int, N> { }; } template<class R, class T, class...Types, class U, int... indices> std::function<R (Types...)> bind_first(std::function<R (T, Types...)> f, U val, std::integer_sequence<int, indices...> /*seq*/) { return std::bind(f, val, my_placeholder<indices+1>::ph...); } template<class R, class T, class...Types, class U> std::function<R (Types...)> bind_first(std::function<R (T, Types...)> f, U val) { return bind_first(f, val, std::make_integer_sequence<int, sizeof...(Types)>()); }
这里的核心思想是在模板中传入长度与参数个数相同的整数序列,而且为每一个序列中的整数添加一个placeholder
。其实并不须要本身定义一个placeholder
,由于std::placeholder
的实现是相似的:
// PLACEHOLDER ARGUMENTS namespace placeholders { _INLINE_VAR constexpr _Ph<1> _1{}; _INLINE_VAR constexpr _Ph<2> _2{}; } // namespace placeholders
结合已有实现以及std::placeholder
的解决方案以下:
template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr, int... Indices> std::function<Ret(Args...)> erase_class_info(Ret(Struct::* func_ptr)(Args...), ObjectPtr obj_ptr, std::integer_sequence<int, Indices...>) { std::function<Ret(Args...)> erased_function = std::bind(func_ptr, obj_ptr, std::_Ph<Indices + 1>{}...); return erased_function; } template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr> bool register_memeber_function(const std::string& sig, Ret(Struct::* func_ptr)(Args...), ObjectPtr obj_ptr) { std::function<Ret(Args...)> erased_func = erase_class_info(func_ptr, obj_ptr, std::make_integer_sequence<int, sizeof...(Args)>()); auto p_funciton_manager = FunctionManager<decltype(erased_func)>::get_instance(); p_funciton_manager->register_function(sig, erased_func); return true; }
另外,成员函数添加const
之后的函数类型是不一样的,简单地添加两个相似接口能够解决这个问题:
template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr, int... Indices> std::function<Ret(Args...)> erase_class_info(Ret(Struct::* func_ptr)(Args...) const, ObjectPtr obj_ptr, std::integer_sequence<int, Indices...>) { std::function<Ret(Args...)> erased_function = std::bind(func_ptr, obj_ptr, std::_Ph<Indices + 1>{}...); return erased_function; } template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr> bool register_memeber_function(const std::string& sig, Ret(Struct::* func_ptr)(Args...) const, ObjectPtr obj_ptr) { std::function<Ret(Args...)> erased_func = erase_class_info(func_ptr, obj_ptr, std::make_integer_sequence<int, sizeof...(Args)>()); auto p_funciton_manager = FunctionManager<decltype(erased_func)>::get_instance(); p_funciton_manager->register_function(sig, erased_func); return true; }
如今已经支持成员函数的注册了:
class Real { public: int add(int a, int b) const { return a + b; } int sub(int a, int b) { return a - b; } }; Real real; bool b1 = register_memeber_function("real", &Real::add, &real); bool b2 = register_memeber_function("REAL", &Real::sub, &real); auto f1 = get_function<std::function<int(int, int)>>("real"); auto f2 = get_function<std::function<int(int, int)>>("REAL"); std::cout << f1(2, 1) << std::endl; std::cout << f2(2, 1) << std::endl;
至此FunctionManager
实现完成
前面提到了使用静态成员变量也可以实现注册功能,用静态成员变量注册函数
能够进一步减小用户须要写的代码,FunctionManager
但愿不须要用户提供变量名。参考boost宏BOOST_CLASS_EXPORT
,其利用模板类和静态成员变量实现了类的注册,而且不须要用户提供变量名,其原理以下:
namespace boost::archive::detail::extra_detail { template<> struct init_guid<ClassToRegister> { static guid_initializer<ClassToRegister> const& g; //静态成员g } static guid_initializer<ClassToRegister> const& g = register_function(); // 定义g,同时注册类 }
BOOST_CLASS_EXPORT
仍是不能知足需求,由于BOOST_CLASS_EXPORT
用于注册类,假若用于注册多个相同类型的函数会致使静态成员g重复定义,而实际注册函数时同一类型的函数每每会被注册屡次。如:
int add(int a, int b) { return a + b; } int mul(int a, int b) { return a * b; } _BOOST_LIKE_REGISTER("ADD", add); _BOOST_LIKE_REGISTER("MUL", mul); // 静态成员g重复定义
FunctionManager
经过命名空间解决重复定义这一问题,宏_EASY_REGISTER_FUNCTION
实现以下:
#define _EASY_REGISTER_FUNCTION(FunctionSig, FunctionPtr) \ namespace VicentChenSpace { \ namespace Dummy { \ namespace FunctionPtr { \ struct Dummy { \ static bool const& b; \ }; \ } \ } \ } \ bool const& VicentChenSpace::Dummy::FunctionPtr::Dummy::b = register_function<decltype(::FunctionPtr)>(FunctionSig, ::FunctionPtr); \
虽然_EASY_REGISTER_FUNCTION
使用起来更方便,但有如下问题:
c++17
支持#include <functional> #include <map> #include <iostream> #define _REGISTER_FUNCTION(VariableName, FunctionSig, FunctionPtr) \ bool Bool##VariableName = register_function<decltype(FunctionPtr)>(FunctionSig, FunctionPtr); 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; } // 也可使用insert,两者间存在一点区别 void register_function(const std::string& sig, FunctionType function) { m_sig_func_map[sig] = function; } // 实际上不能使用运算符[],由于当map中sig不存在时会自动建立一个<sig, empty>对象,demo中忽略这个问题 FunctionType get_function(const std::string& sig) { return m_sig_func_map[sig]; } }; template <typename FunctionPtr> bool 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); return true; } template <typename FunctionType> FunctionType get_function(const std::string& function_sig) { auto p_function_manager = FunctionManager<FunctionType>::get_instance(); return p_function_manager->get_function(function_sig); } template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr, int... Indices> std::function<Ret(Args...)> erase_class_info(Ret(Struct::* func_ptr)(Args...), ObjectPtr obj_ptr, std::integer_sequence<int, Indices...>) { std::function<Ret(Args...)> erased_function = std::bind(func_ptr, obj_ptr, std::_Ph<Indices + 1>{}...); return erased_function; } template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr> bool register_memeber_function(const std::string& sig, Ret(Struct::* func_ptr)(Args...), ObjectPtr obj_ptr) { std::function<Ret(Args...)> erased_func = erase_class_info(func_ptr, obj_ptr, std::make_integer_sequence<int, sizeof...(Args)>()); auto p_funciton_manager = FunctionManager<decltype(erased_func)>::get_instance(); p_funciton_manager->register_function(sig, erased_func); return true; } template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr, int... Indices> std::function<Ret(Args...)> erase_class_info(Ret(Struct::* func_ptr)(Args...) const, ObjectPtr obj_ptr, std::integer_sequence<int, Indices...>) { std::function<Ret(Args...)> erased_function = std::bind(func_ptr, obj_ptr, std::_Ph<Indices + 1>{}...); return erased_function; } template <typename Ret, typename Struct, typename ...Args, typename ObjectPtr> bool register_memeber_function(const std::string& sig, Ret(Struct::* func_ptr)(Args...) const, ObjectPtr obj_ptr) { std::function<Ret(Args...)> erased_func = erase_class_info(func_ptr, obj_ptr, std::make_integer_sequence<int, sizeof...(Args)>()); auto p_funciton_manager = FunctionManager<decltype(erased_func)>::get_instance(); p_funciton_manager->register_function(sig, erased_func); return true; } // ----- 普通函数注册 ----- // // 普通函数 int add(int a, int b) { return a + b; } _REGISTER_FUNCTION(ADD, "ADD", add); int mul(int a, int b) { return a * b; } _REGISTER_FUNCTION(MUL, "MUL", mul); // 命名空间内函数 namespace VicentSpace { int add(int a, int b) { return a + b; } } _REGISTER_FUNCTION(NAMESPACE_ADD, "NAMESPACE_ADD", VicentSpace::add); // 模板函数 template<typename T> T addT(T a, T b) { return a + b; } _REGISTER_FUNCTION(TEMPLATE_ADD, "TEMPLATE_ADD", addT<int>); // ----- 类内函数注册 ----- // class Real { public: int add(int a, int b) const { return a + b; } int sub(int a, int b) { return a - b; } static int mul(int a, int b) { return a * b; } }; Real real; template <typename T> class RealT { public: T addT(T a, T b) const { return a + b; } T subT(T a, T b) { return a - b; } static T mulT(T a, T b) { return a * b; } }; RealT<int> real_t; // 静态函数 _REGISTER_FUNCTION(STATIC_MUL, "STATIC_MUL", Real::mul); // 静态模板函数 _REGISTER_FUNCTION(STATIC_TEMPLATE_MUL, "STATIC_TEMPLATE_MUL", RealT<int>::mulT); // 成员函数 bool b1 = register_memeber_function("REAL_ADD", &Real::add, &real); bool b2 = register_memeber_function("REAL_SUB", &Real::sub, &real); // 模板成员 bool b3 = register_memeber_function("REALT_ADD", &RealT<int>::addT, &real_t); bool b4 = register_memeber_function("REALT_SUB", &RealT<int>::subT, &real_t); int main(int argc, char* argv[]) { // 普通函数 auto normal_add = get_function<std::function<decltype(add)>>("ADD"); auto normal_mul = get_function<std::function<decltype(add)>>("MUL"); std::cout << "Normal Add 1 + 2 = " << normal_add(1, 2) << std::endl; std::cout << "Normal Mul 1 * 2 = " << normal_mul(1, 2) << std::endl; // 命名空间内函数 auto namespace_add = get_function<std::function<decltype(VicentSpace::add)>>("NAMESPACE_ADD"); std::cout << "Namespace Add 1 + 2 = " << namespace_add(1, 2) << std::endl; // 模板函数 auto template_add = get_function<std::function<int(int, int)>>("TEMPLATE_ADD"); std::cout << "Template Add 1 + 2 = " << template_add(1, 2) << std::endl; // 静态函数 auto static_mul = get_function<std::function<int(int, int)>>("STATIC_MUL"); std::cout << "Static Mul 1 * 2 = " << static_mul(1, 2) << std::endl; // 静态模板函数 auto static_template_mul = get_function<std::function<int(int, int)>>("STATIC_TEMPLATE_MUL"); std::cout << "Static Template Mul 1 * 2 = " << static_template_mul(1, 2) << std::endl; // 成员函数 auto real_add = get_function<std::function<int(int, int)>>("REAL_ADD"); auto real_sub = get_function<std::function<int(int, int)>>("REAL_SUB"); std::cout << "Member Add 2 + 1 = " << real_add(2, 1) << std::endl; std::cout << "Const Member Sub 2 - 1 = " << real_sub(2, 1) << std::endl; // 模板成员 auto real_t_add = get_function<std::function<int(int, int)>>("REALT_ADD"); auto real_t_sub = get_function<std::function<int(int, int)>>("REAL_SUB"); std::cout << "Template Member Add 2 + 1 = " << real_t_add(2, 1) << std::endl; std::cout << "Template Const Member Sub 2 - 1 = " << real_t_sub(2, 1) << std::endl; return 0; }