Thread: Variadic Functions

  1. #1
    Registered User
    Join Date
    Nov 2006
    Posts
    65

    Variadic Functions

    Hello All,

    I'd like to call a function with this prototype:
    Code:
    int make_sth(char *text, ...);
    /* expects a char * + a variable number of ints as args */
    with an array of ints. Kind of like this:
    Code:
    int num_of_ints;
    int *some_ints;
    
    ..... /* set some_ints to point to num_of_ints ints */
    
    switch(num_of_ints) {
    case 2:
      make_sth("a string", some_ints[0], some_ints[1]);
      break;
    case 3:
      make_sth("a string", some_ints[0], some_ints[1], some_ints[2]);
    ............................
    How can this be done without a switch, using some kind of loop?

  2. #2
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  3. #3
    Registered User
    Join Date
    May 2006
    Posts
    903
    In fact, va_list 's are to be used mostly if you can't know the type of the variables being passed. Passing an array or a pointer is perfectly fine in this case as you already know it's going to be int's only.

    I would do something like this: (note that I program mainly in C++ and so my C is rusty)
    Code:
    char* make_str(const char* text, int* array, int size)
    {
      int total_digits = 0;
      for(int i = 0; i < size; i++)
      {
        int temp = array[i];
        do
        {
          temp /= 10;
          total_digits++;
        } while(temp > 0);
      }
    
      char* str = malloc(sizeof(char) * (strlen(text) + 1));
    
      char buffer[255] = {0};
      for(int j = 0; j < size; j++)
      {
        itoa(array[j], buffer, 10);
        strcat(str, buffer);
      }
    
      return str;
    }
    This may be completely out but I'd try something like that. I'd consider that I don't know much about C functions and that there are maybe way easier solutions.. This might not even compile at all or crash or whatever. Just don't hold what I say for true. =P

  4. #4
    Registered User
    Join Date
    Nov 2006
    Posts
    65
    A start?
    Seems like my question was ambigious. Thanks for the example Desolation; I should have made the question clearer. The function make_sth() exists (it's already finished); it has the prototype as above and it is coded to expect a variable number of ints if called as I have done above in the switch example.

    The problem is that I want to call the function with a single array of ints (or an int pointer). However, the function wants plain old ints. Hence, the need to convert the array into individual elements, as I did with the switch statement.

    What I am looking for is code equivalent to the switch statement above, but which uses a loop. (num_of_ints can be > 70.)

  5. #5
    Registered User
    Join Date
    May 2006
    Posts
    903
    Code:
    void make_sth(char* text, int* array, int size)
    {
      for(int i = 0; i < size; i++)
        make_sth(text, array[i]);
    }
    It's a bit hard to tell since I don't really know exactly what your function does. Does it allocate memory ? What does it return ? ..
    Last edited by Desolation; 01-12-2007 at 12:46 AM.

  6. #6
    Registered User
    Join Date
    Nov 2006
    Posts
    65
    Thanks for the help Desolation, but the function checks the number of arguments; all arguments must be passed at once.
    It's a bit hard to tell since I don't really know exactly what your function does.
    I won't post the whole function, because it's long, but I'll post some usage examples. Note: The actual function prototype is very slightly different to what I posted above. (I left the initial arg out, because it didn't really change the question.)
    Code:
    /*
       FUNCTION: eDBi_make_sth
       PURPOSE:  prepare and store a statement handle (sth) for later execution
       INPUT:    st_num - desired identification number for statement handle,
                          must be < NOS_OF_STHs. Used to refer to sth later,
                          and from Perl.
                 query  - null terminated query string (passed to MySQL)
                 ...    - For each placeholder ("?") in query:
                            <> the type, as given in the enum enum_field_types
                            <> an integer which represents:
                                  a) for string types:  the length of the buffer needed to
                                                        store the largest value expected
                                  OR
                                  b) for integer types: whether the integer is unsigned
                          The last argument must be -1.
        OUTPUT:   0 - success
                 >0 - Failure. Returns an error code. If return value is > 10000, the
                      MySQL error code is also given as OUTPUT % 10000 (our error code
                      then equals (int)(OUTPUT / 10000))
    
    */
    int eDBi_make_sth(unsigned int st_num, const char *query, ...);
    /* USAGE example */
    if(eDBi_make_sth(1, "SELECT user_id, last_name FROM users WHERE age > ? AND first_name = ?",
                                 MYSQL_TYPE_LONG, 1,
                                 MYSQL_TYPE_VAR_STRING, 100,
                                 -1))
    {
    /* handle error */
    }
    I wrote the function this way, because I like to call it as I did above (from C). The -1 is only there to ensure that the function can detect if insufficient arguments are passed in to describe all the placeholders.

    Usually, I call the function from C, which works well. But I'd like to be able to call it from Perl also. In order to do this, I need to be able to dynamically create the function call, like the switch statement in my first post did. However, there may be many placeholders in a query, so I'd prefer another method.

  7. #7
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Variadic functions work with the assumption that the caller can determine, at compile time, how many arguments it needs to pass. You are trying to determine the number of arguments to pass at run time.

    I could be barking up the wrong tree, because I'm not familiar with your function. But ........ I gather from your synopsis of the function that the second argument of your function is a string that is essentially a prototype for an SQL query, where later arguments occur in pairs (first of the pair identifying the type of the value, the second of the pair is the value) and the values are substituted one at a time for question marks in the string. Which means that, when calling the function, you have to set up a prototype for the string before calling the function. Why not just change the process of generating the string so the array of ints are written to it BEFORE calling your function? Then you don't have to bother passing all of those ints to the function?

  8. #8
    Registered User
    Join Date
    Nov 2006
    Posts
    65
    It doesn't work quite like that. The 2nd argument per placeholder is not the value. (If the placeholder is of string type, this represents the length of the buffer that should be allocated. If the placeholder is of int type, it represents whether the int is unsigned.)

    I don't actually parse the string at all; it's only sent to MySQL. After sending the string, the function checks with MySQL how many placeholders the query has, which is only used to ensure that the function was called with the right number of placeholder descriptors. The function then goes through the variable argument list, and sets up buffer structures, depending on the types, etc.

    Later on, there's an execution function, which copies actual data into the buffers we allocated during make_sth(). The question marks are never replaced. Data is sent separetly. (Note that if I was using plain queries, rather than prepared statements, then the question marks would simply be replaced.)

    It seems like I'll have to change the function to make this work, although I'd rather hack the stack.

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Perhaps this will help with the calling from perl.
    http://www.swig.org/
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    Registered User
    Join Date
    Nov 2006
    Posts
    65
    Thanks Salem, I'll check it out.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Void Functions Help
    By bethanne41 in forum C++ Programming
    Replies: 1
    Last Post: 05-09-2005, 05:30 PM
  2. Functions and Classes - What did I do wrong?
    By redmage in forum C++ Programming
    Replies: 5
    Last Post: 04-11-2005, 11:50 AM
  3. calling functions within functions
    By edd1986 in forum C Programming
    Replies: 3
    Last Post: 03-29-2005, 03:35 AM
  4. Factory Functions HOWTO
    By GuardianDevil in forum Windows Programming
    Replies: 1
    Last Post: 05-01-2004, 01:41 PM
  5. Shell functions on Win XP
    By geek@02 in forum Windows Programming
    Replies: 6
    Last Post: 04-19-2004, 05:39 AM