簡體   English   中英

我如何重構它以使用內聯函數或模板而不是宏?

[英]How can I refactor this to use an inline function or template instead of a macro?

我在這里有一個有用的宏:

#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[]const wchar_t[] ,則sizeof()將無法傳遞預期的結果。

僅僅添加一個大小成員並不能真正解決問題。 讓用戶提供常量文字的大小還會導致在調用站點出現一堆重復的常量。

關於這個有什么想法嗎?

通過引用傳遞數組,使用模板推斷長度。 我將尋找一個示例,但基本上是:

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

編輯:有人在這里解釋: http : //heifner.blogspot.com/2008/04/c-array-size-determination.html

為什么不讓它成為使用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變量。 隨着程序的維護和其他前綴的添加,使用其他路徑前綴可能會出現問題。 該錯誤不在函數版本中,因為該函數僅在存在經過驗證的匹配項時才更改path參數。

但是,在處理L"\\\\?\\\\"L"\\\\"前綴時仍然可能存在問題,因為兩者可能是匹配的-您需要確保傳遞的匹配前綴可能不止一次按照“優先”順序。

您是否嘗試過用wcslen(prefix)替換sizeof(prefix)/sizeof(wchar_t) wcslen(prefix)嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM