模板参数推导问题

www_adintr_com 2007-03-27 01:03:06
#include <cstdio>
#include <boost/lexical_cast.hpp>

template <typename T, T f> struct WapperFunction;

template <typename RetType, RetType (*f)()>
struct WapperFunction<RetType(*)(), f>
{
static void call()
{
RetType x = f();

printf("Call result of 0X%p {%s} is %s {%s}\n\n", f, typeid(f).name(),
boost::lexical_cast<std::string>(x).c_str(), typeid(x).name());
}
};

template <typename RetType, typename Arg1Type, RetType (*f)(Arg1Type)>
struct WapperFunction<RetType(*)(Arg1Type), f>
{
static void call()
{
Arg1Type arg1;
RetType ret;

printf("Enter the argument1{%s}:", typeid(Arg1Type).name());
std::cin>>arg1;

ret = f(arg1);

printf("Call result of 0X%p {%s} is %s {%s}\n\n", f, typeid(f).name(),
boost::lexical_cast<std::string>(ret).c_str(), typeid(ret).name());
}
};


int f1()
{
return 0;
}

int f2(int x)
{
return x * x;
}

int main()
{
WapperFunction<int(*)(), f1>::call();
WapperFunction<int(*)(int), f2>::call();
};

运行效果如:

Call result of 0X00441361 {int (__cdecl*)(void)} is 0 {int}

Enter the argument1{int}:23
Call result of 0X00441906 {int (__cdecl*)(int)} is 529 {int}

现在的问题是 WapperFunction<int(*)(int), f2>::call(); 的调用中第一个模板参数 int(*)(int) 是可以从第二个参数 f2 中推导出来的。但应该如何使用才能让它进行推导? 使调用方式为:

WapperFunction<f1>::call();
WapperFunction<f2>::call();

或者有办法在运行期来生成全局函数的话,这种调用方式也可以:

WapperFunction::call(f1);
WapperFunction::call(f2);

注意生成的必须要是全局函数,不能是函数对象(需要和 C API 交互)。


...全文
1701 点赞 收藏 127
写回复
127 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
taodm 2007-04-02
你看的资料太旧了吧。
2参版本是PyObject* (PyObject* self, PyObject* args)
3参版本是PyObject* (PyObject* self, PyObject* args, PyObject* keywords),从python2.2才开始的。

回复
adintr 2007-03-30
你看 Python 的 API 文档,回调的函数必须是 PyObject* (PyObject* args, PyObject* keywords)

boost.python 知道 Python, 但是 Python 是不会知道 boost.python 的,所以 Python 根本不可能传递一个 boost 中的类指针给 boost 的。

所以这个 PyObject* func 是 boost 自己想办法弄出来的,不是 python 传过来的。
回复
taodm 2007-03-30
我在python/function.cpp里找到
static PyObject *
function_call(PyObject *func, PyObject *args, PyObject *kw)
{
PyObject* result = 0;
handle_exception(bind_return(result, static_cast<function*>(func), args, kw));
return result;
}
恐怕,不是你想象的PyObject* xxx(PyObject* args, PyObject* keywords)方式了。
这个方式应该是C程序内嵌python编译器代码时使用的方式。
而boost使用的是python程序调第三方库方式,应该用的前面的function_call方式。
回复
taodm 2007-03-30
代码显示得很清楚,它传过来的是PyObject *func,所以有static_cast<function*>(func),
struct BOOST_PYTHON_DECL py_function_impl_base
{
。。。
virtual PyObject* operator()(PyObject*, PyObject*) = 0;
。。。
};
这个声明也很清楚地表明了完成了如此signature的generalized包装,包装成了functor。
回复
adintr 2007-03-30
你看的这里还不是和 python 交互的部分,还在包装过程中。
你看 py_function.hpp 头文件,前面有段注释说明了:

// This type is used as a "generalized Python callback", wrapping the
// function signature:
//
// PyObject* (PyObject* args, PyObject* keywords)

python 调用函数的时候是不可能传递一个可以转换成 function* 指针的参数给你的。
回复
adintr 2007-03-30
PyCFunction
Type of the functions used to implement most Python callables in C. Functions of this type take two PyObject* parameters and return one such value. If the return value is NULL, an exception shall have been set. If not NULL, the return value is interpreted as the return value of the function as exposed in Python. The function must return a new reference.
回复
taodm 2007-03-30
回调的函数必须是 PyObject* (PyObject* self, PyObject* args, PyObject* keywords)
也就是前面的
PyObject * function_call(PyObject *func, PyObject *args, PyObject *kw)
回复
adintr 2007-03-29
另外,除了用宏外还有办法把 typeof(test), test 包装在一个东西内部么?
回复
adintr 2007-03-29
能给出一个在 VC 上实现 typeof 的来看看么?


lex/yacc 是完全的代码生成工具,是根据已有的伪代码来生成 c 代码,因为它放伪代码的地方和生成的代码是在彼此独立的文件中的,所以没有我上面说的一些维护上的困难(你的方法相当于是把伪代码和真是代码搅在一起的,所以维护困难)。即便如此,我觉得代码生成工具从来就没有真正实用过,曾经不是还有用 UML 生成代码的工具么。也没对软件开发行业带来什么实用价值。
回复
taodm 2007-03-29
在boost::spirit以前,所有使用lex/yacc功能的人必须先写.l/.y文件,然后用lex/yacc编译成.c/cpp,然后进行真正的编译,也完全没有有人有意见。
spirit则使用元编程方法,解除了对lex/yacc编译器的需要,不过代价就是语法和标准.l/.y实在相差太大了。
得失之间,我不知道spirit会不会被大规模使用。


