How to solve Variadic Arguments issue (using C): Difference between revisions

From Try-AS/400
Jump to navigation Jump to search
Line 5: Line 5:


== The Issue ==
== The Issue ==
I<ref>[[User:Heiko]]</ref> have some code that
I<ref>[[User:Heiko]]</ref> have some code that I have been using for years in macOS and Linux projects. It's purpose is to log messages with the source's filename, line number and some custom message.
 
This how it's used in the code:
 
 
This is how the define adds the filename and linenumber:
 
 
This is the actual logging code (you can skip that, it's not relevant, except for the parameters):
 
void logWithMethodName(const char* methodName, int lineNumber, int logLevel, char *format, ...)
{
if (gLogLevel>=logLevel)
{
/* Variables */
char msg1[kMaxMsgLen];
char msg2[kMaxMsgLen];
char lMethodName[kMaxMethodLen];
/* Get the arguments: https://jameshfisher.com/2016/11/23/c-varargs/ & https://unixpapa.com/incnote/variadic.html (!) & https://github.com/mpaland/printf/blob/master/printf.c (!) & https://stackoverflow.com/questions/21540778/pass-varargs-to-printf (!) */
va_list argp;
va_start(argp, format); // format being the last argument before '...'
/* Combine format and arguments! */
#ifdef USEOWNIMPLEMENTATIONOFVSPRINTF
memset(msg2, '\0', kMaxMsgLen);
int msg2i=0;
char tmp[kMaxMsgLen];
char *p=NULL; //format;
int ci;
for (ci=0; ci<strlen(format) && msg2i<kMaxMsgLen-1; ci++)
{
/* This works only for SOME formats, like %s or %d (NO PRECISION!)! Everything unknown will be treated as %d! */
char c=format[ci];
if (c=='%')
{
/* Uh! It's a parameter-dingens */
char t=format[ci+1]; // get its type. NO SUPPORT FOR PRECISION YET!
if (t=='s')
{ /* It's a string */
sprintf(tmp, "%s", va_arg(argp, char*));
strcat(msg2, tmp);
msg2i=strlen(msg2);
ci++; // omit the "s"
}
else
{ /* It's something else. Hopefully some kind of number */
sprintf(tmp, "%d", va_arg(argp, int));
strcat(msg2, tmp);
msg2i=strlen(msg2);
ci++; // omit the "s"
}
}
else
{
/* Add everything else to the buffer */
msg2[msg2i]=c;
msg2i++;
}
}
#else
vsprintf(msg2, format, argp);
#endif
if (strlen(methodName) > kMaxMethodLen)
{
memcpy(lMethodName, methodName, kMaxMethodLen-1);
lMethodName[kMaxMethodLen]='\0';
}
else
{
strcpy(lMethodName, methodName);
}
if (strlen(format) > kMaxFormatLen) format[kMaxFormatLen]='\0';
sprintf(msg1, "%s (%d): ", lMethodName, lineNumber);
strcat(msg1, msg2);
/* Output to stdout */
if (1)
{
printf(msg1);
}
/* Write to syslog */
if (0)
{
//logger(msg1);
}
va_end (argp);
}
return;
}


== The Solution ==
== The Solution ==

Revision as of 10:38, 28 November 2019

First of all, what are Variadic Arguments (VA)? It's a way to NOT define the number of arguments a function (or method) takes. You only define the args you definitely going to be given on every call. All additional arguments are then optional, but can be used if given.

While porting C-code from Linux to AS/400 I ran into a problem. This article is going to explain the issue and the solution.

The Issue

I[1] have some code that I have been using for years in macOS and Linux projects. It's purpose is to log messages with the source's filename, line number and some custom message.

This how it's used in the code:


This is how the define adds the filename and linenumber:


This is the actual logging code (you can skip that, it's not relevant, except for the parameters):

void	logWithMethodName(const char* methodName, int lineNumber, int logLevel, char *format, ...)
{
		if (gLogLevel>=logLevel)
		{
			/* Variables */
			char msg1[kMaxMsgLen];
			char msg2[kMaxMsgLen];
			char lMethodName[kMaxMethodLen];
			
			/* Get the arguments: https://jameshfisher.com/2016/11/23/c-varargs/ & https://unixpapa.com/incnote/variadic.html (!) & https://github.com/mpaland/printf/blob/master/printf.c (!) & https://stackoverflow.com/questions/21540778/pass-varargs-to-printf (!) */
			va_list argp;
			va_start(argp, format); // format being the last argument before '...'

			/* Combine format and arguments! */
#ifdef USEOWNIMPLEMENTATIONOFVSPRINTF
			memset(msg2, '\0', kMaxMsgLen);
			int msg2i=0;
			char tmp[kMaxMsgLen];
			char *p=NULL; //format;
			int	ci;
			for (ci=0; ci<strlen(format) && msg2i<kMaxMsgLen-1; ci++)
			{
				/* This works only for SOME formats, like %s or %d (NO PRECISION!)! Everything unknown will be treated as %d! */
				char c=format[ci];
				if (c=='%')
				{
					/* Uh! It's a parameter-dingens */
					char t=format[ci+1]; // get its type. NO SUPPORT FOR PRECISION YET!
					if (t=='s')
					{	/* It's a string */
						sprintf(tmp, "%s", va_arg(argp, char*));
						strcat(msg2, tmp);
						msg2i=strlen(msg2);
						ci++; // omit the "s"
					}
					else
					{	/* It's something else. Hopefully some kind of number */
						sprintf(tmp, "%d", va_arg(argp, int));
						strcat(msg2, tmp);
						msg2i=strlen(msg2);
						ci++; // omit the "s"
					}
				}
				else
				{
					/* Add everything else to the buffer */
					msg2[msg2i]=c;
					msg2i++;
				}
			}
#else
			vsprintf(msg2, format, argp);
#endif


				if (strlen(methodName) > kMaxMethodLen)
				{
					memcpy(lMethodName, methodName, kMaxMethodLen-1);
					lMethodName[kMaxMethodLen]='\0';
				}
				else
				{
					strcpy(lMethodName, methodName);
				}

				if (strlen(format) > kMaxFormatLen) format[kMaxFormatLen]='\0';
				
				sprintf(msg1, "%s (%d): ", lMethodName, lineNumber);
				strcat(msg1, msg2);

				/* Output to stdout */
				if (1)
				{
					printf(msg1);
				}


				/* Write to syslog */
				if (0)
				{
					//logger(msg1);
				}
			
			va_end (argp);
		}
	
	return;
}

The Solution