For systems without heap, i.e. small microcontrollers, it might be a good idea to return a struct containing a char array large enough for longest string.
// define a non-growable string type with adjustable size,
// similar to Turbo Pascal but (here) zero-terminated
template<unsigned N>struct String{ // see (A)
char s[N];
};
// A function converting to binary
String<17>binaryStr(uint16_t n) // see (B)
{
String<17>buf; // see (C)
char*p=buf.s;
uint16_t mask=0x8000;
while (;mask!=1; mask>>=1) if (mask&n) break;
while (;mask; mask>>=1) *p++ = mask&n ? '1' : '0';
*p=0;
return buf; // see (C)
}
// Use it somewhere
printf("42 is 0b%s\n",binaryStr(42).s); // see (D)
A: For plain C, define some strings with fixed lengths instead, as you have no templates.
B: Functions returning a struct get a hidden pointer to a caller-reserved stack space.
As would be the following declaration:
String<17>& binaryStr(String<17>&, uint16_t);
The resulting string cannot have more than 16 characters plus terminating '\0', therefore 17.
C: At return, the struct has to be delivered in one piece. Accessing the hidden parameter is usually impossible. (In MSVC, it is possible using __$ReturnUdt.) Therefore, you need a local copy of the same struct and fill it with your code. Modern compilers are smart enough to avoid the "move constructor" at return statement and indeed won't allocate local stack space for buf.
D: Using the return value requires attention, especially at printf() functions having ... ellipisis (variable argument list). As standard behaviour of C/C++ is copying structures, the compiler would not complain but copying all the characters onto the stack! Therefore, you have to get a pointer, here by accessing .s.
For more readability for C++ users, add
const char*c_str() const{return s;}
to the String<> class.
As net result, you have a function that really returns a string, and you neither have to cope with manual stack space reservation, nor using static char arrays, nor use heap. Gotcha, you have to pay attention to forward a pointer, not the struct, to ... parameters.
name()looks like a query make itconst:string name() const;.const? The returned object ends up belonging to the client, and he should be free to do with it what he wants.const std::string&might be an even better idea.