Jump to content
  • Advertisement
Sign in to follow this  

[Free Code] A Simple Module Class

This topic is 4816 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

I came up with this an hour ago to solve a maintanence nightmare I was having with modules. Each module (i.e. renderer, texture manager, scene graph, etc) was having to keep a private variable called m_init that checked to see if the module has successfully been started up instead of having the parent module keep track of it (which is just as awkward). So, I came up with a base class that all my modules now derive from that manages this entirely, and it's proven so useful that I thought I would share. Note that I am still in the process of changing over all my modules to this format so it hasn't yet been tested. Module.h
/* (C) Sami Hamlaoui 2004-2005
*/

#ifndef MODULE_H
#define MODULE_H

#if _MSC_VER > 1000
#pragma once
#endif  // _MSC_VER > 1000

/*
====================================================================================================
Classes
====================================================================================================
*/

/*
==========
Module
==========
*/

class Module
{

public:

    //--- Ctors/Dtors ---//

    Module ();
    virtual ~Module ();

    //--- Functions ---//

    bool Init ();
    void Shutdown ();

    const std::string &GetModuleName ();

protected:

    //--- Functions ---//

    virtual bool OnInit () = 0;
    virtual void OnShutdown () = 0;

    bool GetModuleInit ();
    void SetModuleName (const std::string &name);

private:

    //--- Functions ---//

    void SetModuleInit (const bool status);

    //--- Variables ---//

    bool m_module_init;
    std::string m_module_name;
};

#endif  // MODULE_H



Module.cpp
/* (C) Sami Hamlaoui 2004-2005
*/

/*
====================================================================================================
Headers
====================================================================================================
*/

#include "..\..\Global.h"
#include "Module.h"

/*
====================================================================================================
Public Functions
====================================================================================================
*/

/*
==========
Module
==========
*/

Module::Module () : m_module_init (false)
{
}

/*
==========
~Module
==========
*/

Module::~Module ()
{
    Shutdown ();
}

/*
==========
Init
==========
*/

bool Module::Init ()
{
    // See if the module has already been initialised.
    if (GetModuleInit ())
    {
        // It has, so report an error and exit.
        error.Report ("Module \"" + GetModuleName () + "\" is already initialised.", ERROR_ACTION_NONE, ERROR_LOCATION);
        return false;
    }

    logfile << GetModuleName () << " - Initialising..." << std::endl;

    // We have attempted to enter initialisation. This is added here so OnInit can call public functions without
    // everything going wrong.
    SetModuleInit (true);

    // See if the module started up successfully.
    if (OnInit ())
    {
        logfile << GetModuleName () << " - Finished." << std::endl;
        return true;
    }

    else
    {
        // Reset this to false as OnInit failed.
        SetModuleInit (false);

        // It wasn't so write an error message
        logfile << GetModuleName () << " - Initialisation failed. Program will begin to exit." << std::endl;

        // Call the child's OnShutdown function to clear up any mess.
        OnShutdown ();

        // Pass back the error.
        return false;
    }
}

/*
==========
Shutdown
==========
*/

void Module::Shutdown ()
{
    // If this module hasn't been initialised yet then exit here. It is perfectly legal to call Shutdown () on
    // an initialised module (that is the point of this function). However, we should NEVER call OnShutdown ()
    // unless GetModuleInit () returns true.
    if (!GetModuleInit ())
        return;

    logfile << GetModuleName () << " - Shutting down." << std::endl;

    // Call the child's shutdown function.
    OnShutdown ();

    // We're not initialised anymore :).
    SetModuleInit (false);

    // Do not reset m_module_name as the module might be started up again,
    // and m_module_name should be set in the constructor.
    logfile << GetModuleName () << " - Finished." << std::endl;

}

/*
==========
GetModuleName
==========
*/

const std::string &Module::GetModuleName ()
{
    return m_module_name;
}

/*
====================================================================================================
Protected Functions
====================================================================================================
*/

/*
==========
GetModuleInit
==========
*/

bool Module::GetModuleInit ()
{
    return m_module_init;
}

