In addition to Zao's points, you also have no type safety and can easily supply a parameter list that does not match the format string.
Here's a really naive header I use to provde type-safe string building. You could extend this easily enough:
#ifndef STRINGFORMAT_H
#define STRINGFORMAT_H
#include <string>
#include <sstream>
template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8> std::string stringFormat(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7, const T8 &t8)
{
std::ostringstream os;
os << t0 << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8;
return os.str();
}
template<class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7> std::string stringFormat(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7){ return stringFormat(t0, t1, t2, t3, t4, t5, t6, t7, ""); }
template<class T0, class T1, class T2, class T3, class T4, class T5, class T6> std::string stringFormat(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6){ return stringFormat(t0, t1, t2, t3, t4, t5, t6, ""); }
template<class T0, class T1, class T2, class T3, class T4, class T5> std::string stringFormat(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5){ return stringFormat(t0, t1, t2, t3, t4, t5, ""); }
template<class T0, class T1, class T2, class T3, class T4> std::string stringFormat(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4){ return stringFormat(t0, t1, t2, t3, t4, ""); }
template<class T0, class T1, class T2, class T3> std::string stringFormat(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3){ return stringFormat(t0, t1, t2, t3, ""); }
template<class T0, class T1, class T2> std::string stringFormat(const T0 &t0, const T1 &t1, const T2 &t2){ return stringFormat(t0, t1, t2, ""); }
template<class T0, class T1> std::string stringFormat(const T0 &t0, const T1 &t1){ return stringFormat(t0, t1, ""); }
template<class T0> std::string stringFormat(const T0 &t0){ return stringFormat(t0, ""); }
#endif // STRINGFORMAT_H
Usage
class SomeClass
{
};
// implement ostream overload for SomeClass
SomeClass c;
std::string s = stringFormat("There are ", 0.24f, " things and ", c, " works too");
You can just std::cout the result and throw your exception instead of returning the string. I'm sure better, more extensible ways of doing this now with varadic template args exist but they are beyond me at present.