Customizing (or rather extending) sprintf()

Started by
3 comments, last by irreversible 10 years, 3 months ago

I want to extend the regular sprintf() to take advantage unknown string lengths and my own memory manager. Hence I set out to write a little wrapper to precalculate the string length, allocate it manually and then do the sprintf() operation. This seemed like a straightforward task until it started crashing my program. As it stands, working out what is wrong has proven to be somewhat of a formidable task. Here's my initial code:


ISharedPtr<char> SPRINT(IN ISharedPtr<char>& buf, char * fmt, ...)
{
	va_list ap;
	int iFmtLen = 0;

	if(fmt == NULL)
		{ return buf = null; }

	//if buf is allocated, free it
	if(buf != null)
		{ Delete(buf); }

 	va_start(ap, fmt);
		//#get the length
		#ifdef WIN32
			//I was using the VC version of snprintf(), but it started crashing so after a bit of googling
			//it turned out _scprintf() should be specialized for the task. It fails in the same way.
			iFmtLen = _scprintf(fmt, ap);//_snprintf(NULL, 0, fmt, ap); // <- PROBLEM HERE
		#else
			iFmtLen = snprintf(NULL, 0, fmt, ap);
		#endif

		//use my allocator
		buf = NewArray(char, iFmtLen + 1);

		//do regular sprintf()
		vsprintf(buf.ptr, fmt, ap);
	va_end(ap);

	return buf;
}

The symptoms: granted, I don't know the printX() functions in detail, but my first question arises when outputting the fmt argument after entering SPRINT(). It shows up as:

fmt before entering SPRINT(): '%d'

fmt when output to console from inside SPRINT(): '4245253'

Inspecting fmt while debugging shows the expected '%d'

Passing the format directly to _scprintf() like so:

_scprintf("'%d", 100000000);

produces the expected length of 9. Passing it as it is in the code produces 7 or some similar incorrect result. What totally screws everything up and causes the entire program to crash inside output.c (SDK file), though, is passing a string in as a variadic argument: fmt = "%s"

All arguments are correct and all variadic types match the format. What am I missing here?

PS - I would appreciate not being reminded that I should be using boost::shared_ptr or whatnot.

Edit: the title is likely pretty misleading...

Advertisement

One thing I noticed: you're using _scprintf and snprintf, but you're passing those functions the va_list you have. I think you want to be using _vscprintf and vsnprintf.

Also, I know this isn't your main problem, but: I think MSVC understands and properly handles the special case where you call vsnprintf/snprintf with a destination buffer of NULL and a length of 0, so you may be able to avoid the #ifdef WIN32 stuff. (EDIT: just tested _snprintf(NULL, 0, fmt, etc) on MSVC 2010 and it returned the proper length).

Thanks for the answer. Just out of curiosity - did you test it with a direct variadic input or the va_list enumerator? It does work for me in the former case, but I can't get it working with va_list - eg when SPRINT() is a proxy function with variadic input.

PS: VC doesn't have snprintf(), hence my using _snprintf() (as commented out). Click me.

Thanks for the answer. Just out of curiosity - did you test it with a direct variadic input or the va_list enumerator? It does work for me in the former case, but I can't get it working with va_list - eg when SPRINT() is a proxy function with variadic input.

PS: VC doesn't have snprintf(), hence my using _snprintf() (as commented out). Click me.

Hmm, when I wrote the above post I only tested _snprintf. But just now I tested vsnprintf and it all worked. Here's my test code:


void test_vsnprintf(const char *fmt, ...)
{
	va_list arg;
	va_start(arg, fmt);
	const int len = vsnprintf(NULL, 0, fmt, arg);
	printf("len: %d\n", len);
	va_end(arg);
}

int main(int argc, const char *argv[])
{
	test_vsnprintf("hello %d %s", 10, "fun times");		// prints "len: 18", as expected/desired

	return 0;
}

Hm - I actually feel a bit silly for not having tried that myself. Strange though that Google and the dozen or so sites I checked didn't point this out to me. Also strange - that the functions I did try didn't work, even though they seem to explicitly claim to do so.

In any case - help much appreciated!

This topic is closed to new replies.

Advertisement