1

There is a way in C to obtain a dynamic length argument list with va_list, as described here: http://www.cprogramming.com/tutorial/c/lesson17.html

That quite simple, but most times in C++ not needed. I am currently building a top level wrapper class for encapsulating some Zend functionality.

Anyway, I do need to make a call to such a dynamic function like printf from a normal function dynamically.

I mean the reverse way of the example described above, here is waht I got so war, maybe this explains my idea a little better:

void ZendParams::setRequired( int &Var  )
{
        // save every var pointer to a stack
        // and save the type with it. (if needed, does not seems to be)
        // after call to ZendParams::fetch()
        // the values are stored in the passed variables
        this->_params.push_back( (void*) &Var );
        this->_paramStr.append( 'i' );
}

size_t ZendParams::fetch()
{
        if ( zend_parse_parameters(
                ZEND_NUM_ARGS() TSRMLS_CC, ,
                this->_paramStr.c_str(),
                ...
                ) !== FAILURE)
        {

        }

        return 0;
}

So I want to make the call to zend_parse_parameters dynamically.

The base idea is to add pointer to vars in a list and pass them to the zend function (as reference) dynamically.

I thought there must be a way to do this with va_list , too.

But how?

To get it more simple, I am using this example:

list<int> myList;
myList.push_back(1);
myList.push_back(5);
myList.push_back(10);
myList.push_back(37);
myList.push_back(42);

double function avg( int num, ... )
    va_list arguments;                     
    int sum = 0;

    va_start ( arguments, num );           
    for ( int x = 0; x < num; x++ )        
    {
        sum += va_arg ( arguments, int ); 
    }
    va_end ( arguments );

    return sum / num;      
}

I want to call avg with all numbers I got in the list. I took the example function from the tutorial mentioned above, but it should show up what I mean in a very simple way. However, I can not change the function called, as it is part of the zend framework.

Is there any way in C or C++ to do this?


My 2nd approach:

template <typename... Arguments>
size_t Zend::getMethodParams( string paramStr, Arguments... Args )
{    
        if ( zend_parse_parameters(
                ZEND_NUM_ARGS() TSRMLS_CC, ,
                paramStr.c_str(),
                Args...
                ) == FAILURE)
        {
            return 0;
        }
}

To be called like this (to get the defined 3 Parameters):

string My1stParam;
int My2ndParam;
int My3rdParam;

Zend::getMethodParams<string, int, int>( "sii", &My1stParam, &My2ndParam, &My3rdParam );

I think that should work, but there is one hard issue with that: The zend_parse_parameters function returns 2 values for a string (c-style string!): - a char pointer and - the string length.

So I would either have to call it that way:

char* My1stParam_str;
int My1stParam_length
int My2ndParam;
int My3rdParam;

Zend::getMethodParams<string, int, int>( "sii", &My1stParam_str, &My1stParam_length, &My2ndParam, &My3rdParam );
string My1stParam;
My1stParam.assign(My1stParam_str, My1stParam_length);

Anyway, that was what I wanted to prevent.

Or I would have to modify the list of arguments passed to the zend_parse_parameters function to do these additional steps internally.

I am hoping to be able to call it at least in that way:

string My1stParam;
int My2ndParam;
int My3rdParam;

Zend::getMethodParams<string, int, int>( "sii", &My1stParam, &My2ndParam, &My3rdParam );

