我正在围绕C Python API(Python 1.5)重写一个C包装器,我注意到函数Py_VaBuildValue使用了可变数量的args。我想知道我是否必须在我的C ++函数包装器中使用相同的函数来传递给这个函数,或者是否有更多的C ++方法来处理它?</ p>
我知道变量函数可能是造成无法解决的问题的原因,所以如果有更好的方法,我宁愿避免使用它。
编辑:
所以这是我需要在C ++函数中创建的C代码:
int Set_Global(char *modname, char *varname, char *valfmt, ... /* cval(s) */) {
int result;
PyObject *module, *val; // "modname.varname = val"
va_list cvals;
va_start(cvals, valfmt); // C args after valfmt
module = Load_Module(modname); // get/load module
if (module == NULL)
return -1;
val = Py_VaBuildValue(valfmt, cvals); // convert input to Python
va_end(cvals);
if (val == NULL)
return -1;
result = PyObject_SetAttrString(module, varname, val);
Py_DECREF(val); // set global module var
return result; // decref val: var owns it
}
所以我使用std :: string而不是char *创建相同的函数,我想将省略号更改为更像c ++的东西,然后我可以在函数内部传递给Py_VaBuildValue。
答案 0 :(得分:3)
如果你想要聪明并且不要害怕一些沉重的模板魔法,应该可以生成(或按摩)valfmt
以始终匹配你想要传递的类型(我假设它使用格式说明符类似于printf,但该技术适用于任何类型的格式规范)。你可以这样做:
template <typename T> struct format_trait;
template <> struct format_trait<int> { static const char * format() { return "%i"; }};
template <> struct format_trait<unsigned> { static const char * format() { return "%u"; }};
... // and so on for each type you want to use
template <typename Arg1>
int Set_Global(const std::string &modname, const std::string &varname, const Arg1 &arg1)
{
return ::Set_Global(modname.c_str(), varname.c_str(), format_trait<Arg1>::format(), arg1);
}
template <typename Arg1, typename Arg2>
int Set_Global(const std::string &modname, const std::string &varname, const Arg1 &arg1, const Arg2 &arg2)
{
return ::Set_Global(modname.c_str(), varname.c_str(),
std::string(format_trait<Arg1>::format()) + format_trait<Arg2>::format(),
arg1, arg2);
}
... // Repeat up to number of argument you reasonably expect to need or use C++0x variadic templates.
这是一种简单的方法,其中每个值都以默认方式格式化并组合在一起。如果你想要更复杂的东西,你可以创建一个函数,它将获得valfmt
字符串和正确的格式说明符(从特征中获得),并修复格式字符串以匹配。
答案 1 :(得分:1)
您可以编写模板函数来检查参数的正确性,并且不会让您的程序崩溃。
bool BuildValueCheck(const char * s, int v)
{
if( s[0] == 'i' )
return true;
return false;
}
bool BuildValueCheck(const char * s, float v)
{
if( s[0] == 'f' )
return true;
return false;
}
bool BuildValueCheck(const char * s, char * v)
{
if( s[0] == 's' || s[0] == 'z' )
return true;
return false;
}
// and so on for each other type
template<typename t1>
PyObject *BuildValue(char * format, t1 v1)
{
char * s = strchr(format, "ifsz...."); // Skip here all "()[]" etc
if( !s )
return NULL; // and print an error
if(!BuildValueCheck(s, v1))
return NULL; // and also print an error
return Py_BuildValue(format, v1);
}
template<typename t1, typename t2>
PyObject *BuildValue(char * format, t1 v1, t2 v2)
{
// Skip here all "()[]" etc
char * s = strchr(format, "ifsz....");
if( !s )
return NULL;
if(!BuildValueCheck(s, v1))
return NULL;
s = strchr(s+1, "ifsz....");
if( !s )
return NULL;
if(!BuildValueCheck(s, v2))
return NULL;
return Py_BuildValue(format, v1, v2);
}
// and so on for 3,4,5 params - I doubt your program uses more
// and then replace all Py_BuildValue with BuildValue across the code, or make a #define