?
Solved

Simple Class Design Q

Posted on 2006-05-25
17
Medium Priority
?
292 Views
Last Modified: 2013-11-20
Application has the following classes and member functions:

CAccount:
GetAddressDetails() & GetAccountName()

CBank:
GetAccount(CString& a)
AddAccount(CString& b)
DeleteAccount(CString& c)
CreateLogFile()
WriteLog(CString& d)

The problem is that I want to create one logfile and be able to write log data using the "WriteLog" method in both CAccount and CBank classes. How do I do this? How about creating a 3rd object, say, CLogFile, or maybe use a pointers. Can it be achieved this way?







0
Comment
Question by:XPUSR
  • 5
  • 4
  • 3
  • +4
17 Comments
 

Author Comment

by:XPUSR
ID: 16764718
Also, how could I implement the log file for a static method?
0
 
LVL 11

Expert Comment

by:Deepu Abraham
ID: 16766664
Yes you can create a 3rd class called CEventLog

CAccount
{

........
CEventLog objEventLog; //use objEventLog.WriteLog(..) inside the class functions since it is private.
........
public:
CAccount();
~CAccount();
GetAddressDetails()
GetAccountName()
};

CBank
{
........
CEventLog objEventLog;
........
public:
CBank();
~CBank();
GetAccount(CString& a);
AddAccount(CString& b);
DeleteAccount(CString& c);

};

CEventLog
{
CString strFilename;
public:
CEventLog();
CEventLog(CString _strFilename);
SetLogFile(CString _strFilename);
~CEventLog();
WriteLog(CString& d); //It will be good to make this function thread safe if mutiple threads
                                 //are accessing your log file.

private:
CreateLogFile(); //Call this inside the constructor CEventLog()
}

Hope I get across!

Best Regards,
DeepuAbrahamK
0
 
LVL 22

Expert Comment

by:mahesh1402
ID: 16766799
You may also Grab this Read Class 'CLogFile' src and extend it...

http://www.viksoe.dk/code/logfile.htm <==

-MAHESH
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16766801
class CAccount
{
public:
  CAccount(CLogFile* pLogFile);

protected:
  CLogFile* m_pLogFile;
....
}

CAccount::CAccount(CLogFile* pLogFile)
{
  ASSERT(pLogFile);  //stop user just passing NULL
  m_pLogFile = pLogFile;
...
}

Similar for CBank


Create and open one file for logging, now create your CAccount and CBank objects (based on that log file) and you write to the SAME log file when you want.
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16766804
To use for a static member function you need to pass the pointer to the log file directly as part of the call

eg.
static void MyFunc(CLogFile* pLogFile, ....)
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 16767748
And there is also the very simple technique that is usally overlooked by C++ zealots...  I use this technique in code that I know will not be in a published library...

THe logging operation is typically an application-wide service that you want to be available throughout your program... so place it at that level!  Add your CMyLogger object pointer to your CWinApp-derived object.