So say this clearly: The parameters are known at compile time, but the function call will be very different within all occurrences in the later sourcecode.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
  • yes, it is. Is there any way with this? – Sebastian Büttner Apr 13 '13 at 13:29
  • Variadic templates should help you – Andy Prowl Apr 13 '13 at 13:31
  • Currently looking up here: http://www.cplusplus.com/articles/EhvU7k9E/ I don't really get the point how to do a call to a foreign function with that... – Sebastian Büttner Apr 13 '13 at 13:34
  • So you want to be able to call `avg()` by providing all the elements of `myList`? – Andy Prowl Apr 13 '13 at 13:49
  • yes, thats what I want. But without modifying the avg function. – Sebastian Büttner Apr 13 '13 at 13:54
  • Not sure how to do that – Andy Prowl Apr 13 '13 at 14:01
  • me neither.. even after having read some articles about variadic templates. – Sebastian Büttner Apr 13 '13 at 14:04
  • The point is, although the number of parameters is not fixed, it must still be known at compile-time, while the size of a container is known only at run-time – Andy Prowl Apr 13 '13 at 14:34
  • oh, thats no problem I could variate with this, I update the post above with my 2nd approach – Sebastian Büttner Apr 13 '13 at 14:41
  • What does it mean "I could variate with this?" – Andy Prowl Apr 13 '13 at 14:42
  • I meant I am flexible in there. As you can see my initial post has been updated, I added a second approach with is slightly different. – Sebastian Büttner Apr 13 '13 at 14:47
  • I'm sorry, I still find it unclear. Maybe somebody better than me will be able to help :) – Andy Prowl Apr 13 '13 at 14:51
  • anyway, many, many thanks so far! :) – Sebastian Büttner Apr 13 '13 at 14:53
  • It is hard to tell what is your problem exactly. Do you want to call `zend_parse_parameters` without *statically* knowing the number of arguments you want to pass? This is not possible. No way in C or C++. no stdarg, no varargs, no variadic templates, nothing. – n. m. could be an AI Apr 13 '13 at 14:59
  • So I try to explain it more simple :D The params are known at compile time, but they are different from call to call. I want to use that function for every function I will write as a php extension. I also updated my 1st post to show it more cleary. Only the 2nd approach is now important :) – Sebastian Büttner Apr 13 '13 at 15:01
  • OK, so you want to *wrap* a call to `zend_parse_parameters` into your own function, would that be a correct description of your goal? – n. m. could be an AI Apr 13 '13 at 15:11
  • yes, exactly! thanks :D The reason for that is, that I want to convert in that function c-strings with length parameter to c++ string and so on... – Sebastian Büttner Apr 13 '13 at 15:13
  • Variadic functions are impossible to wrap in C or C++03. That's precisely why C has functions like `vprintf`. Zend API consistently fails to provide such functions. Blame PHP engineers for broken incomplete APIs. It is possible to wrap a variadic function in C++11 with variadic templates, like your second approach demonstrates. You don't even need to specify `` explicitly when calling your function, the compiler will do that automatically for you. `getMethodParams("sii", &My1stParam, &My2ndParam, &My3rdParam)` should work. – n. m. could be an AI Apr 13 '13 at 15:24
  • thanks for that hint, but how about my problem with that`the cstring -> c++ string conversion with the additional parameter passed (strlength) – Sebastian Büttner Apr 13 '13 at 15:27
  • See: [Calling a C function with a varargs argument dynamically](http://stackoverflow.com/questions/280940/calling-a-c-function-with-a-varargs-argument-dynamically). See also [libffi](http://sourceware.org/libffi/) and [dyncall](http://www.dyncall.org/). – Jonathan Leffler Apr 13 '13 at 15:37
  • If you want to convert say `std::string` arguments to c-string ones, with or without length, you have a bit more work to do, but it is still doable within the variadic templates framework. Would you mind asking a separate question about it? Say, something like "Converting a single std::string argument to a sequence of char* and int arguments with variadic templates". – n. m. could be an AI Apr 13 '13 at 15:38

1 Answers1

0

I found a way around this within the zend framework. I did indeed search for such a solution before, but it seems to be not very good documented ( n.m. already mentoined, that there is no zend internal way like a va_list taking function ).

But there is indeed one!

For everyone else stucking with this problem:

long arg;
zval ***args;
int i, argc = ZEND_NUM_ARGS( );

if (zend_parse_parameters(1 TSRMLS_CC, "l", &arg) == FAILURE) return;

array_init(return_value);
add_index_long(return_value, 0, arg);

if(argc>1) {
    args = (zval ***)emalloc(argc * sizeof(zval **));
    if(zend_get_parameters_array_ex(argc, args) == FAILURE) {
        efree(args);
        return;
    }
    for(i = 1; i < argc; i++) {
        zval_add_ref(args[i]);
        add_index_zval(return_value,i, *args[i]);
    }
    efree(args);
}

That is the solution ;)

This snippet, found on http://docstore.mik.ua/orelly/webprog/php/ch14_07.htm parses all parameters dynamically into the c representation of an PHP array.