C++中两个宏__PRETTY_FUNCTION__和__FUNCSIG__的应用

一、辅助宏

一般在开发者中,像__FUNCTION__(PRETTY_FUNCTION__和__FUNCSIG),__LINE__和__FILE__这几个宏,都会比较熟悉。当然在新的c++17以后提供了本地库std::source_location来取代这些宏,不过可以预见的是,可能在未来的相当长的时间内二者可能是并存的。在实际的工程开发应用中,为了更好的定位问题,往往会有各种宏来进行辅助工作。这也是宏编程为什么饱爱诟病却迟迟不能被赶出来的一个原因。
不过整体上来看,标准还是朝着减少使用宏的方向在发展,但还是那句话,从理论到现实,是有非常大的差距的。

二、函数辅助宏的说明

1、FUNCSIG
看名字就很容易明白,函数的签名,其实就是得到函数的声明形式,不过可能比代码稍微多一点调用方式,它的应用更类似于用某种工具软件去看库的导出函数签名,这样可能就比较好理解了。一般开发过动态库的都用用过类似于Depends。所以需要说明的,这个是应用在VC的环境下的。什么意思,这不是标准的应用宏。其实在VC中还有一个类似的宏__FUNCDNAME__,它也是用来得到函数的修饰名

2、PRETTY_FUNCTION
这个看名字也好理解,好看的,那就意味着好理解的。毕竟,对于任何漂亮的东西,人都是愿意去理解的。它其实和上面的__FUNCSIG__类似,只不过它应用于GCC,是其的一个扩展宏。既然是扩展的,肯定功能比标准的要多一些,这也算是夹带私货,编译器厂商经常做的,理解并支持,毕竟对开发者有利。
它会把函数名称(包括成员函数)和类名及参数列表都打出来。对模板的支持也非常到位,这个就看实际的应用需求情况了。

3、func__和__FUNCTION
说了前面的几个宏,这就又涉及到一个问题,既然都是取得同样或者类似的结果,标准就不统一一下么,回答是肯定的,其实C99定义了__func__,注意是小写的啊,它其实不是一个宏,而一个隐式静态的常量字符串数组:

static const char __func__[] = "function-name";

但是反过来,虽然大家把C和c++经常混在一起编程,可人家标准可是分别由不同的标准委员会制定。所以c++对这个支持的不太好,弄了一个__FUNCTION__。这个就得看未来标准的发展和相互兼容的情况来定吧。

三、应用

说多少不如看一个简单例程,更清晰明白:


#include <iostream>

#ifdef _WIN64
#define __FUNC__ __FUNCSIG__
#else
#define __FUNC__  __PRETTY_FUNCTION__
#endif

void Display()
{
    std::cout << __FUNC__ << ": " << std::endl;
    std::cout << "===================="  << std::endl;
}

template<typename T, typename... Args>
void Display(T t, Args... args)
{
    std::cout << __FUNC__ << ": " << t << std::endl;
    Display(args...);
}

class Example
{
public:
    void TestFunc()
    {
        std::cout << " call __FUNCTION__ or __func__: "<< std::endl;
        std::cout << " __FUNCTION__: " << __FUNCTION__ << std::endl;
        std::cout << "====================" <<  std::endl;
        std::cout << " __func__: " << __func__ << std::endl;
        std::cout << "====================" << std::endl;
        std::cout << "__FUNCDNAME__" << __FUNCDNAME__ << std::endl; //VC可以 GCC不可以
    }
};
namespace T
{
    class  Test
    {
    public:
        template <class T>
        static void run(int i)
        {
            std::cout << "call run int" << std::endl;
            std::cout << __func__ << std::endl;
            std::cout << __FUNCTION__ << std::endl;
            std::cout << __FUNC__ << std::endl;
            std::cout << "__FUNCDNAME__" << __FUNCDNAME__ << std::endl;//VC可以 GCC不可以
        }
        template <class T>
        static void run(double d)
        {
            std::cout << "call run double" << std::endl;
            std::cout << __func__ << std::endl;
            std::cout << __FUNCTION__ << std::endl;
            std::cout << __FUNC__ << std::endl;
        }
        template <>
        static void run<double>(double d)
        {
            std::cout << "call run p double" << std::endl;
            std::cout << __func__ << std::endl;
            std::cout << __FUNCTION__ << std::endl;
            std::cout << __FUNC__ << std::endl;
        }
        template <class T>
        static void run(T d)
        {
            std::cout << "call run stand" << std::endl;
            std::cout << __func__ << std::endl;
            std::cout << __FUNCTION__ << std::endl;
            std::cout << __FUNC__ << std::endl;
        }
    };
}


