• 摆脱printf的噩梦


    众所周知,printf是一个方便、直观、易写、变长参数的打印函数,但它有一个致命的缺陷,如下的语句将导致程序出现严重的运行时错误:

    printf("%s", 1);
    

    然后程序中断,程序员深夜被电话叫醒……噩梦被打断以后陷入另一个噩梦。深究以后发现,幕后黑手是C语言变长参数的传递问题。

    1. 没有传递参数类型。

    2. 没有传递参数个数。

    当然,在C语言里边还是可以解决这个问题滴,但……却无可避免地失去了printf的优雅。C++改善了吗?失望……

    直到C++终于释出了新的变元参数传递的标准。由Kenny Kerr(1)和sasha(2) 提出了设想。在github中,找到了一个解决方案 tinyformat(3)。

    但事情还没有结束,要把tinyformat用于log4cpp中,为使用vc2010编程的项目提供一个的安全可靠的日志模块。尤其是对于服务程序来说,日用不能停。

    封装log4cpp

    log4cpp有一大堆配置语句,使用起来不是很方便,参照Lan'Sir(4)的文章进行如下封装:

    class LogBase
    { 
    private:
    	static LogBase *m_pInstance;
    	log4cpp::Category *m_pRoot;
    protected: 
    	LogBase() 
    	{
    		m_pRoot = &log4cpp::Category::getRoot();
    		m_pRoot->setPriority(log4cpp::Priority::INFO);
    	} 
    private:
    	LogBase& operator=(LogBase&); 
    	LogBase(const LogBase&); 
    
    public: 
    	~LogBase()
    	{ 
    		if ( m_pRoot != NULL) { delete m_pRoot; m_pRoot = NULL; } 
    		if ( m_pInstance != NULL) { delete m_pInstance; m_pInstance = NULL; }
    	}
    	static LogBase* getInstance()
    	{  
    		if( m_pInstance == NULL) m_pInstance = new LogBase();
    
    		return m_pInstance;
    	}
    	void log(log4cpp::Priority::Value priority, const std::string& message)
    	{
    		if (m_pRoot != NULL) m_pRoot->log(priority, message);
    	}
    };
    
    LogBase* LogBase::m_pInstance = NULL;
    
    

    使用tinyformat

    令人郁闷的是vs2010支持C98而不是C11。幸好tinyformat作者已经做了一些工作——用宏定义循环设置了1-16个变元的函数。

    // C++98 version
    void log_info(const char* s)
    {
    	std::string m = s;
    	LogBase *pInstance = LogBase::getInstance();  
    	if (pInstance != NULL) pInstance->log(log4cpp::Priority::INFO, s);	
    }
    #define MAKE_LOG_INFO_FUNC(n)	
    template<TINYFORMAT_ARGTYPES(n)>    
    void log_info(const char* fmt, TINYFORMAT_VARARGS(n))	
    {	
    	std::string m = tfm::format(fmt, TINYFORMAT_PASSARGS(n));	
    	LogBase *pInstance = LogBase::getInstance();  
    	if (pInstance != NULL) pInstance->log(log4cpp::Priority::INFO, m);	
    }
    TINYFORMAT_FOREACH_ARGNUM(MAKE_LOG_INFO_FUNC)
    

    现在就可以试一试了:

    int _tmain(int argc, _TCHAR* argv[])
    {
    	log_info("hello %s!", 555);
    	log_info("hello world%s!");
    	return 0;
    
    }
    

    Tips

    1. 在自己的项目中同时使用jsoncpp和log4cpp的lib

      分别编译lib时,需将属性.C/C++.代码生成.运行库统一。例如都设置为“多线程/MT”或者都设置为“多线程调试/MTD”。

      在不同类型的运行库中存在同样的一些函数名,连接在一起时将出现重复定义和定义冲突错误。

      具体内容可参考文章:“关于VC link2005 重复定义,定义冲突的错误解决”(5)以及“配置自己的OpenGL库,glew、freeglut库编译,库冲突解决(附OpenGL Demo程序)”(6)

    2. 消除警告:未找到 PDB“vc100.pdb”

      编译jsoncpp和log4cpp的lib时,部分调试信息被放在 vc100.pdb 文件中。编译链接自己的项目时出现找不到 vc100.pdb 的告警信息。

      一般情况下,在调试自己的项目时都不会跟踪到jsoncpp和log4cpp的内部。如果真的怀疑它们有问题,也是到它们各自的项目中进行调试。因此,简单合理的解决办法是在编译lib时,关闭调试信息。

      属性.C/C++.常规.调试信息格式设置为空。

      如果在自己的项目中仍然希望保留lib的这些调试信息,要解决文件名冲突的问题。详见“warning LNK4099: 未找到 PDB”(7)

    3. 消除警告:无法解析的外部符号 __imp__WSACleanup

      编译log4cpp的lib时可能会用到ws2_32.lib。详见“无法解析的外部符号 __imp__WSACleanup@”(8)。

      在添加头文件处,添加引用:

    #pragma comment(lib,"ws2_32.lib")
    

    参考链接:

    1. Windows with C++ - Using Printf with Modern C++

    2. Compile-Time and Runtime-Safe Replacement for “printf”

    3. https://github.com/c42f/tinyformat

    4. Linux混合编程+log4cpp

    5. 关于VC link2005 重复定义,定义冲突的错误解决

    6. 配置自己的OpenGL库,glew、freeglut库编译,库冲突解决(附OpenGL Demo程序)

    7. warning LNK4099: 未找到 PDB“vc120.pdb”

    8. 无法解析的外部符号 __imp__WSACleanup@

  • 相关阅读:
    docker一些基本操作
    Error requesting socket: exit status 255(一个很不错的解决办法)【转】
    十五周至十八周的任务进度
    7月24号day16总结
    7月23号day15总结
    7月22号day14总结
    7月21号day13总结
    7月20号day12总结
    7月19日day11总结
    7月18号day10总结
  • 原文地址:https://www.cnblogs.com/ccpaging/p/5443156.html
Copyright © 2020-2023  润新知