mulle-objc Value-to-String Conversion Reference

· nat's blog


There are many ways to turn a value into a string in this stack. This doc catalogues every one () so you can pick the right tool without guessing.

TLDR — The "I just want sprintf / stringWithFormat:" Cheat Sheet #

These are the six format-string entry points you'll use most often, sorted by memory-management style:

You want… Use this Output type Free it? Example
Stack (short-lived) mulle_sprintf_do(s, fmt, …) char * no mulle_sprintf_do(s, "%d", i) { use(s); }
Build incrementally (C) mulle_buffer_do(buf) { mulle_buffer_sprintf(...); } mulle_buffer * no (auto) mulle_buffer_do(buf) { mulle_buffer_sprintf(buf, "%d", i); … extract_string; }
Build incrementally → autoreleased C string mulle_buffer_do_autoreleased_string(buf, NULL, s) { … } char * no (pool) mulle_buffer_do_autoreleased_string(buf, NULL, s) { mulle_buffer_sprintf(buf, "%d", i); }
Heap (must free) mulle_asprintf(&p, fmt, …) char * mulle_free(p) mulle_asprintf(&p, "%d", i); … mulle_free(p);
Autoreleased C string (one-shot) MulleObjC_asprintf(fmt, …) char * no (pool) s = MulleObjC_asprintf("%d", i);
Autoreleased NSString [NSString stringWithFormat:…] NSString * no (pool) s = [NSString stringWithFormat:@"%d", i];
Mutable NSString [NSMutableString string] + -appendFormat: NSMutableString * no (pool) ms = [NSMutableString string]; [ms appendFormat:@"%d", i];

Prefer mulle_buffer_do / mulle_buffer_do_autoreleased_string over mulle_snprintf — they grow as needed, compose naturally, and avoid fixed buffer management bugs.


Reference Table #

API / Macro Level Output Type Lifetime / Ownership NUL-terminated? Format args? Allocator Notes
mulle_snprintf(buf, size, fmt, ...) C char * (caller buf) caller owns storage yes yes returns -1 on overflow, always NULs
mulle_sprintf(buf, fmt, ...) C char * (caller buf) caller owns storage yes yes unsafe — no size limit
mulle_vsnprintf(buf, size, fmt, va) C char * (caller buf) caller owns storage yes yes (va_list) va_list variant
mulle_mvsnprintf(buf, size, fmt, va) C char * (caller buf) caller owns storage yes yes (mulle_vararg) mulle_vararg variant
mulle_asprintf(&strp, fmt, ...) C char * (heap) caller must mulle_free() yes yes mulle_default_allocator heap string, use with mulle_malloc family
mulle_vasprintf(&strp, fmt, va) C char * (heap) caller must mulle_free() yes yes (va_list) mulle_default_allocator va_list variant
mulle_mvasprintf(&strp, fmt, va) C char * (heap) caller must mulle_free() yes yes (mulle_vararg) mulle_default_allocator mulle_vararg variant
mulle_allocator_asprintf(a, &p, fmt, ...) C char * (heap) caller must free via a yes yes custom supply your own allocator
mulle_allocator_vasprintf(a, &p, fmt, va) C char * (heap) caller must free via a yes yes (va_list) custom supply your own allocator
mulle_sprintf_do(s, fmt, ...) { … } C char * (stack buffer) valid only inside {…} block yes yes stack-local; auto-cleanup on block exit
mulle_vsprintf_do(s, fmt, args) { … } C char * (stack buffer) valid only inside {…} block yes yes (va_list) va_list variant of above
mulle_buffer_sprintf(buf, fmt, ...) C appends to mulle_buffer buffer owns no yes buffer's allocator does not NUL-terminate; call mulle_buffer_make_string() if needed
mulle_buffer_vsprintf(buf, fmt, va) C appends to mulle_buffer buffer owns no yes (va_list) buffer's allocator va_list variant
mulle_buffer_mvsprintf(buf, fmt, va) C appends to mulle_buffer buffer owns no yes (mulle_vararg) buffer's allocator mulle_vararg variant
mulle_buffer_do(name) { … } C mulle_buffer * (stack) auto-cleanup on block exit no NULL (default) idiomatic short-lived buffer
mulle_buffer_do_flexible(name, capacity) { … } C mulle_buffer * (stack) auto-cleanup on block exit no NULL (default) with custom starting capacity
mulle_buffer_add_string(buf, s) C appends to mulle_buffer buffer owns no buffer's allocator raw string append
mulle_buffer_add_bytes(buf, bytes, len) C appends to mulle_buffer buffer owns no buffer's allocator raw binary append
mulle_buffer_extract_string(buf) C char * (heap) caller owns, must free yes buffer's allocator steals ownership from buffer
mulle_buffer_extract_bytes(buf) C void * (heap) caller owns, must free no buffer's allocator steals ownership from buffer
mulle_printf(fmt, ...) C stdout (stream) yes yes output to stdout
mulle_fprintf(fp, fmt, ...) C FILE * (stream) yes yes output to any FILE stream
mulle_dtostr(value, buf) C char * (caller buf) caller owns storage yes double→string; buffer must be ≥25 bytes
mulle_dtostr_decompose(value) C struct mulle_dtostr_decimal value type decompose double, custom format
mulle_utf8_convert_to_utf16_string(src, len, a) C mulle_utf16_t * (heap) caller must free via a yes custom encoding conversion
mulle_utf8_convert_to_utf32_string(src, len, a) C mulle_utf32_t * (heap) caller must free via a yes custom encoding conversion
MulleObjC_asprintf(fmt, ...) ObjC char * (heap) autoreleased — do not free yes yes mulle_default_allocator wrapped in autorelease pool
MulleObjC_vasprintf(fmt, args) ObjC char * (heap) autoreleased — do not free yes yes (va_list) mulle_default_allocator va_list variant
MulleObjC_mvasprintf(fmt, args) ObjC char * (heap) autoreleased — do not free yes yes (mulle_vararg) mulle_default_allocator mulle_vararg variant
MulleObjC_strdup(s) ObjC char * (heap) autoreleased — do not free yes mulle_default_allocator autoreleased copy of C string
mulle_buffer_do_autoreleased_string(buf, a, s) { … } ObjC char * (heap) autoreleased — do not free yes custom or NULL build in buffer → autorelease result
MulleObjCAutoreleaseAllocation(p, a) ObjC void * autoreleased — do not free custom or NULL wrap any malloc'd pointer in autorelease
-[NSObject UTF8String] ObjC char * (heap) autoreleased — do not free yes equivalent to [[self description] UTF8String]
-[NSObject description] ObjC NSString * autoreleased override to customize
TypeUTF8String(value) (e.g. CGRectUTF8String, CGSizeUTF8String, …) ObjC char * (heap) autoreleased — do not free yes type-specific formatting, returns autoreleased C string
[NSString stringWithUTF8String:cstr] Foundation NSString * autoreleased create NSString from C string
[NSString stringWithCString:cstr encoding:e] Foundation NSString * autoreleased NSString from C string with explicit encoding
[NSString stringWithFormat:fmt, ...] Foundation NSString * autoreleased yes formatted NSString
-[NSString UTF8String] Foundation char * valid for lifetime of NSString yes get C string from NSString (not autoreleased!)
NSMutableString + -appendFormat:fmt, ... Foundation NSMutableString * caller retains yes incrementally build NSString; stringWithFormat: but mutable
NSMutableString + -appendString:s Foundation NSMutableString * caller retains raw NSString append
NSMutableString + -setString:s Foundation NSMutableString * caller retains replace content
NSStringFromClass(cls) Foundation NSString * autoreleased class name as string
NSStringFromSelector(sel) Foundation NSString * autoreleased selector name as string
@(expr).stringValue Foundation NSString * autoreleased boxed value → string