/*
==========
SetModuleName
==========
*/

void Module::SetModuleName (const std::string &name)
{
    m_module_name = name;
}

/*
====================================================================================================
Private Functions
====================================================================================================
*/

/*
==========
SetModuleInit
==========
*/

void Module::SetModuleInit (const bool status)
{
    m_module_init = status;
}



Users Guide to follow in next post... [Edited by - MENTAL on March 15, 2005 8:46:21 AM]

Share this post


Link to post
Share on other sites
Advertisement

Users Guide


Creating a new Module


Create a class that inherits the public interface of Module. Do not forget to add OnInit and OnShutdown implementations as they are defined as pure virtual in the Module class.

Your Module's Constructor


In you module's constructor, call SetModuleName with the name of your module. This should be the friendly name (e.g. "Error Manager") and not the class name (e.g. "CErrorManager"). Use classnames if you want, but log files are easier to read with friendly names.

That's all you need to do in regards to the Module class. After that just do the normal constructor stuff.

Your Module's Destructor


Empty if you desire, as Module::~Module will call Module::Shutdown, which will call Module::OnShutdown if it needs to be. Simple eh?

Writing OnInit


This is where your initialisation code goes. You do not need to check to see if the module has already been initialised as this function will ONLY be called if GetModuleInit is false.

If you encounter an error, do not worry about freeing any memory (unless it's temporary memory). Just output a message to the log file and return false. It's as simple as that.

If everything goes to plan then return true.

Writing OnShutdown


OnShutdown will only be called if a) OnInit returned false, or b) if Shutdown was called after OnInit returned true. Use this function to clear up the mess you made in OnInit, free any resources, and all the usual stuff that happens when you shut down a module.

Other Functions


At the top of each PUBLIC function, just place the line:


if (!GetModuleInit ()) return;







This way you protect yourself against other code. This is called defensive programming. Defensive programming is a Good ThingTM.

For protected and private functions, you do not need to add this line, as they can only be called by public functions which are already protected. If you use friends (fool) however, then you may well need to protect yourself with this line.

Using the Module


Just call the module's Init and Shutdown (not OnInit or OnShutdown). These functions will peform a single check and then call OnInit or OnShutdown respective if it passes.

I'm a Loser, Give me an example!


Hypothetical graphics engine:


class Graphics : public Module
{

public:

Graphics ();

void PublicFunction ();

protected:

bool OnInit ();
void OnShutdown ();

private:

void PrivateFunction ();
};

Graphics::Graphics ()
{
SetModuleName ("Graphics Engine");
}

void Graphics::PublicFunction ()
{
if (!GetModuleInit ()) return;

PrivateFunction ();
}

bool Graphics::OnInit ()
{
if (!window.Init ())
return false;

if (!texturemgr.Init ())
return false;

if (!renderer.Init ())
return false;

return true;
}

void Graphics::OnShutdown ()
{
renderer.Shutdown ();
texturemgr.Shutdown ();
window.Shutdown ();
}

void Graphics::PrivateFunction ()
{
int sum = 1 + 1;
}






Note that this assumes that window, renderer and texturemgr are also derived from Module (which they should be).

The only drawback I have found with this system is that you can not pass parameters to the Init function, but if you're clever then you will be storing all your settings in a centralised configuration manager :).


Sorry for all the spelling and grammar mistakes. English is my first language, but I'm just far too lazy to check it.

Share this post


Link to post
Share on other sites
It seems strange to be calling onShutdown if onInit fails. In C++, the destructor is not called if the constructor fails!

You've managed to control the order of construction (initiation) of essentially global objects, but not their destruction. Does ShutDown() properly set the initiated flag so that the destructor doesn't call shutdown again?

Share this post


Link to post
Share on other sites
This isn't constructors and destructors. This is user-called Init and Shutdown. There are some instances where you may need to shut down a module and have to start it back up again (changing screen resolutions for example).

And as you see in Shutdown, SetModuleInit (false) is called which will prevent Shutdown happening twice (it will enter, but then exit straight away).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!