Sign in to follow this  

'Application Data' Directory

This topic is 3773 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hiya, With the help from some people here on GameDev I've written a simple logging class for my project. I've just found that it won't work on Vista though (suprise suprise) because it needs to write to the user's local Application Data folder. As I want to be able to reuse this class, my solution (so far) is to use a class I've written called ConfigFile to open an INI file, and read out the name of the application, so I can write to the correct directory. For example, if the application is called Test1, it should write to "C:\...\Application Data\Test1\Log.txt". My question is, is it OK in this case to store the name of the application as a static member of the logging class, so I don't have to open, parse, and close the INI file every time I need to log something? It's not too much overhead if I do have to, but it just seems cleaner to store the name somewhere. I ask because a lot of people have said using static members like this are as bad as using globals, so I've been avoiding them - I just can't work out another way, though. I hope that was clear enough! Any opinions/suggestions are much appreciated, thanks. James.

Share this post


Link to post
Share on other sites
It's fine.

Until the day you'll need to log into two different files. Then you'll add one more static member.

And that'll work until you'll need to log to 3 different files, and selectively filter levels.

There's nothing wrong with storing the name, or even holding the file open for the duration of the application (although that is debatable). But declaring it static will prevent any ability to extend your logging needs. And then, since you'll have based your entire design around a global instance, you'll find that your entire code-base is locked into that functionality.

Why not just pass an instance of a logging class (containing your file functionality) to where you want to do the logging?

Share this post


Link to post
Share on other sites
Thanks for yet another quick reply Antheus.

The logging class I have is this:

class Logger
{
public:
Logger( const std::wstring fileName = L"Log.txt" )
{ m_out.open( fileName.c_str(), std::ios::app ); }
~Logger() { m_out.flush(); m_out.close(); }

template < typename T >
inline Logger& operator<< ( const T &t )
{ m_out << t; return *this; }

private:

/* here is where I'm wondering if I can store the app. data path, so I can prepend it to the filename in the constructor - something like this: */
static std::wstring m_directory;

std::wofstream m_out;
};

// usage:
void main()
{
Logger::Logger() << L"Some text.";
}




If I store the directory, then the constructor can do something like this:

Logger::Logger( std::wstring fileName = L"Log.txt" )
{ fileName.insert( 0, m_directory ); m_out.open( fileName.c_str() ); }


Is that an acceptable solution?
Thanks again.

Share this post


Link to post
Share on other sites
Why static?

Here's the thing. If you do things statically, you'll run into problems, since you won't specify the context for your logs.

Just tie everything to an instance:

class Logger
{
public:
Logger( const std::string &filename );
private:
std::string filename;
}


No static, no nothing. But, it will cost you about 3 extra characters each time you want to log something....

If you then still wish to reserve resources, then define loggers globally.

static Logger engineLogger("engine.log.txt");
static Logger soundLogger("sound.log.txt");
static Logger gameLogger("game.log.txt");



And from there, it's just a step to abstracting the destinations. Rather than tying loggers to a filename, you define a class representing a destination. FileAppender, DatabaseAppender, CoutAppender, ....

So eventually, you get down to something like this:

void foo()
{
Logger log( coutAppender );
log << Logger::WARNING << "We're barred";
}

or, in your case:

void foo()
{
Logger( coutAppender ) << Logger::WARNING << "We're barred";
}



And then, you can even make a composite appender, which logs to multiple sources. General purpose appender looks something like this:

class Appender
{
virtual void commit( const std::string &logText );
};

Share this post


Link to post
Share on other sites
OK, thanks.

Please forgive me being a bit slow today... can you suggest how to store an application name in the logger, assuming that it's not known at compile-time and needs to be read from a file?

I could read it in every time I log something, but that seems a little wasteful, is all.

Perhaps I'm trying to go about it the wrong way - the problem is that the logger needs to write to a folder in the 'Application Data' directory, and it should also be re-usable in other projects. For one application, it might need to write to "\Application Data\Foo\" and for another it might need to write to "\Application Data\Bar".

I'm trying to think of a way of doing this while still allowing me to instance, write to, and destroy the logger on one line.

Edit:

Quote:
Original post by Spoonbender
Just a little bonus question? Is the application data path hardcoded? (Because it might not be called that in non-english versions of Windows)


No, I find it be calling the Windows API function SHGetFolderPath(). It's the folder within that - which is different for each application - that the logger needs to know about.

Share this post


Link to post
Share on other sites

This topic is 3773 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this