Безопасный / гибкий фасад для Windows FormatMessage - PullRequest
4 голосов
/ 10 мая 2009

Мне нужно использовать FormatMessage () для проекта, но мне не нравится его страшный интерфейс. Кто-нибудь знает о фасаде, который убирает его, но в то же время учитывает параметры замены?

Я только что прочитал вторую часть введения FastFormat и собираюсь написать расширение для FormatMessage () (или спрашивать команду проекта FastFormat , если у них есть один в работе), но я стремлюсь получить что-то как можно скорее, поэтому, если есть что-то еще приличное, я бы, вероятно, взял бы это вместо этого.

Я хочу иметь возможность писать такой код, как:

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());

Что дало бы результат:

The child-svr service depends on the parent-svr service which failed to start be cause of the following error:
ship happens

Текущая оболочка, которую я создал, имеет интерфейс:

std::string LookupError(HINSTANCE hinst, DWORD id, ...);

Есть две проблемы с этим:

  • Это небезопасно, так как легко передать любой тип - int, std::string, void* - это не const char*
  • Легко не совпадать с числом аргументов с числом, требуемым для строки формата, представляющей ошибку

Учитывая возможности FastFormat с точки зрения безопасности типов, я хочу знать, есть ли способ следовать его механизмам для работы с FormatMessage () .

Ответы [ 2 ]

1 голос
/ 10 мая 2009

Так как количество параметров, вставляемых в строку формата, не может быть проверено компилятором, невозможно сделать это действительно безопасным для типов во время компиляции.

Вы можете получить большую часть пути, просто имея несколько перегрузок для различного числа вставленных параметров, а затем указав вставленные значения с помощью чего-то гибкого, например boost::any. Таким образом, перегрузка для двух параметров будет:

std::string FormatMessage(HINSTANCE hinst, DWORD id, const boost::any &arg1, const boost::any &arg2);

Когда вы извлекаете значение из arg1, boost выдаст, если вы попытаетесь получить неправильный тип, поэтому вам просто нужно изучить строку формата и попытаться получить требуемый тип из каждого аргумента.

В качестве альтернативы вы можете использовать шаблоны и std :: ostringstream (или boost :: lexical_cast) для очень гибкой версии; снова будут перегрузки, позволяющие варьировать число аргументов, поэтому вот версия с одним аргументом:

template <class TArg1>
std::string FormatMessage(HINSTANCE hinst, DWORD id, const TArg1 &arg1)
{
    std::ostringstream arg1Stream;
    arg1Stream << arg1;
    std::string arg1String = arg1Stream.str();

    DWORD_PTR argArray = reinterpret_cast<DWORD_PTR>(arg1String.c_str());

    // ... etc
}

Таким образом, вы можете получить строку из каждого аргумента, если передаваемый тип может быть передан в потоковом режиме, и больше ничего не требуется, если строки формата ожидают вставки только строк.

0 голосов
/ 19 февраля 2015

Библиотека C ++ Format позволяет форматировать собственные сообщения об ошибках Windows, соответствующие кодам ошибок, возвращаемым GetLastError(), и сообщения об ошибках POSIX, соответствующие ошибкам, заданным errno. Например:

// This throws a WindowsError with the description
//   cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR)
  throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename);

Сообщение об ошибке Windows получено с помощью функции FormatMessage API. Вы также можете отформатировать сообщение об ошибке с fmt::format_windows_error, которое не вызывает исключение. Подробнее см. Системные ошибки .

Отказ от ответственности: я являюсь автором формата C ++

...