简体   繁体   English

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

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

I have a useful macro here: 我在这里有一个有用的宏:

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

}

It's used about 6 times within the scope of a single function (that's it), but macros seem to have "bad karma" :P 在单个函数的范围内仅使用了6次(就是这样),但是宏似乎具有“不良业力”:P

Anyway, the problem here is the sizeof(prefix) part of the macro. 无论如何,这里的问题是宏的sizeof(prefix)部分。 If I just replace this with a function taking a const wchar_t[] , then the sizeof() will fail to deliver expected results. 如果我仅将其替换为带有const wchar_t[]const wchar_t[] ,则sizeof()将无法传递预期的结果。

Simply adding a size member doesn't really solve the problem either. 仅仅添加一个大小成员并不能真正解决问题。 Making the user supply the size of the constant literal also results in a mess of duplicated constants at the call site. 让用户提供常量文字的大小还会导致在调用站点出现一堆重复的常量。

Any ideas on this one? 关于这个有什么想法吗?

Pass the array by reference, using a template to infer the length. 通过引用传递数组,使用模板推断长度。 I'll go looking for an example, but basically: 我将寻找一个示例,但基本上是:

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

EDIT: Someone explained it here: http://heifner.blogspot.com/2008/04/c-array-size-determination.html 编辑:有人在这里解释: http : //heifner.blogspot.com/2008/04/c-array-size-determination.html

Why not just make it a regular function that uses wcslen() to get the length of the parameter you're interested in. There's enough stuff going on in that macro/function that I imagine there's little value trying to force it to be inlined. 为什么不让它成为使用wcslen()来获取您感兴趣的参数的长度的常规函数​​呢?在该宏/函数中发生了很多事情,我想尝试强制将其内联没有什么价值。 The 'overhead; “开销”; of the wcslen() call and processing is almost certainly not going to be a bottleneck. wcslen()调用和处理的过程几乎肯定不会成为瓶颈。

The only trick in the macro that you really should be concerned with is (as GMan pointed out) the return from the within the macro that's hidden when the macro is invoked. 您真正应该关注的宏中的唯一技巧是(如GMan所指出的)调用宏时隐藏的宏中的返回。

Just have the thing be a function that returns a success/fail, and you can return if the function succeeds: 只要让事物是一个返回成功/失败的函数,就可以在函数成功的情况下返回:

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

to use the function: 使用功能:

//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;

Given what the processing that's occurring in the macro, I don't think you need to be worried about function call overhead. 鉴于宏中正在发生的处理,我认为您不必担心函数调用的开销。

Also, your macro implementation has some behavior which I think is probably a bug - if the path starts with L"\\\\?\\\\" that means it also starts with L"\\\\" and your first invocation of the macro: 另外,您的宏实现具有某些行为,我认为这可能是一个错误-如果路径以L"\\\\?\\\\"开头,这意味着它也以L"\\\\"开头,并且您是首次调用该宏:

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

will change the path variable. 将更改path变量。 As the program gets maintained and additional prefixes get added, the problem could be seen with other path prefixes. 随着程序的维护和其他前缀的添加,使用其他路径前缀可能会出现问题。 This bug isn't in the function version, since the function changes the path parameter only when there's a verified match. 该错误不在函数版本中,因为该函数仅在存在经过验证的匹配项时才更改path参数。

However, there's still possibly an issue when dealing with the L"\\\\?\\\\" and L"\\\\" prefixes in that both might be a match - you need to make sure you pass in the prefixes that might match more than once in 'priority' order. 但是,在处理L"\\\\?\\\\"L"\\\\"前缀时仍然可能存在问题,因为两者可能是匹配的-您需要确保传递的匹配前缀可能不止一次按照“优先”顺序。

您是否尝试过用wcslen(prefix)替换sizeof(prefix)/sizeof(wchar_t) wcslen(prefix)吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 排除宏,我可以使用什么,如 C++ 中的内联 function - Exclude Macro, What can I use like inline function in C++ 是否可以在不影响性能的情况下将此宏更改为内联函数? - Can I change this macro to an inline function without a performance hit? 如何用支持__LINE__和__FILE__的内联函数替换我的c ++异常宏? - How can I replace my c++ exception macro with an inline function with __LINE__ and __FILE__ support? 如何将此宏转换为功能模板? - How do I convert this macro to a function template? 如何导出宏函数并在项目 .exe 中使用它 - How can i export a macro function and use it in a project .exe 我可以使用流操作符重写日志宏以使用C ++模板函数吗? - Can I rewrite a logging macro with stream operators to use a C++ template function? 如何使用模板模板类型作为函数参数? - How can I use template template type as an function argument? 如何在模板 function 中使用不同的结构作为模板参数? - How can I use different struct as template argument in a template function? 为什么函数模板的显式实例化不使用内联或constexpr - Why can explicit instantiation of a function template not use inline or constexpr 如何重构容器以直接使用谓词方法? - how can I refactor a container to use a predicate method directly?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM