I need to use FormatMessage() for a project, but I don't like its scary interface. Does anyone know of a facade that tidies it up while still allowing for the replacement parameters?
I just read the second part of the FastFormat introduction, and am considering writing an extension for FormatMessage() (or asking the FastFormat project team if they've got one in the works), but I'm keen to get something asap, so if there's anything else decent out there I'd probably grab that instead.
What I want is to be able to write code such as:
HINSTANCE netevent = ::LoadLibrary("netevent.dll");
std::string msg = LookupError(netevent, EVENT_SERVICE_START_FAILED_II,
"child-svr", "parent-svr", "ship happens");
::puts(msg.c_str());
Which would give the result:
The child-svr service depends on the parent-svr service which failed to start be cause of the following error:
ship happens
The current wrapper I've built has the interface:
std::string LookupError(HINSTANCE hinst, DWORD id, ...);
There are two problems with this:
- It's not type-safe, since it's easy to pass any type -
int
,std::string
,void*
- that is notconst char*
- It's easy to mismatch the number of arguments with the number required by the format string representing the error
Given the abilities of FastFormat in terms of type-safety, I want to know if there's a way to follow its mechanisms to deal with FormatMessage().
As the number of parameters to be inserted into the format string cannot be checked by the compiler, it is impossible to make this truly type-safe at compile time.
You can get most of the way there by just having a few overloads for different numbers of inserted parameters, and then specifying the inserted values with something flexible like
boost::any
. So the overload for two parameters would be:When you retrieve the value from
arg1
, boost will throw if you try to get the wrong type, so you just need to examine the format string and try to get the required type from each argument.Alternatively you could use templates and std::ostringstream (or boost::lexical_cast) for a very flexible version; again there would be overloads to allow the number of arguments to vary, so here's the single-argument version:
That way you can get a string from each argument as long as the passed type can be streamed, and nothing else should be required as long as the format strings expect only strings to insert.