Engine (1.) - Logování

Rád pozoruji programovací styl ostatních programátorů ale všiml jsem si, že spousta lidí vůbec netestuje chybné inicializace důležitých modulů a v případě že program spadne netuší, v čem může být chyba. Přitom důkladné testování by měla být úplná samozřejmost. Přitom řešení je jednoduché vytvořením třídy pro ukládání informací do logovacího souboru, kterou dnes popíši.

Všechny zdrojáky zde uvedené budou vycházet z mého vlastního enginu tak se nedivte že neposkytuji nějaký funkční program, ale že vždy popíši pouze určitý odladěný modul. Tak jdeme na to. Rozhrání modulu je v hlavičkovém souboru CLog.h jehož celý výpis je zde:

//-----------------------------------------------------------
// MODUL:   CLog.h
// PROJECT:   3D engine
// AUTHORS:   Roman Schulz, rschulz@centrum.cz
//-----------------------------------------------------------


#ifndef _CLog_H_
#define _CLog_H_


class CLog
{
   public:
      CLog() {};
      ~CLog() {};

      void Init(char *FileName);

      void NewLine();
      void NewLine(char *Text, ...);
      void AddLast(char *Text);
      void HexDumpData(char *data, int size);
   protected:
      char m_FileName[127];
};

#endif

V podstatě se jedná pouze o třídu, jejíž nejdůležitější metody jsou NewLine a AddLast. Nejdříve je však potřeba třídu inicializovat zadáním jména výstupního souboru. O to se stará metoda Init, která vytvoří nový soubor nebo vymaže obsah původního souboru pro logování údajů. Samozřejmě nesmí chybět načtení hlavičkových souborů s používanými funkcemi:

//-----------------------------------------------------------
// MODUL:   CLog.cpp
// PROJECT:   3D engine
// AUTHORS:   Roman Schulz, rschulz@centrum.cz
// DESCRIPTION: 
//-----------------------------------------------------------

#include "CLog.h"
#include "CTimer.h"
#include 
#include 
#include 
#include 


//-----------------------------------------------------------
// Name: Init()
// Desc: Fce slouzi k ulozeni jmena souboru a jeho otevreni
//-----------------------------------------------------------
void CLog::Init(char *FileName)
{
   FILE *LOGFile;

   // Ulozi jmeno logovaciho souboru
   strcpy(m_FileName, FileName);

   // Vymaze obsah souboru
   LOGFile = fopen(m_FileName, "wt");

   // Zavre soubor
   fclose (LOGFile);
}

Myslím, že překopírovat tohle zvládne každý, kdo už nějaký ten prográmek v céčku vytvářel. Teď přejdu k něčemu jen o trochu složitějšímu. Jedná se o metody s velmi podobnými funkcemi. Jedná se o metody NewLine a AddLast. Nejjednoduší s těchto 3 metod je NewLine() která do logovacího souboru uloží prázdný řádek, který slouží ke zpřehlednění uložených údajů. Druhá podobná metoda je NewLine(char *Test, ...), která má parametry úplně stejné jako funkce printf() a která slouží k formátování textu a jeho uložení do souboru. Poslední z těchto funkcí je AddLast(char *Text) která slouží k dokončení zprávy uložené v logovacím souboru, například text "OK", "Failed" nebo podobně. Zde je výpis těchto funkcí:

//-----------------------------------------------------------
// Name: NewLine()
// Desc: Sem budeme zapisovat vsechny zpravy
//-----------------------------------------------------------
void CLog::NewLine(char *Text, ...)
{
   FILE *LOGFile;
   char Line[1024];
   va_list ArgP;

   // Konvertuje parametry do retezce
   va_start(ArgP, Text);
   vsprintf(Line, Text, ArgP);
   va_end(ArgP);

   // Pokusi se otevrit zadany soubor
   if ((LOGFile = fopen(m_FileName, "a+")) == NULL)
   {
      return;
   }

   // Ukonci predchozi radek
   putc('\n', LOGFile);

   // Zapis radek do souboru
   fprintf(LOGFile, Line);

   // Uzavreni souboru
   fclose(LOGFile);
}