《C++ Templates》8.3.3章详细讲述了模板的非类型参数的限制,必须外部存储属性的东西才可以成为模板参数。
回复
taodm 2007-03-29
当然,我也可以给你另外的解:typeof运算符
C++标准下一版将一定加入typeof运算符,gcc现在已经支持typeof运算符
VC7.1以上可以自己实现(需要预注册)的typeof。
template<typename t, t * f> struct T {};
void test(){}
int main(int argc, char *argv[])
{
T<typeof(test), test> t;
cout << sizeof(t);
system("PAUSE");
return EXIT_SUCCESS;
}
回复
adintr 2007-03-29
static int f1() { return 0; } 真的编译不过, 为什么啊? 静态函数怎么就不能在编译期求值了?


你提的两个方法答非所问, 我已经强调过我没有在解决脚本问题。
如果你是想说这个问题本身没什么意义,我觉得你可以把自己想象成为 boost.python 的作者。
另外,
1. 不是所有的代码你都可以改的。我还想改 CreateThread 让它支持函数对象呢,微软给代码让我改么,我改了在别人的机器上怎么运行。 当然 CreateThread 由于可以带一个 void* 参数,操作起来不存在这个问题。 只是证明一下,不是什么 C API 你都可以改的。
2. 你可能觉得写一个查找替换工具就可以解决问题,但是这对代码的维护是灾难。比如你把 Script_Export("Test", &TestFun); 替换成了对应的包装函数,现在 TestFun 的参数变了,你怎么办? 去修改替换后生成的代码? 而且别人最后阅读你的代码的时候看到的是替换后的代码, 读起来之恼火。除非你的工具能够在开始编译的时候去替换,编译结束之后把它替换回来,而且你要负责把这个工具部署到你项目中所有认的机子上,要不然别人编译不过。再次,假设你是 boost.python 的作者,你觉得你用这种工具替换的方法来完成的库会被使用者接受吗?
回复
taodm 2007-03-29
《C++ Templates》8.3.3章详细讲述了模板的非类型参数的限制。
由于实参匹配时,几乎所有转换都被禁止,所以试图省略你第一个参数,就必须连第二个参数都要放弃。
而放弃第二个参数,则只能引入静态变量来保存信息。

另外static int f1(){ return 0;}也将在你原来的方法上编译不过。

无论你在解决什么实际问题,还是应该优先考虑下面2个方法
1修改调用代码,让它支持functor
2自己编个小程序,搜索Script_Export("Test", &TestFun);这样的行并自动生成包装函数

回复
adintr 2007-03-29
我是楼主 adlay(www.adintr.com)(无可奈何天) , 由于回复不能超过 30 (BS CSDN), 故用这个号来继续~~

总结一下 taodm 的方案:

1. 它不能在 VC 下面编译,这是不可接受的。因为我的工作和学习平台是 VC.
2. 由于使用了静态变量,所以有一个 BUG. 然后用匿名名字空间和 __LINE__ 来试图隐藏或者说解决这个 BUG. (如果实在没有其它更好的办法,这还是可以勉强接受的)但其隐患是显而易见的:
这个隐患在我看到代码还没有运行的时候就感觉到了。虽然之后 taodm 用我原始版本也不能按这种方式使用来否定了我的测试,但这个隐患还是非常明显的, 看下面的代码:

int f1()
{
return 0;
}

int f2()
{
return 1;
}

int main()
{
void_f pf1 = CALL(f1), pf2 = CALL(f2);

pf1();
pf2();

system("pause");
};

或者下面的测试:
int f1()
{
return 0;
}

int f2()
{
return 1;
}

template <int (*f)()>
void_f wapperNoArg()
{
return CALL(f);
}

int main()
{
void_f pf1 = wapperNoArg<f1>();
void_f pf2 = wapperNoArg<f2>();

pf1();
pf2();

system("pause");
};

总之,我觉得这个试图隐藏使用静态变量 bug 的方法的隐患是非常明显的。

我希望的是,静态变量是导致这个问题的根源,就不能抛弃静态变量么?

回复
adintr 2007-03-29
boost.python 有 VC 的工程文件,路径是 boost_1_33_1\libs\python\build\VisualStudio
它编译之后得到 boost_python.dll 或者是 boost_python_debug.dll
它自己并不是 python 解释器,它编译的时候还需要 python 自己的头文件和 lib.
它只是把对 python 提供的 C API 包装成 C++ 类和对象。
回复
taodm 2007-03-29
boost的python好像必须用bjam来编译,最终得到了一个dll,一个pyd。它为什么需要由模板元编程来包装呢?
从代码看,模板元编程到创建functor并在一个全局表进行注册,就结束了。
回复
adintr 2007-03-29
好啊,
在接口上它当然应该是接受 functor
但它最终得把它包装成 PyObject* xxx(PyObject* args, PyObject* keywords) 这种方式才能被 python 接受。
回复
taodm 2007-03-29
这个讨论太远了。不如来讨论boost::python库吧。
我扫了一下,和你讨论的问题差很远,它使用了functor
回复
adintr 2007-03-29
Script_Export("Test", &CppFun) 不动的话你不能通过编译

在“宏”面前,这个太小意思了。

-------
难道你不是想通过宏让编译器忽略 Script_Export("Test", &CppFun) 来继续编译么?

你做个搜索代码进行替换后再编译的小工具么? 拿出来看看不就明白了,不用讨论这么多废话了。
回复
taodm 2007-03-29
你还是没尝试做过这样的小工具,所以你误解了。
使用宏的目的是不是让编译器忽略那段代码,是在不影响可读性及语意的情况下引入少少的额外处理,完成了函数名的变换,让程序员少敲了几个字母。
回复
加载更多回复
相关推荐
发帖
C++ 语言
创建于2007-09-28

6.0w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
申请成为版主
帖子事件
创建了帖子
2007-03-27 01:03
社区公告
暂无公告