class CMyApp : public CWinApp
{
public:
      CMyApp();
      CMyLogger *m_pLogger;

...

Instatiate it in InitInstance,

BOOL CMyApp::InitInstance()
{
      AfxEnableControlContainer();
      CString sLogFile= gMyDir+ CSTR_LOGFILENAME;
      m_pLogger= new CMyLogger( sLogFile );
...

Then any class object can do something like:

void AnyOldObject::WriteToLog( LPCSTR sText )
{
      if ( theApp.m_pLogger && theApp.m_pLogger->fLoggingIsEnabled ) {
            gpLogger->Write( sText );
      }
}
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16790889
>>>> logging operation is typically an application-wide service

if so, you should make it a singleton like the application object.

// logfile.h

class LogFile
{
     CString                  m_logfilePath;
     CRITICAL_SECTION m_cs;         // use it if in case of multi-threading
public:
     LogFile( const CString& logfilePath)
          : m_logfilePath(logfilePath) { InitializeCriticalSection(&m_cs); }
     void write(const CString& message);
};

// the one and only logfile object
extern LogFile theLogFile;


// logfile.cpp

#include "stdafx.h"
#include "logfile.h"

// the one and only logfile object
LogFile theLogFile("logfile.log");

void LogFile::write(const CString& message)
{
      EnterCriticalSection(&m_cs);
      // will throw an exception on fail
      TRY
      {
               CStdioFile logfile(m_logfilePath, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite | CFile::typeText);
               logfile.WriteString(message);
               logfile.Close();
           }
      CATCH (CFileException,  pfe)
      {
              LeaveCriticalSection(&m_cs);
              THROW(pfe);
              return;
       }
       END_CATCH
       LeaveCriticalSection(&m_cs);
 }

// anyothersource.cpp

#include "stdafx.cpp"
#include "logfile.h"


   ....
   theLogFile.write("Hello Logfile\n");

Regards, Alex
0
 
LVL 2

Expert Comment

by:seet82
ID: 16804576
juz a bit of correction to itsmeandnobodyelse,
the above codes does not really follows the design pattern singleton
the characteristic for singleton is that there can and will only be 1 instance of the class
the correct implementation for singleton follows the following pattern

public class Singleton {
    private:
        static Singleton *instance;
        Singleton() //note the use of private constructor
        {
             //do your initialization of your class here
        }
    public:
        static Singleton getInstance()
        {
            if (instance==NULL)
            {
                instance=new Singleton();
            }
            return instance;
        }
}

Singleton::instance=NULL;

when using the singleton class, you will call the getInstance function for the singleton class, ie
Singleton::getInstance()
however, you will need to call delete the instance of singleton manually to prevent memory leak
0
 

Author Comment

by:XPUSR
ID: 16845646
Thanks,
What would the structure of the Singleton be for a log file? Should the getInstance() method be subsituted with a createLogFile(CString cstrFileName) method???
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 375 total points
ID: 16868175
>>>> the above codes does not really follows the design pattern singleton

Note, as seet82 correctly told there is a design pattern named 'singleton' which assures that you can't create more than one instance of a class if following the pattern. However, beside of the design pattern the word 'singleton' stands for any class that has only one instance regardless whether the class design assures that property or not. In case of Logfile I wouldn't recommend to make it a singleton class as there is no reason why you shouldn't use two or more Logfile instances. I only used the term 'singleton' because MFC framework already has a few 'singleton' classes, e. g. your application class or the CMainFrame class. So, if you would need only one logfile you could do it the same way as MFC does it with the application singleton.

>>>> What would the structure of the Singleton be for a log file?

class LogFile
{
     CString                  m_logfilePath;
     CRITICAL_SECTION m_cs;         // use it if in case of multi-threading
     static LogFile*        m_pTheLogFile;

     LogFile( const CString& logfilePath)
          : m_logfilePath(logfilePath) { InitializeCriticalSection(&m_cs); }
public:
     static LogFile& getInstance()
     {
          if ( m_pTheLogFile == NULL)
              return  *m_pTheLogFile = new LogFile("logfil.txt");
         return  *m_pTheLogFile;
     }
     void write(const CString& message);
};

// logfile.cpp

   LogFile* LogFile::m_pTheLogFile = NULL;


You would use it like


    LogFile::getInstance().write("Hello World");


As told above, a logfile object doesn't need to be a singleton.

Regards, Alex

0
 

Author Comment

by:XPUSR
ID: 16868477
>> DanRollins

>>And there is also the very simple technique that is usally overlooked by C++ zealots...  I use this technique in code that I know will not be in a published library...

>>THe logging operation is typically an application-wide service that you want to be available throughout your program... so place it at that level!  Add your CMyLogger object pointer to your CWinApp-derived object.

I am having difficulty implementing this method, specifically accessing the m_pLogger from another class.  

When do the following:

if ( theApp.m_pLogger && theApp.m_pLogger->fLoggingIsEnabled )

I get an error stating

 error C2065: 'theApp' : undeclared identifier
 error C2227: left of '.m_plogger' must point to class/struct/union

This also occurs even when I declare (and try to access it in a different class) a simple test variable in the CMyApp header, say "int x".
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16868534
You need the extern keyword.

eg in your .cpp file.
extern int DefinedElsewhere;    //similar for the log file



ps.
Don't forget one of the reasons for using C++ is to allow one to only access variables where they are required (member vars of classes, and pointers to them).  Using an application global variable this way goes against that methodology.  

I would advise passing the pointer to the log file directly via functions.  It can cope easily with one or multiple log files.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16871377
>>>> error C2065: 'theApp' : undeclared identifier

normally 'theApp' is only defined properly - or better as expected by Dan and me - if the MFC application was using the MFC framework and was created by using the Application Wizard and is an SDI or MDI application. Then, your CWinApp derived application class has a (global) extern definition of the single instance of that class at end of the application header:

//myapp.h


....

class CMyApp : public CWinApp
{
  ...
};

// the one and only application instance

extern CMyApp theApp;

....

// myapp.cpp

...

// here 'the one and only application instance' was defined
CMyApp theApp;

...


In all other cpp you have access to the 'theApp' global variable by including myapp.h or declaring it as extern again:

   extern CMyApp theApp;

If your application header has no extern declaration but theApp was defined in the application cpp, you simply could add the extern declaration after class definition.


Note, as Andy explained global variables is a C technique (which there are many in MFC). In C++ you could realize singletons by using static member functions as showed above.  Note, that doesn't apply to 'theApp' cause it was used by MFC sources.

For other singletons you could/should add them as static members in the class itself:

class MySingleton
{
  ...

public:
     static CMySingleton m_mySingleton;
};

to the class definition and define it in the cpp

// mysingleton.cpp

    CMySingleton CMySingleton::m_mySingleton;

You would need to specifiy the scope CMySingleton:: when using it but it is a good C++ way to replace global instances.

Regards, Alex
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 16873204
You can also access the CWinApp-derived object as follows:

in any module:

   #include "MyApp.H"       // the wizard probably already added this for you

    ....

    CMyApp* pMyApp = (CMyApp*) AfxGetApp();

    int y= pMyApp->x;
    pMyApp->LogMsg( "hi!");
0
 

Author Comment

by:XPUSR
ID: 16897384
itsmeandnobodyelse

Regarding the Singleton implementation:
Where should the Open(CString filename, CString filepath) method be placed - in the constructor?
Why would stop one doing the following:

LogFile logfile("c:/directory/");

logfile.write("Bla bla bla");





0
 

Author Comment

by:XPUSR
ID: 16898339
sorry, that should read what should stop one doing the following:
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16900187
In my implementations of a logfile I open and close the logfile in the write function. That way I can open the logfile with an editor while the program is still running (otherwise you might get sharing violations). Furthermore, if writing to the logfile in a multi-threaded program you need to make synchronization or the call might hang up. Closing the file with each log additionally flushes the io buffer, so your logfile is always up-to-date.

>>>> what should stop one doing the following:
>>>> LogFile logfile("c:/directory/");

You could try to create (or open and append) the file given in the constructor. Maybe you want to write a start message to the logfile (timestamp) as well. If file creation fails you could call assert() to throw a debug assertion or you might throw a C++ exception if the logfile can be wrong in release mode also. If you don't like exceptions you could add a bool member 'isvalid' to your Logfile class and set it to false if file creation fails. Then, after Logfile creation you could check validity:

    LogFile logfile("c:/directory/");
    if (!logFile.isValid())
    {
          cout << "logfile " << logFile.getLogfilePath() << " can't be opened." << endl;
          return 2;
    }
   
Regards, Alex

 
0

Featured Post

Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this article, I'll describe -- and show pictures of -- some of the significant additions that have been made available to programmers in the MFC Feature Pack for Visual C++ 2008.  These same feature are in the MFC libraries that come with Visual …
Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
As many of you are aware about Scanpst.exe utility which is owned by Microsoft itself to repair inaccessible or damaged PST files, but the question is do you really think Scanpst.exe is capable to repair all sorts of PST related corruption issues?
Suggested Courses

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question