Skip to main content
GameDev.net gamedev.net
🔒 Locked

c++: sprintf but return chars. Possible?

Started by suliman May 22, 2016 at 1:37 PM 22 replies 4k views
Original Post
suliman
suliman

Hi

Im doing:

.



//doStuff looks like this: void doStuff(char * n);

char temp[40];
sprintf(tempText,"Mytext: %d", myInt);
doStuff(tempText);

.

Is there a built in "getChar" function (in c or c++) so i can do this directly, like:

.


doStuff(getChar("Mytext: %d", myInt));
Alberth
Alberth

If you don't mind a static buffer, you can fold those three lines in a "getChar" function (although the name is a bit misleading).

If you do mind a static buffer (and that is pronably a good thing), you need storage for the result, you could make your getChar return a std::string.

Last but not least you can write your own 'printf' function using varargs, and do whatever you desire, it might be a bit complicated though if you want to omit the text buffer.

Khatharr
Khatharr

You could just overload doStuff() with varargs...

*grumble stdio grumble*


void doStuff(const char* formatString, ...) {
  va_list args;
  va_start(args, formatString);
  size_t strLen = vsnprintf(nullptr, 0, formatString, args);
  std::vector<char> buffer(strLen + 1);
  vsnprintf(buffer.data(), buffer.size(), formatString, args);
  va_end(args);

  //do stuff - your char* is now buffer.data()
}

or for more general usage


std::string stringf(const char* formatString, ...) {
  va_list args;
  va_start(args, formatString);
  size_t strLen = vsnprintf(nullptr, 0, formatString, args);
  std::string buffer(strLen + 1, 0);
  vsnprintf(&buffer[0], buffer.size(), formatString, args);
  va_end(args);
  buffer.pop_back();
  return buffer;
}

doStuff(stringf("derp %d herp", 42).c_str());
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
fastcall22
fastcall22
You could abuse operator,:


void doStuff(const char*);

int main() {
    char buf[200];
    doStuff((sprintf(buf,"%d",myInt),buf));
}
Disclaimer: Don’t actually do this.
Chris_F
Chris_F

If you are using C++ then you should be using iostream instead of stdio. Alternatively you could at least use snprintf.

BitMaster
BitMaster
The iostream classes are often very annoying to work with when you have to deal with more complex substitutions. It can become very hard to read, very fast.

Something like boost::format is often a better idea because it combines a familiar, easier to read syntax with all the type safety C++ can offer you.
21st Century Moose
21st Century Moose

I'm going to be contrary and advise not to do this. What if your hypothetical getChar fails?

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
suliman
suliman

What do you mean "if it fails"?

What i do now with sprintf could then also potentially fail no? I just want to compress the code i need to write. I end up in many such situations (must create a temporary char array that i inject floats and other stuff with %.2f etc and then pass it to a function that takes a "char *" parameter).

Hodgman
Hodgman

Hi
Im doing:
.



//doStuff looks like this: void doStuff(char * n);

char temp[40];
sprintf(tempText,"Mytext: %d", myInt);
doStuff(tempText);
.
Is there a built in "getChar" function (in c or c++) so i can do this directly, like:
.

doStuff(getChar("Mytext: %d", myInt));


If we reinvent std::string, you can get half way there :P
class Chars
{
  char* c;
  size_t len;
public:
  explicit Chars( size_t length ) : c(new char[length]) len(length) {}
  Chars() : c() len() {}
  ~Chars() { delete[] c; }
  Chars( const Chars& o ) : c(o.len ? new char[o.len] : 0), len(o.len) { std::copy(o.c, o.c+len, c); }
  Chars( Chars&& o ) : c(o.c) len(o.len) { o.c = 0; o.len = 0; }
  Chars& operator=( const Chars& o ) { delete[] c; c = o.len ? new char[o.len] : 0; len = o.len; std::copy(o.c, o.c+len, c); return *this; }
  Chars& operator=(Chars&& o) { c = o.c; len = o.len; o.c = 0; o.len = 0; }

  const char* c_str() const { return c; }
        char* c_str()       { return c; }
}

Chars getChar( const char* fmt, ... )
{
    va_list arg;
    va_start(arg, fmt);
    size_t size = (size_t)vsnprintf( 0, 0, fmt, arg );
    Chars result = Chars( size );
    vsnprintf( result.c_str(), size, fmt, arg );
    va_end(argptr);
    return result;
}

