This question描述了如何使用__PRETTY_FUNCTION__
获取函数的全名,包括其返回类型,参数类型,命名空间和模板参数。
考虑以下,美丽的功能:
namespace foo {
namespace {
template<int i>
int (*bar(int (*arg)(int *)))(int *) {
printf("%s\n", __PRETTY_FUNCTION__);
return arg;
}
} // anonymous namespace
} // namespace foo
如果您不明白,该函数将获取并返回指向int *
- &gt;的指针。 int
功能。
当使用g++
(4.9)编译时,其漂亮的名称是
int (* foo::{anonymous}::bar(int (*)(int*)))(int*) [with int i = 1337]
和clang++
(3.5),
int (*foo::(anonymous namespace)::bar(int (*)(int *)) [i = 1337])(int *)
这些字符串非常不适合测试函数是否属于某个命名空间。有没有其他方法,或者说,编译器提供的库来解析这些字符串?
为了澄清,我宁愿拥有像
这样的东西foo::{anonymous}::bar <I don't care about anything beyond this point>
更理想的情况是,我喜欢编译时的方式,例如constexpr
函数split(__PRETTY_FUNCTION__)
,它会产生某种列表
等,但我对完全合格的功能名称感到满意。
答案 0 :(得分:4)
经过仔细观察,我写了这段代码:
template <typename InputIterator, typename T>
InputIterator findClosing( InputIterator first, InputIterator last, T close )
{
if (first == last)
return last;
auto open = *first;
unsigned counter = 1;
while (++first != last)
{
if (*first == close && --counter == 0)
return first;
if (*first == open)
++counter;
}
return last;
}
template <std::size_t N,
std::size_t N2>
std::string f(char const(&str)[N], char const(&name)[N2])
{
using namespace std;
// Argument to isalnum must be unsigned:
auto cond = [] (unsigned char c) {return !isalnum(c) && c != '_';};
auto iter = str;
for (;;++iter)
{
iter = search( iter, end(str),
begin(name), end(name)-1 );
if (iter == end(str))
throw invalid_argument("");
if ((iter == begin(str) || cond(iter[-1]))
&& (iter == end(str) - N2 || (cond(iter[N2-1]) && iter[N2-1] != ':')))
break;
}
auto origin_iter = iter;
while(iter != begin(str))
{
--iter;
for (auto p : {"()", "{}"})
if (*iter == p[1])
iter = findClosing(reverse_iterator<char const*>(iter+1),
reverse_iterator<char const*>(begin(str)),
p[0]).base()-2;
if (cond(*iter) && *iter != ':')
return string(iter+1, origin_iter+N2-1);
}
return string(iter, origin_iter+N2-1);
}
它应该适用于任何函数,假设__PRETTY_FUNCTION__
中不存在不必要的空格,__func__
仅包含非限定函数名。
答案 1 :(得分:3)
这并不是你所要求的,因为它没有返回一个constexpr
字符串的值,但是它接近了。但是,它完全是constexpr
,它返回一个指向命名空间开头的指针,以及指向它结尾的指针(函数名的开头),以及可选的字符串长度。 / p>
constexpr bool isNotIdentifierChar(const char *pf)
{
return !isalnum(*pf) && *pf!='_';
}
constexpr const char* getNamespaceEnd(const char *pf, const char *func)
{
return (isNotIdentifierChar(pf) && 0==strncmp(&pf[1], func, strlen(func))
&& isNotIdentifierChar(pf+strlen(func)+1) && ':'!=pf[strlen(func)+1])
? &pf[1]
: getNamespaceEnd(++pf, func);
}
constexpr const char* getNamespaceStartIter(const char *pf, const char *end)
{
return (*pf==' ' && strchr(&pf[1], ' ') > end)
? &pf[1]
: getNamespaceStartIter(++pf, end);
}
constexpr const char* getNamespaceStart(const char *pf, const char *func)
{
return getNamespaceStartIter(pf, getNamespaceEnd(pf, func));
}
constexpr size_t getNamespaceSize(const char *pf, const char *func)
{
return getNamespaceEnd(pf, func) - getNamespaceStart(pf, func);
}
一个人不能constexpr
返回std::string
因为std::string
(或任何等效的构造)有一个非平凡的析构函数,但我们可以返回开始和结束像这样的命名空间:
printf("%s\n", std::string(
getNamespaceStart(__PRETTY_FUNCTION__, __func__),
getNamespaceEnd(__PRETTY_FUNCTION__, __func__)+strlen(__func__)
).data());
此版本包含功能名称(本例中为“bar”),但也可以省略+strlen(__func__)
。
我们也可以通过使用一个简单的类来使它更清晰:
class NamespaceString {
public:
NamespaceString(const char *pf, const char *func)
: start(getNamespaceStart(pf, func)),
end(getNamespaceEnd(pf, func)) {}
std::string getString() const {
return std::string(start, end);
}
private:
const char *start;
const char *end;
};
然后在函数中使用更清晰:
static const NamespaceString ns(__PRETTY_FUNCTION__, __func__);
printf("%s\n", ns.getString().data());
更新了演示,其中包括@ Columbo的答案和这一个:live code