Variadic function can have type safety.

N

Nephi Immortal

The compiler usually cannot check whether the unnamed arguments passed are of the type the function expects, or convert them to the required type. Therefore, care should be taken to ensure correctness in this regard, since undefined behavior results if the types do not match.

For example, printf( “”, … ) is a variadic function. You can rewriteit in your own variadic function to implement type safety and type checking when possible. The class type is good choice unless you want type checking. Also, you can insert assertion if you want type safety.

What happen if you use va_list? The va_list is truly char* type. If you put string into va_list, then va_list is invalid because of wrong type and does not have type checking.

There are many ways I can overcome the limitation of variadic function. You can put unnamed class type into printf function as long as printf can have more than 8 overloading functions. When you read my code, it looks very reasonable readability.

Take a look at my code below. I borrowed sample code from Microsoft library. I made some modifications and I added my class type. Please comment what do you think.

// variable_argument_lists.cpp
//#include <stdio.h>
//#include <stdarg.h>

#include<iostream>
#include<string>
#include<stack>
#include <cassert>
#include <tchar.h>

using namespace std;

class User_Type
{
public:
enum Built_In_Types
{
eInt_Type,
eFloat_Type,
eChar_Type,
eString_Type
};

protected:
union Types
{
int i;
float f;
char c;
char *s;
} types;

public:
User_Type()
{
}

~User_Type()
{
}

virtual Built_In_Types get_type() = 0;
virtual Types get_value() = 0;
};

template< typename T >
class Derived_User_Type : public User_Type
{
public:
explicit Derived_User_Type( T data );
~Derived_User_Type();

virtual Built_In_Types get_type();
virtual Types get_value();
};

template<>
class Derived_User_Type< int > : public User_Type
{
public:
explicit Derived_User_Type( int data )
{
types.i = data;
}

~Derived_User_Type()
{
}

virtual Built_In_Types get_type()
{
return eInt_Type;
}

virtual Types get_value()
{
return types;
}
};

template<>
class Derived_User_Type< float > : public User_Type
{
public:
explicit Derived_User_Type( float data )
{
types.f = data;
}

~Derived_User_Type()
{
}

virtual Built_In_Types get_type()
{
return eFloat_Type;
}

virtual Types get_value()
{
return types;
}
};

template<>
class Derived_User_Type< char > : public User_Type
{
public:
explicit Derived_User_Type( char data )
{
types.c = data;
}

~Derived_User_Type()
{
}

virtual Built_In_Types get_type()
{
return eChar_Type;
}

virtual Types get_value()
{
return types;
}
};

template<>
class Derived_User_Type< char* > : public User_Type
{
public:
explicit Derived_User_Type( char* data )
{
types.s = data;
}

~Derived_User_Type()
{
}

virtual Built_In_Types get_type()
{
return eString_Type;
}

virtual Types get_value()
{
return types;
}
};

typedef Derived_User_Type< int > Int_Type;
typedef Derived_User_Type< float > Float_Type;
typedef Derived_User_Type< char > Char_Type;
typedef Derived_User_Type< char* > String_Type;

class Argument_List
{
public:
Argument_List()
{
}

~Argument_List()
{
}

User_Type& get_parameter()
{
return *list.top();
}

void push( User_Type& list )
{
this->list.push( &list );
}

void pop()
{
list.pop();
}

int get_size()
{
return list.size();
}

private:
stack< User_Type* > list;
};

void vprint( string str, Argument_List& list );

void print( string str, User_Type& arg1 )
{
Argument_List list;
list.push( arg1 );
vprint( str, list );
}

void print( string str, User_Type& arg1, User_Type& arg2 )
{
Argument_List list;
list.push( arg2 );
list.push( arg1 );
vprint( str, list );
}

void print( string str, User_Type& arg1, User_Type& arg2, User_Type& arg3 )
{
Argument_List list;
list.push( arg3 );
list.push( arg2 );
list.push( arg1 );
vprint( str, list );
}

void print( string str, User_Type& arg1, User_Type& arg2, User_Type& arg3, User_Type& arg4 )
{
Argument_List list;
list.push( arg4 );
list.push( arg3 );
list.push( arg2 );
list.push( arg1 );
vprint( str, list );
}


// ShowVar takes a format string of the form
// "ifcs", where each character specifies the
// type of the argument in that position.
//
// i = int
// f = float
// c = char
// s = string (char *)
//
// Following the format specification is a variable
// list of arguments. Each argument corresponds to
// a format character in the format string to which
// the szTypes parameter points

//void ShowVar( char *szTypes, ... )
void vprint( string szTypes, Argument_List& list )
{
// va_list vl;
// int i;

// szTypes is the last argument specified; you must access
// all others using the variable-argument macros.
// va_start( vl, szTypes );

// Step through the list.
string::iterator iter = szTypes.begin();

// for( i = 0; szTypes != '\0'; ++i )
for( ; iter != szTypes.end(); ++iter )
{
union Printable_t
{
int i;
float f;
char c;
char *s;
} Printable;

// Type to expect.
// switch( szTypes )
switch( *iter )
{
case 'i':
_ASSERT_EXPR( User_Type::eInt_Type == list.get_parameter().get_type(), _TEXT("This current type is NOT int type.") );
// Printable.i = va_arg( vl, int );
Printable.i = list.get_parameter().get_value().i;
list.pop();
// printf_s( "%i\n", Printable.i );
cout << Printable.i << endl;
break;

case 'f':
_ASSERT_EXPR( User_Type::eFloat_Type == list.get_parameter().get_type(), _TEXT("This current type is NOT float type.") );
// Printable.f = va_arg( vl, double );
Printable.f = list.get_parameter().get_value().f;
list.pop();
// printf_s( "%f\n", Printable.f );
cout << Printable.f << endl;
break;

case 'c':
_ASSERT_EXPR( User_Type::eChar_Type == list.get_parameter().get_type(), _TEXT("This current type is NOT char type.") );
// Printable.c = va_arg( vl, char );
Printable.c = list.get_parameter().get_value().c;
list.pop();
// printf_s( "%c\n", Printable.c );
cout << Printable.c << endl;
break;

case 's':
_ASSERT_EXPR( User_Type::eString_Type == list.get_parameter().get_type(), _TEXT("This current type is NOT string type.") );
// Printable.s = va_arg( vl, char * );
Printable.s = list.get_parameter().get_value().s;
list.pop();
// printf_s( "%s\n", Printable.s );
cout << Printable.s << endl;
break;

default:
break;
}
}
// va_end( vl );
}

int main()
{
// ShowVar( "fcsi", 32.4f, 'a', "Test string", 4 );
print( "fcsi", Float_Type( 32.4f ), Char_Type( 'a' ), String_Type( "Test string" ), Int_Type( 4 ));

return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,955
Messages
2,570,117
Members
46,705
Latest member
v_darius

Latest Threads

Top