//-----------------------------------------------------------
// Name: NewLine()
// Desc: Pretizena funkce NewLine pro vlozeni prazdneho radku
//-----------------------------------------------------------
void CLog::NewLine()
{
   FILE *LOGFile;

   // Pokusi se otevrit zadany soubor
   if ((LOGFile = fopen(m_FileName, "a+")) == NULL)
   {
      return;
   }

   // Ukonci radek
   putc('\n', LOGFile);

   // Uzavreni souboru
   fclose(LOGFile);
}

//-----------------------------------------------------------
// Name: AddLast()
// Desc: Touto funkci dokoncime informaci na radku: ...OK
//-----------------------------------------------------------
void CLog::AddLast(char *Text)
{
   FILE *LOGFile;

   // Pokusi se otevrit zadany soubor
   if ((LOGFile = fopen(m_FileName, "a+")) == NULL)
   {
      return;
   }

   // Vloz text na konec posledni radky
   fprintf(LOGFile, Text);

   // Uzavreni souboru
   fclose(LOGFile);
}

Poslední metodou, kterou dnes zmíním, je HexDumpData. Tato metoda slouží k haxadecimálmímu výpisu dat do souboru. Určitě se bude časem hodit každému programátorovi, její parametr je ukazatel do paměti a velikost dat, metoda do logovacího souboru uloží přehlednou tabulku s výpisem dat. Podrobně zde metodu rozebírat nebudu, ale každý kdo o to bude mít zájem si ji může podrobně prozkoumat sám. Jsme přece programátoři a nesmíme se toho bát:-)

//-----------------------------------------------------------
// Name: HexDumpData()
// Desc: Vypise na obrazovku data v HEXa tvaru
// From: www.designcurve.net
//-----------------------------------------------------------
void CLog::HexDumpData(char *data, int size)
{
   int i, bytes_left, bytes = 0;
   unsigned char dump[15];
   FILE *LOGFile;

   // Pokusi se otevrit zadany soubor
   if ((LOGFile = fopen(m_FileName, "a+")) == NULL)
   {
      return;
   }

   if (data && size)
   {
      fprintf(LOGFile, "\n\n");
      fprintf(LOGFile, "-----------------------------------------------------------------------------\n");
      fprintf(LOGFile, "|Address |                     HEX                         |      ASCII     |\n");
      fprintf(LOGFile, "-----------------------------------------------------------------------------\n");

      while (bytes < size)
      {
         memset(dump, 0, sizeof(dump));

         bytes_left = size - bytes;

         if (bytes_left >= 16)
         {
            memcpy(&dump, &data[bytes], 16);

            fprintf(LOGFile, "|%8X| %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X |",
               bytes & 0xFFFFFFF0, dump[0], dump[1], dump[2], dump[3], dump[4], dump[5], dump[6], dump[7],
               dump[8], dump[9], dump[10], dump[11], dump[12], dump[13], dump[14], dump[15]);

            fprintf(LOGFile, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c|\n",
               isprint(dump[0]) ? dump[0] : '.', isprint(dump[1]) ? dump[1] : '.', isprint(dump[2]) ? dump[2] : '.',
               isprint(dump[3]) ? dump[3] : '.', isprint(dump[4]) ? dump[4] : '.', isprint(dump[5]) ? dump[5] : '.',
               isprint(dump[6]) ? dump[6] : '.', isprint(dump[7]) ? dump[7] : '.', isprint(dump[8]) ? dump[8] : '.',
               isprint(dump[9]) ? dump[9] : '.', isprint(dump[10]) ? dump[10] : '.', isprint(dump[11]) ? dump[11] : '.',
               isprint(dump[12]) ? dump[12] : '.', isprint(dump[13]) ? dump[13] : '.', isprint(dump[14]) ? dump[14] : '.',
               isprint(dump[15]) ? dump[15] : '.');

            bytes += 16;
         }
         else if (bytes_left > 0)
         {
            memcpy(&dump, &data[bytes], bytes_left);
            bytes += bytes_left;

            fprintf(LOGFile, "|%8X| ", bytes & 0xFFFFFFF0);

            /* Hex side */
            for (i = 0; i < bytes_left; i++)
               fprintf(LOGFile, "%2X ", dump[i]);

            /* Pad Hex side with spaces */
            for (/* */; i < 16; i++)
               fprintf(LOGFile, "   ");

            /* Separator */
            fprintf(LOGFile, "|");

            /* Ascii side */
            for (i = 0; i < bytes_left; i++)
               fprintf(LOGFile, "%c", isprint(dump[i]) ? dump[i]: '.');

            /* Pad ascii side with spaces */
            for (/* */; i < 16; i++)
               fprintf(LOGFile, " ");

            /* End of line */
            fprintf(LOGFile, "|\n");
         }
      }

      fprintf(LOGFile, "-----------------------------------------------------------------------------\n\n");
      bytes++;
   }

   // Uzavreni souboru
   fclose(LOGFile);
}