Chars c = getChar("Mytext: %d", myInt);
doStuff(c.c_str());
alh420
alh420
If we reinvent std::string, you can get half way there :P

Or, you could just ignore the extra copy and just use std::string.

Here's a c++11 templated version I'm using.


template<class... Args >
std::string makeString(const char* format, Args&&... args) {
  char buffer[256];
  int len = snprintf(buffer, 256, format, std::forward<Args>(args)...);
  if(len >= 256) {
    // if string is too long, try again with a bigger buffer
    char buffer[len + 1];
    snprintf(buffer, len + 1, format, std::forward<Args>(args)...);
    return buffer;
  }
  return buffer;
}

.

Edit: Hmm. Maybe I should read the topic more carefully before answering. Oh well, I'll leave this here anyway. Back to work and getting stuff compiling I guess...

ApochPiQ
ApochPiQ

What do you mean "if it fails"?

What i do now with sprintf could then also potentially fail no? I just want to compress the code i need to write. I end up in many such situations (must create a temporary char array that i inject floats and other stuff with %.2f etc and then pass it to a function that takes a "char *" parameter).



The best way to solve this is with a string class of some kind. Otherwise you're going to have a nightmare cleaning up all the heap-allocated temporary strings. Honestly, I think you're pushing the laziness lever a little too hard here. I'd be shocked if your actual coding bottleneck was declaring temporary char arrays.
phil_t
phil_t

If you're just looking for nice c++ string formatting, I recommend fmt.



fmt (formerly cppformat) is an open-source formatting library. It can be used as a safe alternative to printf or as a fast alternative to C++ IOStreams.

Khatharr
Khatharr

What do you mean "if it fails"?

What i do now with sprintf could then also potentially fail no? I just want to compress the code i need to write. I end up in many such situations (must create a temporary char array that i inject floats and other stuff with %.2f etc and then pass it to a function that takes a "char *" parameter).



The best way to solve this is with a string class of some kind. Otherwise you're going to have a nightmare cleaning up all the heap-allocated temporary strings. Honestly, I think you're pushing the laziness lever a little too hard here. I'd be shocked if your actual coding bottleneck was declaring temporary char arrays.

My suspicion was that the "clutter" and DRY violation was the complaint.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
SeraphLance
SeraphLance

We have a macro for this at work that basically amounts to this (with different names and a lot of extra checks):


#define getChar(fmt, ...) sprintfAndReturnBuffer(_alloca(1024), 1024, fmt, ##__VA_ARGS__)

At the end of the day, you're not getting much. If you want to print a formatted string like that, you're going to have to allocate somehow, and in most cases I'd rather allocate with a real buffer than rely on trivial conveniences like this one.

suliman
suliman

Well a temp buffer is ok for me, I only use this in specific situations. Not pretty i know but it works:)

Thanks for your input.


char * getStr(const char* fmt, ...) {	
	static char txt[1000];
	va_list va;
	va_start( va, fmt );
	vsprintf( txt, fmt, va );
	va_end( va );
	return txt;
}
SeraphLance
SeraphLance

I'm not sure that's wise. For example, what do you think this will do?


doStuff2(getStr("Mytext: %d", myInt), getStr("Myothertext: %d", myOtherInt));

If you're okay with nasty surprises -- and I don't recommend being okay with nasty surprises -- your code is fine as-is, but if you want the above code to actually work you need to allocate actual memory instead of a static buffer (and even then, ideally in the calling scope).

fastcall22
fastcall22
Use StringBuilder:


class string_builder {
public:
	template<typename T>
	string_builder& operator << (const T& val) {
		s << val;
		return *this;
	}

	operator const std::string () const {
		return s.str();
	}

private:
	std::ostringstream s;
};


int main(int argc, char* argv[]) {
	try {
		// contrived example
		if ( argc <= 1 ) {
			throw std::runtime_error(
				string_builder() << "Not enough arguments, got " << argc
			);
		}
	}
	catch ( const std::exception& ex ) {
		std::cerr << "Exception caught: " << ex.what() << std::endl;
	}
}
Wait, this isn’t Java?
SeraphLance
SeraphLance

Use StringBuilder:


Is it bad if every time I see the whole IO "<<" overload I vomit in my mouth a little?

Pink Horror
Pink Horror

Use StringBuilder:


Is it bad if every time I see the whole IO "<<" overload I vomit in my mouth a little?

Vomit all you want. I just personally think that "vomit in my mouth a little" is extremely overused. I still manage to keep my stomach acid down when I read it.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.