Как я могу изменить его, чтобы использовать встроенную функцию или шаблон вместо макроса? - PullRequest
1 голос
/ 22 марта 2010

У меня есть полезный макрос здесь:

#include <algorithm>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>
#include <Windows.h>

namespace Path {

bool Exists(const std::wstring& path)
{
    DWORD result = GetFileAttributesW(path.c_str());
    return result != INVALID_FILE_ATTRIBUTES;
}

// THIS IS THE MACRO IN QUESTION!
#define PATH_PREFIX_RESOLVE(path, prefix, environment) \
if (boost::algorithm::istarts_with(path, prefix)) { \
    ExpandEnvironmentStringsW(environment, buffer, MAX_PATH); \
    path.replace(0, (sizeof(prefix)/sizeof(wchar_t)) - 1, buffer); \
    if (Exists(path)) return path; \
}

std::wstring Resolve(std::wstring path)
{
    using namespace boost::algorithm;
    wchar_t buffer[MAX_PATH];
    trim(path);
    if (path.empty() || Exists(path)) return path;

    //Start by trying to see if we have a quoted path
    if (path[0] == L'"') {
        return std::wstring(path.begin() + 1, std::find(path.begin() + 1, path.end(), L'"'));
    }

    //Check for those nasty cases where the beginning of the path has no root
    PATH_PREFIX_RESOLVE(path, L"\\", L"");
    PATH_PREFIX_RESOLVE(path, L"?\?\\", L"");
    PATH_PREFIX_RESOLVE(path, L"\\?\\", L"");
    PATH_PREFIX_RESOLVE(path, L"globalroot\\", L"");
    PATH_PREFIX_RESOLVE(path, L"system32\\", L"%systemroot%\\System32\\");
    PATH_PREFIX_RESOLVE(path, L"systemroot\\", L"%systemroot%\\");

    static std::vector<std::wstring> pathExts;
    if (pathExts.empty()) {
        #define MAX_ENVVAR 32767
        wchar_t pathext[MAX_ENVVAR];
        DWORD length = GetEnvironmentVariableW(L"PATHEXT", pathext, MAX_ENVVAR);
        if (!length) WindowsApiException::ThrowFromLastError();
        split(pathExts, pathext, std::bind2nd(std::equal_to<wchar_t>(), L';'));
        pathExts.insert(pathExts.begin(), std::wstring());
    }
    std::wstring::iterator currentSpace = path.begin();
    do {
        currentSpace = std::find(currentSpace, path.end(), L' ');
        std::wstring currentPath(path.begin(), currentSpace);
        std::wstring::size_type currentPathLength = currentPath.size();
        typedef std::vector<std::wstring>::const_iterator ExtIteratorType;
        for(ExtIteratorType it = pathExts.begin(); it != pathExts.end(); it++) {
            currentPath.replace(currentPathLength, currentPath.size() - currentPathLength, *it);
            if (Exists(currentPath)) return currentPath;
        }
        if (currentSpace != path.end())
            currentSpace++;
    } while (currentSpace != path.end());

    return path;
}

}

Он используется около 6 раз в рамках одной функции (вот и все), но макросы, похоже, имеют "плохую карму": P

В любом случае, проблема здесь в части макроса sizeof(prefix). Если я просто заменю это функцией, принимающей const wchar_t[], то sizeof() не даст ожидаемых результатов.

Простое добавление члена размера тоже не решит проблему. Заставление пользователя указывать размер константы также приводит к путанице дублированных констант на сайте вызова.

Есть идеи на этот счет?

Ответы [ 3 ]

3 голосов
/ 22 марта 2010

Почему бы просто не сделать эту обычную функцию, которая использует wcslen(), чтобы получить длину интересующего вас параметра. В этом макросе / функции происходит достаточно вещей, которые, я полагаю, мало что пытаются заставить быть встроенным. «Накладные расходы; wcslen() вызов и обработка почти наверняка не станут узким местом.

Единственный трюк в макросе, который вас действительно должен беспокоить - это (как указал GMan) возврат из макроса, который скрыт при вызове макроса.

Просто создайте функцию, которая возвращает успех / неудачу, и вы можете вернуться, если функция завершится успешно:

bool PathPrefixResolve( std::wstring& path, wchar_t const* prefix, wchar_t const* environment)
{
    wchar_t buffer[MAX_PATH];

    if (boost::algorithm::istarts_with(path, prefix)) {
        ExpandEnvironmentStringsW( environment, buffer, MAX_PATH);

        std::wstring tmp( path);

        tmp.replace(0, wcslen( prefix), buffer);
        if (Exists(tmp)) {
            path = tmp;
            return true;
        }
    }

    return false;
}

для использования функции:

//Check for those nasty cases where the beginning of the path has no root
if (PathPrefixResolve2(path, L"\\", L"")) return path;
if (PathPrefixResolve2(path, L"?\?\\", L"")) return path;
if (PathPrefixResolve2(path, L"\\?\\", L"")) return path;
if (PathPrefixResolve2(path, L"globalroot\\", L"")) return path;
if (PathPrefixResolve2(path, L"system32\\", L"%systemroot%\\System32\\")) return path;
if (PathPrefixResolve2(path, L"systemroot\\", L"%systemroot%\\")) return path;

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

Кроме того, ваша реализация макроса имеет некоторое поведение, которое я думаю, вероятно, является ошибкой - если путь начинается с L"\\?\\", это означает, что он также начинается с L"\\" и вашего первого вызова макроса:

PATH_PREFIX_RESOLVE(path, L"\\", L"");

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

Тем не менее, при работе с префиксами L"\\?\\" и L"\\", возможно, все еще существует проблема, заключающаяся в том, что оба могут совпадать - необходимо убедиться, что вы передаете префиксы, которые могут совпадать более одного раза, в 'priority' заказ.

3 голосов
/ 22 марта 2010

Передайте массив по ссылке, используя шаблон для определения длины. Я пойду искать пример, но в основном:

template<size_t N>
bool func(const char (&a)[N], blah, blah) { ... }

РЕДАКТИРОВАТЬ: Кто-то объяснил это здесь: http://heifner.blogspot.com/2008/04/c-array-size-determination.html

1 голос
/ 22 марта 2010

Вы пытались заменить sizeof(prefix)/sizeof(wchar_t) на wcslen(prefix)?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...