Функция C Variadic в оболочке C ++ - PullRequest
3 голосов
/ 22 марта 2011

Я переписываю оболочку C вокруг API Python C (Python 1.5) и заметил, что функция Py_VaBuildValue использует переменное число аргументов. Я задавался вопросом, нужно ли мне использовать то же самое в моей оболочке функции C ++, чтобы перейти к этой функции, или есть ли более C ++ способ справиться с этим?

Я знаю, что вариадические функции могут быть причиной неисчислимых проблем, поэтому я бы предпочел не использовать их, если есть лучший способ.

EDIT:

Итак, вот код 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 внутри функции.

Ответы [ 2 ]

3 голосов
/ 22 марта 2011

Если вы хотите быть умным и не бояться какого-то тяжелого волшебства шаблонов, должна быть возможность генерировать (или массажировать) 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 голос
/ 22 марта 2011

Вы можете написать шаблонную функцию, которая проверит правильность параметров и не допустит сбоя вашей программы.

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 
...