从 class T 获取函数名称 (__func__) 和指向成员函数 void(T::*pmf)() 的指针
Getting a function name (__func__) from a class T and a pointer to member function void(T::*pmf)()
是否可以编写一些 f()
模板函数,它采用类型 T
和指向签名 void(T::*pmf)()
成员函数的指针作为(模板 and/or 函数)参数和 returns 指向成员函数的 __func__
变量(或损坏的函数名称)的 const char*
?
编辑:我被要求解释我的用例。我正在尝试编写一个单元测试库(我知道有一个用于此目的的 Boost Test 库)。 我的目标是完全不使用任何宏:
struct my_test_case : public unit_test::test {
void some_test()
{
assert_test(false, "test failed.");
}
};
我的测试套件运行器将调用 my_test_case::some_test()
,如果它的断言失败,我希望它记录:
ASSERTION FAILED (&my_test_case::some_test()): test failed.
我可以使用 <typeinfo>
来获取 class 的名称,但是指向成员函数的指针只是一个偏移量,它不会向用户提供关于测试函数的任何线索打电话。
TL;DR:除了使用宏之外,不可能以合理的可移植方式执行此操作。使用debug symbols确实是一个硬解,会在以后引入维护和架构问题,而且是不好的解决方案。
不保证函数名称以任何形式存储在二进制文件中[或其他任何地方]。静态自由函数当然不必将它们的名称暴露给世界其他地方,并且虚拟成员函数也没有真正需要暴露它们的名称(除非 vtable 在 A.c 中形成并且成员函数在B.c).
链接器也完全允许删除函数和变量的所有名称。共享库可以使用名称来查找二进制文件中不存在的函数,但如果系统正在使用该方法,"ordinal" 方法也可以避免这种情况。
除了将 assert_test
设为宏之外,我看不到任何其他解决方案 - 这实际上是一个很好的宏用例。 [好吧,您当然可以将 __func__
作为参数传递,但这肯定不会比在这种有限情况下使用宏更好。
类似于:
#define assert_test(x, y) do_assert_test(x, y, __func__)
然后实现 do_assert_test
来做你原来的 assert_test
会做的事情 [减去找出函数名称的不可能的部分]。
如果它是单元测试,并且您可以确定您将始终使用调试符号来执行此操作,则可以通过使用调试符号构建然后使用调试界面找到您当前所在的函数的名称。我说它不可移植的原因是给定 OS 的调试 API 不是标准的 - Windows 是一种方式,Linux 另一个,我不确定它在 Mac 中是如何工作的OS - 更糟糕的是,我对该主题的快速搜索似乎表明读取调试符号没有 API 因此 - 有一个调试 API 可以让你检查当前进程并弄清楚你在哪里,寄存器包含什么等等,但不能找出函数的名称。所以这绝对是比 "convince whoever needs to be convinced that this is a valid use of a macro" 更难的解决方案。
看来您要实现的目标是获取 assert_test()
中调用函数的名称。使用 gcc 你可以使用
backtace
做到这一点。这是一个天真的例子:
#include <iostream>
#include <execinfo.h>
#include <cxxabi.h>
namespace unit_test
{
struct test {};
}
std::string get_my_caller()
{
std::string caller("???");
void *bt[3]; // backtrace
char **bts; // backtrace symbols
size_t size = sizeof(bt)/sizeof(*bt);
int ret = -4;
/* get backtrace symbols */
size = backtrace(bt, size);
bts = backtrace_symbols(bt, size);
if (size >= 3) {
caller = bts[2];
/* demangle function name*/
char *name;
size_t pos = caller.find('(') + 1;
size_t len = caller.find('+') - pos;
name = abi::__cxa_demangle(caller.substr(pos, len).c_str(), NULL, NULL, &ret);
if (ret == 0)
caller = name;
free(name);
}
free(bts);
return caller;
}
void assert_test(bool expression, const std::string& message)
{
if (!expression)
std::cout << "ASSERTION FAILED " << get_my_caller() << ": " << message << std::endl;
}
struct my_test_case : public unit_test::test
{
void some_test()
{
assert_test(false, "test failed.");
}
};
int main()
{
my_test_case tc;
tc.some_test();
return 0;
}
编译:
g++ -std=c++11 -rdynamic main.cpp -o main
输出:
ASSERTION FAILED my_test_case::some_test(): test failed.
注意:这是一个 gcc (linux, ...) 解决方案,可能很难移植到其他平台!
是否可以编写一些 f()
模板函数,它采用类型 T
和指向签名 void(T::*pmf)()
成员函数的指针作为(模板 and/or 函数)参数和 returns 指向成员函数的 __func__
变量(或损坏的函数名称)的 const char*
?
编辑:我被要求解释我的用例。我正在尝试编写一个单元测试库(我知道有一个用于此目的的 Boost Test 库)。 我的目标是完全不使用任何宏:
struct my_test_case : public unit_test::test {
void some_test()
{
assert_test(false, "test failed.");
}
};
我的测试套件运行器将调用 my_test_case::some_test()
,如果它的断言失败,我希望它记录:
ASSERTION FAILED (&my_test_case::some_test()): test failed.
我可以使用 <typeinfo>
来获取 class 的名称,但是指向成员函数的指针只是一个偏移量,它不会向用户提供关于测试函数的任何线索打电话。
TL;DR:除了使用宏之外,不可能以合理的可移植方式执行此操作。使用debug symbols确实是一个硬解,会在以后引入维护和架构问题,而且是不好的解决方案。
不保证函数名称以任何形式存储在二进制文件中[或其他任何地方]。静态自由函数当然不必将它们的名称暴露给世界其他地方,并且虚拟成员函数也没有真正需要暴露它们的名称(除非 vtable 在 A.c 中形成并且成员函数在B.c).
链接器也完全允许删除函数和变量的所有名称。共享库可以使用名称来查找二进制文件中不存在的函数,但如果系统正在使用该方法,"ordinal" 方法也可以避免这种情况。
除了将 assert_test
设为宏之外,我看不到任何其他解决方案 - 这实际上是一个很好的宏用例。 [好吧,您当然可以将 __func__
作为参数传递,但这肯定不会比在这种有限情况下使用宏更好。
类似于:
#define assert_test(x, y) do_assert_test(x, y, __func__)
然后实现 do_assert_test
来做你原来的 assert_test
会做的事情 [减去找出函数名称的不可能的部分]。
如果它是单元测试,并且您可以确定您将始终使用调试符号来执行此操作,则可以通过使用调试符号构建然后使用调试界面找到您当前所在的函数的名称。我说它不可移植的原因是给定 OS 的调试 API 不是标准的 - Windows 是一种方式,Linux 另一个,我不确定它在 Mac 中是如何工作的OS - 更糟糕的是,我对该主题的快速搜索似乎表明读取调试符号没有 API 因此 - 有一个调试 API 可以让你检查当前进程并弄清楚你在哪里,寄存器包含什么等等,但不能找出函数的名称。所以这绝对是比 "convince whoever needs to be convinced that this is a valid use of a macro" 更难的解决方案。
看来您要实现的目标是获取 assert_test()
中调用函数的名称。使用 gcc 你可以使用
backtace
做到这一点。这是一个天真的例子:
#include <iostream>
#include <execinfo.h>
#include <cxxabi.h>
namespace unit_test
{
struct test {};
}
std::string get_my_caller()
{
std::string caller("???");
void *bt[3]; // backtrace
char **bts; // backtrace symbols
size_t size = sizeof(bt)/sizeof(*bt);
int ret = -4;
/* get backtrace symbols */
size = backtrace(bt, size);
bts = backtrace_symbols(bt, size);
if (size >= 3) {
caller = bts[2];
/* demangle function name*/
char *name;
size_t pos = caller.find('(') + 1;
size_t len = caller.find('+') - pos;
name = abi::__cxa_demangle(caller.substr(pos, len).c_str(), NULL, NULL, &ret);
if (ret == 0)
caller = name;
free(name);
}
free(bts);
return caller;
}
void assert_test(bool expression, const std::string& message)
{
if (!expression)
std::cout << "ASSERTION FAILED " << get_my_caller() << ": " << message << std::endl;
}
struct my_test_case : public unit_test::test
{
void some_test()
{
assert_test(false, "test failed.");
}
};
int main()
{
my_test_case tc;
tc.some_test();
return 0;
}
编译:
g++ -std=c++11 -rdynamic main.cpp -o main
输出:
ASSERTION FAILED my_test_case::some_test(): test failed.
注意:这是一个 gcc (linux, ...) 解决方案,可能很难移植到其他平台!