int main()
{
    Example e;
    e.TestFunc();
    std::cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl;
    std::cout << " call __PRETTY_FUNCTION__ or __FUNCSIG__: " << std::endl;
    Display(1.2, 3, 'O', 666, "hello world!");

    std::cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl;
    T::Test::run<std::string>("sss");
    T::Test::run<void>(2.2);
    T::Test::run<int>(6);

    std::cout << " call __FILE__ and __LINE__: " << std::endl;
    std::cout << "cur file name: "<<__FILE__ << "cur line is:"<<__LINE__<< std::endl;
    return 0;
}

看一下在VC环境下的运行结果:

__func__: TestFunc
====================
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
call __PRETTY_FUNCTION__ or __FUNCSIG__:
void __cdecl Display<double,int,char,int,const char*>(double,int,char,int,const char *): 1.2
void __cdecl Display<int,char,int,const char*>(int,char,int,const char *): 3
void __cdecl Display<char,int,const char*>(char,int,const char *): O
void __cdecl Display<int,const char*>(int,const char *): 666
void __cdecl Display<const char*,>(const char *): hello world!
void __cdecl Display(void):
====================
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
call run stand
run
T::Test::run
void __cdecl T::Test::run<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >>(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)
call run double
run
T::Test::run
void __cdecl T::Test::run<void>(double)
call run int
run
T::Test::run
void __cdecl T::Test::run<int>(int)
call __FILE__ and __LINE__:
cur file name: D:\vs2022_project\MacroTest\MacroTest.cppcur line is:93

再看一下在GCC环境下的运行结果:

$ ./mt
 call __FUNCTION__ or __func__:
 __FUNCTION__: TestFunc
====================
 __func__: TestFunc
====================
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 call __PRETTY_FUNCTION__ or __FUNCSIG__:
void Display(T, Args ...) [with T = double; Args = {int, char, int, const char*}]: 1.2
void Display(T, Args ...) [with T = int; Args = {char, int, const char*}]: 3
void Display(T, Args ...) [with T = char; Args = {int, const char*}]: O
void Display(T, Args ...) [with T = int; Args = {const char*}]: 666
void Display(T, Args ...) [with T = const char*; Args = {}]: hello world!
void Display():
====================
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
call run stand
run
run
static void T::Test::run(T) [with T = std::__cxx11::basic_string<char>]
call run double
run
run
static void T::Test::run(double) [with T = void]
call run int
run
run
static void T::Test::run(int) [with T = int]
 call __FILE__ and __LINE__:
cur file name: mt.cppcur line is:94

应该明白很多吧。可以看一下c++最新的标准库提供的std::source_location:
https://zh.cppreference.com/w/cpp/utility/source_location

在B站上,有人用__PRETTY_FUNCTION__这个宏实现了一些编译期的反射,有兴趣可以去看看:
https://www.bilibili.com/video/BV1Ur4y1V7Kh/?spm_id_from=333.999.0.0&vd_source=9ecfde55da4f7ca8d09afdab1b1006e4

四、总结

这种东西很简单,但也很实用。如果利用好了,可以实现一些技巧性的东西,比如上面的B站的那个视频中的应用。同时,也正是这些基础性的东西,反而更可以清楚的看出来不同的标准,不同的编译器,同一标准的不同时代的演进,让开发者可以更明白其中发展的过程而不至于大脑中一片乱乎乎的东西。
大道至简,越是简单的东西,大多数人更容易接受。然后能不能自己外延,就看个人的本事了。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值