C++ 如何重构它以使用内联函数或模板而不是宏?

C++ 如何重构它以使用内联函数或模板而不是宏?,c++,macros,C++,Macros,我这里有一个有用的宏: #include <algorithm> #include <vector> #include <string> #include <boost/algorithm/string.hpp> #include <Windows.h> namespace Path { bool Exists(const std::wstring& path) { DWORD result = GetFileAtt

我这里有一个有用的宏:

#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;
}

}
#包括
#包括
#包括
#包括
#包括
名称空间路径{
布尔存在(常量std::wstring和path)
{
DWORD result=GetFileAttributesW(path.c_str());
返回结果!=无效的\u文件\u属性;
}
//这就是问题中的宏!
#定义路径\前缀\解析(路径、前缀、环境)\
if(boost::algorithm::istart_with(path,prefix)){\
ExpandEnvironmentStringsW(环境、缓冲区、最大路径)\
替换(0,(sizeof(前缀)/sizeof(wchar__t))-1,缓冲区)\
如果(存在(路径))返回路径\
}
std::wstring解析(std::wstring路径)
{
使用名称空间boost::算法;
wchar_t缓冲区[最大路径];
修剪(路径);
如果(path.empty()| | Exists(path))返回路径;
//首先尝试查看是否有引用的路径
如果(路径[0]==L'){
返回std::wstring(path.begin()+1,std::find(path.begin()+1,path.end(),L');
}
//检查路径开头没有根的严重情况
路径前缀解析(路径,L“\\”,L“);
路径前缀解析(路径,L“?\”,L“);
路径前缀解析(路径,L“\?\”,L“);
路径前缀解析(路径,L“globalroot\\”,L“”);
路径\前缀\解析(路径,L“system32\\”,L“%systemroot%\\system32\\”;
路径前缀解析(路径,L“systemroot\”,L“%systemroot%\\”;
静态std::向量路径exts;
if(pathExts.empty()){
#定义MAX_ENVVAR 32767
wchar_t pathext[MAX_ENVVAR];
DWORD长度=GetEnvironmentVariable(L“PATHEXT”,PATHEXT,MAX_ENVVAR);
如果(!length)WindowsApiException::ThrowFromLastError();
拆分(pathExts,pathext,std::bind2nd(std::equal_to(),L';');
insert(pathExts.begin(),std::wstring());
}
std::wstring::迭代器currentSpace=path.begin();
做{
currentSpace=std::find(currentSpace,path.end(),L“”);
std::wstring currentPath(path.begin(),currentSpace);
std::wstring::size_type currentPathLength=currentPath.size();
typedef std::vector::const_迭代器ExtIteratorType;
对于(ExtIteratorType it=pathExts.begin();it!=pathExts.end();it++){
替换(currentPathLength,currentPath.size()-currentPathLength,*it);
如果(存在(currentPath))返回currentPath;
}
if(currentSpace!=path.end())
currentSpace++;
}while(currentSpace!=path.end());
返回路径;
}
}
它在一个函数的范围内使用了大约6次(就是这样),但宏似乎有“坏业力”:p

无论如何,这里的问题是宏的
sizeof(prefix)
部分。如果我只是用一个取
常量wchar\u t[]
的函数替换它,那么
sizeof()
将无法提供预期的结果

简单地添加一个size成员也不能真正解决问题。让用户提供常量文本的大小也会导致调用站点出现大量重复的常量


对此有什么想法吗?

通过引用传递数组,使用模板推断长度。我会去找一个例子,但基本上:

template<size_t N>
bool func(const char (&a)[N], blah, blah) { ... }
模板
bool func(const char(&a)[N],诸如此类,诸如此类){…}
编辑:有人在这里解释:
通过引用传递数组,使用模板推断长度。我会去找一个例子,但基本上:

template<size_t N>
bool func(const char (&a)[N], blah, blah) { ... }
模板
bool func(const char(&a)[N],诸如此类,诸如此类){…}
编辑:有人在这里解释:
您是否尝试过将
sizeof(prefix)/sizeof(wchar\u t)
替换为
wcslen(prefix)

您是否尝试过将
sizeof(prefix)/sizeof(wchar\u t)
替换为
wcslen(prefix)
获取您感兴趣的参数的长度。在宏/函数中有足够多的东西在运行,我想试图将其内联是没有什么价值的。“管理费用”;调用和处理
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"");
将更改
路径
变量。随着程序的维护和附加前缀的添加,其他路径前缀可能会出现问题。此错误不在函数版本中,因为函数仅在存在已验证匹配时才更改路径参数


但是,在处理
L“\\?\\”
L“\\”
前缀时,仍然可能存在一个问题,因为这两个前缀可能都是匹配的-您需要确保按“优先级”顺序传递可能匹配多次的前缀。

为什么不将其作为使用
wcslen()的常规函数呢
获取您感兴趣的参数的长度。在宏/函数中有足够多的东西在运行,我想试图将其内联是没有什么价值的。“管理费用”;调用和处理
wcslen()
几乎肯定不会成为瓶颈

在宏中,您真正应该关注的唯一技巧是(正如GMan指出的)调用宏时从宏中隐藏的返回

让这个东西成为一个函数t