[英]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.