Decision Flow #

Pure C, no runtime
├── Need a short-lived temp string?
│   └── mulle_sprintf_do(s, fmt, ...) { use s; }
├── Need a bounded fixed buffer?
│   └── mulle_snprintf(buf, sizeof(buf), fmt, ...)
├── Need a heap-allocated long-lived string?
│   └── mulle_asprintf(&p, fmt, ...)  →  mulle_free(p)
├── Building up incrementally / many appends?
│   └── mulle_buffer_do(buf) { mulle_buffer_sprintf(buf, ...); ... extract_string }
├── Just print to stdout / stderr?
│   └── mulle_printf(fmt, ...)  /  mulle_fprintf(stderr, fmt, ...)
└── Just a float to string?
    └── mulle_dtostr(value, buf)

ObjC runtime available
├── Want autoreleased C string (no manual free)?
│   ├── MulleObjC_asprintf(fmt, ...)
│   └── mulle_buffer_do_autoreleased_string(buf, NULL, s) { build; }
├── Want autoreleased C string for a value type?
│   └── CGRectUTF8String(rect), etc.
├── Need an NSString (for containers, etc.)?
│   └── [NSString stringWithFormat:...]
└── Implementing -UTF8String?
    └── mulle_buffer_do_autoreleased_string(buf, NULL, s) { format; }  return s;

Memory-Management Rules of Thumb #

Pattern Free? How?
char buf[ N] + mulle_snprintf No stack allocated
mulle_asprintf(&p, ...) Yes mulle_free(p)
mulle_allocator_asprintf(a, &p, ...) Yes via that allocator's free
mulle_sprintf_do(s, ...) No auto stack cleanup on block exit
mulle_buffer_extract_string(buf) Yes mulle_free(p) (or allocator's free)
MulleObjC_asprintf(...) No autoreleased — pool drain frees it
mulle_buffer_do_autoreleased_string(...) No autoreleased
[NSString stringWithFormat:...] No autoreleased
-[NSString UTF8String] No valid while NSString lives
TypeUTF8String(...) No autoreleased

When Each Layer Is Available #

Mechanism Available where
mulle_sprintf / mulle_asprintf / mulle_buffer_sprintf Everywhere (mulle-core / mulle-sprintf)
mulle_printf / mulle_fprintf Everywhere (mulle-fprintf)
mulle_buffer_do / mulle_buffer_extract_string Everywhere (mulle-buffer via mulle-core)
mulle_sprintf_do Everywhere (macro in mulle-sprintf.h)
MulleObjC_asprintf / MulleObjC_vasprintf Needs MulleObjC
mulle_buffer_do_autoreleased_string Needs MulleObjC (MulleObjCPrinting.h)
MulleObjCAutoreleaseAllocation Needs MulleObjC (NSAutoreleasePool)
-[NSObject UTF8String] / -[NSObject description] Needs MulleObjC
TypeUTF8String functions Per-library (MulleCG, MulleUIBase, etc.)
[NSString stringWithFormat:] / [NSString stringWithUTF8String:] Needs MulleFoundation
NSStringFromClass / NSStringFromSelector Needs MulleFoundation

last updated: