C Variadic function in a C++ wrapper
I am rewriting a C wrapper around a C Python API (Python 1.5) and I noticed that the function Py_VaBuildValue uses variadic number of args. I wondered if I have to use the same in my C++ function wrapper to pass to this function or if there is a more C++ way to deal with this?
I know variadic functions can be the cause of untold trouble, so I'd rather avoid having to use if there is a better way.
EDIT:
So here is the C code I need to make into a C++ function:
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
}
So I'm making the same function with std::string instead of char* and I want to change the ellipsis to something more c++ like, that 开发者_运维问答I can however then pass to Py_VaBuildValue inside the function.
If you want to be clever and don't fear some heavy template wizardry, it should be possible to generate (or massage) valfmt
to always match the types you want to pass (I am assuming it uses format specifiers similar to printf, but the technique is applicable to any kind of format specification). You could do something like:
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.
This is the simple way where each value is formatted the default way and combined together. If you want something more complex, you can create a function, that will get valfmt
string and correct format specifiers (obtained from the trait) and will fix up the format string to match.
You may write template function which will check the correctness of params, and won't allow your program to crash.
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
精彩评论