Sign in to follow this  
irreversible

Customizing (or rather extending) sprintf()

Recommended Posts

irreversible    2860

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...

Share this post


Link to post
Share on other sites
Samith    2460

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).

Edited by Samith

Share this post


Link to post
Share on other sites
irreversible    2860

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.

Edited by irreversible

Share this post


Link to post
Share on other sites
Samith    2460

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;
}

Share this post


Link to post
Share on other sites
irreversible    2860

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!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this