nikhil
nikhil

Reputation: 9363

variable arguments in c++ with different datatypes

please read through the following code

#define  INT    0
#define  CHAR   1
#define  FLOAT  2
#define  DOUBLE 3

int createKey(int keySize,vector<int> &types, ...){ 
    va_list varlist;
    va_start(varlist,types.size());

    int intKey;
    float floatKey;
    char charKey;
    double doubleKey;

    char *key = (char*)malloc(keySize);
    int offset = 0;

    for(int i=0;i<types.size();i++){
        switch(types[i]){
            case INT:
                intKey = va_arg(varlist,int);
                memcpy(&key[offset],&intKey,sizeof(int));
                offset += sizeof(int);
            break;

            case CHAR:
                charKey = va_arg(varlist,char);
                memcpy(&key[offset],&charKey,sizeof(char));
                offset += sizeof(char);
            break;

            case FLOAT:
                floatKey = va_arg(varlist,float);
                memcpy(&key[offset],&floatKey,sizeof(float));
                offset += sizeof(float);
            break;

            case DOUBLE:
                doubleKey = va_arg(varlist,double);
                memcpy(&key[offset],&doubleKey,sizeof(double));
                offset += sizeof(double);
            break;

        }
        va_end(varlist);
    }

    int testIntKey;
    char testCharKey;
    float testFloatKey;
    double testDoubleKey;

    offset = 0;

    for(int i=0;i<types.size();i++) {
        switch(types[i]){
            case INT:
                memcpy(&testIntKey,&key[offset],sizeof(int));
                cout<<testIntKey<<endl;
                offset += sizeof(int);
            break;

            case CHAR:
                memcpy(&testCharKey,&key[offset],sizeof(char));
                cout<<testCharKey<<endl;
                offset += sizeof(char);

            break;

            case FLOAT:
                memcpy(&testFloatKey,&key[offset],sizeof(float));
                cout<<testFloatKey<<endl;
                offset += sizeof(float);
            break;

            case DOUBLE:
                memcpy(&testDoubleKey,&key[offset],sizeof(double));
                cout<<testDoubleKey<<endl;
                offset += sizeof(double);
            break;
        }
    }

}

In the above code, I am trying to create a key that is a combination of one or more of the datatypes(int,char,float,double)...I used ellipses as i donot know the number of arguments that may be passed to createKey(). Now the above code when compiled shows the following warnings..

varargsTest.cpp: In function ‘int createKey(int, std::vector<int, std::allocator<int> >&, ...)’:
varargsTest.cpp:20: warning: second parameter of ‘va_start’ not last named argument
varargsTest.cpp:39: warning: ‘char’ is promoted to ‘int’ when passed through ‘...’
varargsTest.cpp:39: note: (so you should pass ‘int’ not ‘char’ to ‘va_arg’)
varargsTest.cpp:39: note: if this code is reached, the program will abort
varargsTest.cpp:45: warning: ‘float’ is promoted to ‘double’ when passed through ‘...’
varargsTest.cpp:45: note: if this code is reached, the program will abort

and when i run the program with the following ..

int main()
{
    vector<int> types;

    types.push_back(INT);
    types.push_back(CHAR);
    types.push_back(INT);

    createKey(9,types,85,'s',97);

}

I get Illegal instruction.

How could this problem be solved...Is this the right approach to handle these kind of problems?

Upvotes: 1

Views: 5002

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753695

When you call a function with a variable argument list (which is generally a bad idea in C++), then any float expressions are automatically promoted (converted) to double, and any char (any of the three flavours) and short (two flavours) are promoted to int. Therefore, as the error message says, you cannot expect va_arg() to collect a float or a char type; you need to collect a double or int and coerce the result if necessary.

In general, C++ programmers look unfavourably on variable argument list functions because they are inherently not type-safe and C++ invests a lot of effort into being type-safe.

The 1998 C++ standard essentially incorporates the (1989) C standard specification for <stdarg.h> verbatim. The 1999 C standard says:

§7.15.1.1 The va_arg macro

Synopsis

#include <stdarg.h>
type va_arg(va_list ap, type);

Description

The va_arg macro expands to an expression that has the specified type and the value of the next argument in the call. The parameter ap shall have been initialized by the va_start or va_copy macro (without an intervening invocation of the va_end212) macro for the same ap). Each invocation of the va_arg macro modifies ap so that the values of successive arguments are returned in turn. The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type. If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:

— one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;

— one type is pointer to void and the other is a pointer to a character type.

Returns

The first invocation of the va_arg macro after that of the va_start macro returns the value of the argument after that specified by parmN. Successive invocations return the values of the remaining arguments in succession.

212) It is permitted to create a pointer to a va_list and pass that pointer to another function, in which case the original function may make further use of the original list after the other function returns.

The C89 standard is similar but does not support/define va_copy.

Upvotes: 4

Related Questions