• Ogre 源码分析之 LogManager


    Ogre LogManager 分析

     

    设计上从名字就可以推断出来LogManager使用了单例模式,这样为LogManager的全局访问提供了非常大的帮助。

    下面是LogManager的继承结构:

     

                

     

    继承结构:

     

    Ogre::Singleton<LogManager>

     

    我们之前有对OgreSingleton模式进行过分析,这里不再赘述。详见 Ogre 设计模式之Singleton

     

    LogAlloc

     

    本来我想把LogAlloc附带分析了,但是分析代码的过程中,发现其内部的复杂度已经超过我目前的C++和多线程的理解水平,所以未来抽时间单独的把LogAlloc分析一下。

     

    现在先把LogAlloc概述一下,希望大牛们看到这篇文章可以指点一二。

     

    LogAllocGeneralAllocatedObject的别名,而GeneralAllocatedObject 是 AllocatedObject的一种通用分配策略(GeneralAllocPolicy),这种采基于Policy的设计,在 Modern C++ Design 里面有详细的讲解。

     

    AllocatedObject 中重载了 new , new[], delete , delete[] 等诸多方法,我们会看到new的很多重载版本,我们这里讲解一个: 

     

    void* operator new[] ( size_t sz, const char* file, int line, const char* func )
    
    {
    
    return Alloc::allocateBytes(sz, file, line, func);
    
    }

    讲到这里不得不先提一下Ogre LogManager的初始化方式,如下:

     

    mLogManager = OGRE_NEW LogManager();
    
    mLogManager->createLog(logFileName, true, true);

     

    OgreOGRE_NEW 宏,如下:

     

    # define OGRE_NEW new (__FILE__, __LINE__, __FUNCTION__)

     

    OGRE_NEW 的 __FILE__, __LINE__, __FUNCTION__ (这三个参数是编译器内部定义的宏,记录出错的文件,行和函数三个参数在 调用 OGRE_NEW  XXX[] 时传入 operator new

     

    这样做的目的是可以为了定位错误所用,当new失败的时候,需要throw的时候,Ogre会把抛出的错误,以写入日志或以弹出窗口的方式来告知用户。

     

    参数是Alloc是模板参数,这里的具体参数是 GeneralAllocPolicy ,它是CategorisedAllocPolicy<Ogre::MEMCATEGORY_GENERAL> 的别名,而 CategorisedAllocPolicy 定义如下:

     

    template <MemoryCategory Cat> class CategorisedAllocPolicy : public NedPoolingPolicy{};

    继承的基类 NedPoolingPolicy 采用了 nedmalloc 的方式,它是多线程的,带锁,其实nedmalloc 也是线程缓存式的内存池。nedmalloc 的确没有看懂,希望大牛指点一二。

    这里的模板参数要注意,它是enum类型的,指示需要制定的内存分配策略。

    好了 LogAlloc 就介绍到这里,关于Ogre的内存分配,我会以后花时间详细分析一下。

     

    LogManager的成员和方法:

     

    回到LogManager的主题(囧 不知不觉感觉刚才有点跑题)LogManager有很多辅助方法如 getter/setter ,这种不再赘述,重点讲解一下LogManager的以下三种方法:

     

    1. Log* createLog();

     

    2. void destroyLog();

     

    3.  void logMessage();

     

    1. createLog()  方法:

     

    Log* LogManager::createLog( const String& name, bool defaultLog, bool debuggerOutput, 
    
    bool suppressFileOutput)
    
    {
    
        OGRE_LOCK_AUTO_MUTEX
    
     
    
            Log* newLog = OGRE_NEW Log(name, debuggerOutput, suppressFileOutput);
    
     
    
            if( !mDefaultLog || defaultLog )
    
            {
    
                mDefaultLog = newLog;
    
            }
    
     
    
            mLogs.insert( LogList::value_type( name, newLog ) );
    
     
    
            return newLog;
    
     } 

    LogManager中的方法几乎都要加锁,createLog也不例外。这里使用了Ogre使用了Ogre独特的加锁方式,这种加锁方式很好的避免了重入和加锁之后忘记解锁的缺点。内部的实现已经类似于局部的类,开始的时候构造加锁,最后离开函数析构解锁。

     

    因为可以Ogre允许创建多个log(默认只有一个),创建之后插入mLogs,它是一个LogList,LogList的定义如下:

     

    typedef map<String, Log*>::type LogList;

     

    使用map的原因在于想把keyvalue对应起来,这个用处在 destroy() 方法中将会有体现。最后这个方法新创建的Log指针。

     

    2. void destroyLog(); 方法

     

    destroyLog() 重载了两种方式,第一种是通过string,另一种是通过Log指针。但是第二种是通过获得Log指针的name,来调用第一种,所以本质上说是一种方法。

     

      void LogManager::destroyLog(const String& name)
        {
            LogList::iterator i = mLogs.find(name);
            if (i != mLogs.end())
            {
                if (mDefaultLog == i->second)
                {
                    mDefaultLog = 0;
                }
                OGRE_DELETE i->second;
                mLogs.erase(i);
            }
    
            // Set another default log if this one removed
            if (!mDefaultLog && !mLogs.empty())
            {
                mDefaultLog = mLogs.begin()->second;
            }
        } 

    简单明了,查找和字符串相同名字的log,然后用OGRE_DELETE删除掉,如果是删除当前默认的Log,那么选择删除后map的第一个作为默认的log

     

    3.  void logMessage(); 方法

     

    这个是LogManager里面最重要的方法,方法中调用了mLog成员变量的logMessage

    方法,这样做主要是把log的具体实现,主要是文件操作,和manager分离开来,达到manager是独立的效果。

     

    在这个方法中需要注意的有以下三点:

     

    A) 关于log报告的等级,如下代码将会根据你需要的等级对log进行写入。

     

           if ((mLogLevel + lml) >= OGRE_LOG_THRESHOLD)

    B) 这里log的实现使用了观察者模式,如果需要监听log 就需要继承 LogListener,并实现抽象方法 messageLogged() ,然后调用addListner()方法把需要监听的类的指针注册进来,存放在一个vector里面,当每次需要写入log的时候,logMessage()方法就遍历vector,调用对应监听者的messageLogged() 的方法。

     

    C Ogre会根据你的需要写入控制台还是file中,而且每次是即时写,即输出到对应目标,调用内嵌类的 Stream (本质是个 basic_string )的对象 mLog 的 flush() 方法。

     

    好了,今天LogManager就分析到这里,下一次分析Ogre SDK 的一个例子或者 Ogre 的 DyLibManager

     

  • 相关阅读:
    HDU 1258 Sum It Up(Dfs)
    HDU 1501 Zipper(Dfs记忆化搜索)
    HDU 1075 What Are You Talking About (字典树)
    HDU 1251 统计难题(字典树)
    HDU 1518 Square(Dfs)
    %与mod的区别
    有向无向欧拉回路通路的判断
    HDU 1104 Remainder(BFS打印路径+数论)(%与mod的区别)
    写给还在怀念骑士(3.0 2.0 Online 私服)的刀狼
    HUD 1312 Red and Black(用深搜写的)
  • 原文地址:https://www.cnblogs.com/singmelody/p/2765532.html
Copyright © 2020-2023  润新知