Na závěr ještě uvedu příklad použití tohoto modulu:

#include "headers/CLog.h"
CLog Log;

int main()
{
   Log.Init("Log/Log.txt");
   
   ....

   // Vyhleda format odpovidajici pozadavkum
   Log.NewLine("Finding Suitable Pixel Format...");
   if (m_PixelFormat = ChoosePixelFormat(g_hDC, &pfd))
   {
      Log.AddLast("OK");
   }
   else
   {
      Log.AddLast("Failed");
      return false;
   }


   // Nastavi vybrany format
   Log.NewLine("Setting Pixel Format...");
   if (SetPixelFormat(g_hDC, m_PixelFormat, &pfd))
   {
      Log.AddLast("OK");
   }
   else
   {
      Log.AddLast("Failed");
      return false;
   }

   ....
}

A příklad logovacího souboru, který vznikl v tomto modulu:

 --------------------------------------------------
  Project:  3D Graphic Engine                      
  Author:   Roman Schulz, Czech Republic           
  Contact:  rschulz@centrum.cz                     
 --------------------------------------------------

 Processor:
   Frequency:      0 MHz
   Vendor:         GenuineIntel
   Type:           Original OEM
   Family:         Intel Pentium Pro (80686)
   Brand ID:       Intel Celeron M (0.09 µm)
   Model:          Intel Celeron M
   Serial:         No Processor Serial Number

 Processor Features:
   AMD 64bit Architecture:                    NO
   Intel 64bit Architecture (IA64):           NO
   Thermal Monitor:                           YES
   Thermal Monitor And Clock Control:         YES
   Floating Point Unit (FPU):                 YES
   Fast Streaming SIMDExtensions Save/Restore:YES
   Hyper Threading:                           NO
   Multimedia Extensions (MMX):               YES
   Extended Multimedia Extensions (MMX+):     NO
   Streaming SIMD Extensions (SSE):           YES
   Streaming SIMD2 Extensions (SSE2):         YES
   3DNow! Instructions:                       NO
   Enhanced 3DNow! Instructions:              NO

 Initialising Window:
 Registering Window Class...OK
 Creating Window...OK
 Getting a Device Context...OK
 Setting Up Display Mode 1024x768x16...OK

 Initialising OpenGL:
 Finding Suitable Pixel Format...OK
 Setting Pixel Format...OK
 Creating OpenGL Rendering Context...OK
 Activating OpenGL Rendering Context...OK

 Initialising OpenGL Extensions:
 Setting Up Multitexture Support...OK
 Number Of Texture Units...4
 Settings Up Texture Compression Support...OK

 Checking Hardware Acceleration...Supported (ICD)

 OpenGL Features:
   Manufacturer...Intel
   Renderer...Intel Montara-GM
   Version...1.3.0 - Build 4.14.10.3712
   Buffer Mode...Double Buffering
   Color Buffer...16 Bits (5650)
   Depth Buffer...16 Bits
   Stencil Buffer...0 Bits
   Accumulation Buffer...64 Bits (16161616)
   Num Of Auxiliary Buffers...0
   Maximum Texture Size...2048x2048 Pixels
   Maximum Num Of Lights...16
   Supported Extensions:
     GL_ARB_multitexture
     GL_ARB_texture_border_clamp
     GL_ARB_texture_compression
     GL_ARB_texture_cube_map
     GL_ARB_texture_env_add
     GL_ARB_texture_env_combine
     GL_ARB_texture_env_dot3
     GL_ARB_texture_env_crossbar
     GL_ARB_transpose_matrix
     GL_EXT_abgr
     GL_EXT_bgra
     GL_EXT_blend_color
     GL_EXT_blend_func_separate
     GL_EXT_blend_minmax
     GL_EXT_blend_subtract
     GL_EXT_clip_volume_hint
     GL_EXT_compiled_vertex_array
     GL_EXT_cull_vertex
     GL_EXT_fog_coord
     GL_EXT_packed_pixels
     GL_EXT_packed_pixels_12
     GL_EXT_rescale_normal
     GL_EXT_secondary_color
     GL_EXT_separate_specular_color
     GL_EXT_stencil_wrap
     GL_EXT_texture_compression_s3tc
     GL_EXT_texture_env_add
     GL_EXT_texture_env_combine
     GL_EXT_texture_filter_anisotropic
     GL_3DFX_texture_compression_FXT1
     GL_IBM_texture_mirrored_repeat
     GL_NV_texgen_reflection
     GL_WIN_swap_hint

 Initializing Direct Input:
 Creating Direct Input Object...OK
 Creating Keyboard Device...OK
 Creating Mouse Device...OK
 Setting Up Keyboard Cooperative Level...OK
 Setting Up Mouse Cooperative Level...OK
 Setting Up Keyboard Data Format...OK
 Setting Up Mouse Data Format...OK
 Acquiring Keyboard Device...OK
 Acquiring Mouse Device...OK

 Initialising FMOD Sound System:
 Checking DLL Version...OK
 Setting Output Type...OK
 Setting Up Sound Driver...OK
 Setting Up Quality MMX Mixer...OK
 Initializing FMOD Sound System...OK

 FMOD Sound System Features: 
 Selected Output Type: Direct Sound
 Selected Mixer Name: Interpolating Volume Ramping PPro+ MMX Mixer
 Selected Driver: Primární ovladač zvuku
 Checking 3D Sound Support...Failed
 Checking EAX2 Reverb Support...Failed
 Checking EAX3 Reverb Support...Failed
 Number Of Allocated Channels...106
 Number Of Hardware 3D Channels...10

A to je vše. Když program někde spadne, ihned vím v čem je chyba a to je obrovská výhoda logovacího souboru. Věřím, že se najde alespoň někdo kdo ocení tento modul vlastně celý článek a v případě zájmu budu pokračovat dalšími částmi mého enginu. Těším se na vaše reakce pod článkem...

Vyšlo 18.01.2005, v blogu: 0 1 2 3 4 5 6 7 8

Děkuji, že jste se rozhodl(a) přečíst tento článek. Budu rád i za komentář. Pokud Vás tento článek zaujal a rádi byste jej doporučili ostatním, podpořte mně prosím tím, že věnujete minutku svého času a uděláte mi reklamu na linkuj.cz, vybrali.sme.sk či jagg.cz. Přeji příjemné čtení

Poslední články

Diskuse k blogu

Zatím nikdo nevložil komentář. Chcete být první? Přidání příspěvku
©PC-guru.cz 2000-2008 | Optimalizováno pro 1024*768