[TOC]
time: 2017/08/16
问题
在cpp2lua的代码中,源代码有用到函数模版的特化,结果在特化参数为字符串指针(*const char**)的时候得到的是userdata类型,并不是我们想要的字符串类型。抛开lua来说,就是调用了并不是想要的那个函数,这个问题就是模版特化的问题。
部分代码如下:
// 定义1: 基础模版函数
template<typename T>
inline void PushToLua(lua_State *L, T data) {
CppToLua<T>::ConvertUserdata(L, data);
};
// 定义2:基础模版函数(类型指针)
template<typename T>
inline void PushToLua(lua_State *L, T* data) {
CppToLua<T>::ConvertUserdata(L, data);
};
// 定义3:特化的模版函数
template<>
inline void PushToLua(lua_State *L, const char* data) {
lua_pushstring(L, data);
}
// lua代码使用
PushToLua<R>(L, result);
结果:
// PushToLua传入的为字符串
print(test:GetStr())
// 打印结果,我想要的是GetStr返回的字符串,比如:"test"
userdata: 00921DD8
调用了基础模版函数,而不是我想要的特化的函数
// 想要调用的函数:定义3
inline void PushToLua(lua_State *L, const char* data)
// 实际上调用的函数:定义1
inline void PushToLua(lua_State *L, T data)
解惑
基础知识
先了解一些基础知识
- 函数模版的全特化有两种写法[2]
template < >
int compare<const char*>(const char* left, const char* right)
{
std::cout <<"in special template< >..." <<std::endl;
return strcmp(left, right);
}
也可以
template < >
int compare(const char* left, const char* right)
{
std::cout <<"in special template< >..." <<std::endl;
return strcmp(left, right);
}
- 重载和特化
(1)函数模版只有全特化,没有偏特化,可以理解为重载就可以解决偏特化的需求;
(2)函数模版的基础模版参数是需要传入类型的:template<class T>;
(3)函数模版的特化版本是空的参数类型:template<>.
- 函数调用规则:
(1)优先找非模版函数;
(2)如果没有非模版函数,先找基础模版;
(3)确定了基础模版之后,再看有没有该基础模版的特化版本.
- 小结
通过以上的函数调用规则,我们可以知道为什么没有调用定义3,而是调用了定义1
(1)定义1和定义2是两个基础模版,所以优先在这两个基础模版里面选择(因为没有非模版函数);
(2)特化的版本是其中一个基础模版的特化版本;
(3)在调试的时候确定如下:函数再调用的时候,选择了定义1的基础模版调用。同时也说明了,定义3是定义2的特化版本,而不是定义1的,都是指针类型,所以还是比较好理解一点;
(4)那么另外一个问题来了,为什么是调用的是定义1的基础模版而不是定义2的基础模版?
另外一个问题
- 测试
如果我把使用的时候约定参数类型去掉,修改如下:
// 使用
PushToLua(L, result);
这个时候,是能够找到特化模版
template<>
inline void PushToLua(lua_State *L, const char* data) {
lua_pushstring(L, data);
}
也就是说,这个时候输出的是字符串,而不是userdata。
同样的现象,如果增加一个非模版函数,也是对的:
inline void PushToLua(lua_State *L, const char* data) {
lua_pushstring(L, data);
}
结果为:
(1)PushToLua
(2)如果PushToLua(L, result);调用方式,是优先执行非模版函数的.
- 猜想
(1)PushToLua
(2)PushToLua(L, result)的调用方式,是按照上面的调用规则走的,也就是传递的R是一个const char*,会匹配到定义2的基础模版,从而发现定义2的基础模版有特化版本,就找到特化版本调用;
(3)这一点可以从int类型的调用看出来,即使是 PushToLua(L, member); 调用一个整形的版本,结果就是一个整形,而不是一个userdata。
解决
- 1.加一个非模版函数,参数就是字符串指针类型
// 定义4:非模版函数
inline void PushToLua(lua_State *L, const char* data) {
lua_pushstring(L, data);
}
- 2.使用的时候改成如下方式:
// 修改前
PushToLua<R>(L, result);
// 修改后
PushToLua(L, result);
总结
- 记住函数模版的调用规则,小心坑
- PushToLua
使用方式比较特殊,最好还是和正常的函数调用相一致,免生端倪! - 关于猜想部分,还没有去研究和确认~