2009-07-19 9 views
0

J'essaie de faire un wrapper pour la classe de journalisation standard d'Ogre (un moteur 3D open source). Je veux qu'il ait la même syntaxe que std::cerr, et qu'il soit également envoyé à cerr sous Linux. Voici ce que j'ai:Comment puis-je créer une instance de mon singleton const?

#ifndef _LOGGER_H_ 
#define _LOGGER_H_ 

#ifndef _XSTRING_ 
    #include <xstring> 
#endif 

#ifndef __LogManager_H__ 
    #include "OgreLogManager.h" 
#endif 

class Logger 
{ 

public: 

    static Logger* m_Instance; 
    static Logger* getInstance() { return m_Instance; } 
    static const Logger& getInstanceConst() { return *m_Instance; } 

    Logger& operator << (const std::string& a_Message) 
    { 
     m_Log.append(a_Message); 
     _CheckNewLine(); 
     return *m_Instance; 
    } 

    Logger& operator << (const char* a_Message) 
    { 
    m_Log += a_Message; 
    _CheckNewLine(); 
    return *m_Instance; 
} 

private: 

    std::string m_Log; 

    Logger() 
    { 
     m_Log = ""; 
    } 

    void _CheckNewLine() 
    { 
     if (m_Log.at(m_Log.size() - 1) == '\n') 
     { 
      Ogre::LogManager::getSingleton().logMessage(m_Log); 

#if OGRE_PLATFORM != PLATFORM_WIN32 && OGRE_PLATFORM != OGRE_PLATFORM_WIN32 

      std::cerr << m_Log; 

#endif 

      m_Log.clear(); 
     } 
    } 
}; 

#endif 

Maintenant, cela fonctionne très bien, et ce singleton est instancié dans un Cpp:

#include "logger.h" 

Logger* Logger::m_Instance = new Logger(); 

Le problème vient quand je veux utiliser le singleton dans plusieurs têtes. J'instancier dans game3d.h, qui est inclus par à peu près tous les en-têtes comme ceci:

Logger awesomelogger = Logger::getInstance(); 

Malheureusement, cela donne plusieurs erreurs sur les en-têtes essayant de redéclarer awesomelogger. Je veux en faire un const, ce qui ferait disparaître cela, mais qui introduit de nouvelles erreurs. Voilà ce que j'ai essayé:

friend Logger& operator << (const Logger& a_Logger, const std::string& a_Message) 
{ 
    a_Logger.m_Log.append(a_Message); 
    a_Logger._CheckNewLine(); 
    return *m_Instance; 
} 

Ma question est: comment puis-je faire une instance de cette classe constante ou comment puis-je réécrire cette classe, mais toujours capable de faire awesomelogger << "output" << s_Stuff << "\n";

+3

Une discussion technique sur la façon de mettre en œuvre un singleton, avec fil de sécurité en C++ se trouve dans cet article: http://www.aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised .pdf –

Répondre

1

ne pas faire avec votre question, mais notez que les noms tels que _LOGGER_H_ et __LogManager_H__ sont reserverd en C++ - vous n'êtes pas autorisé à les utiliser dans votre propre code. Si vous ne comprenez pas les règles concernant les caractères de soulignement au début des noms (ou double soulignement n'importe où), ne les utilisez pas. Maintenant, en ce qui concerne votre question, un enregistreur n'est évidemment pas const. La façon classique pour donner accès à un singleton est une variation sur ce point:

static Logger* getInstance() { 
    static Logger logger; 
    return & logger; 
} 
+0

Neil, je suis curieux de savoir quelle est votre façon "standard" de faire les gardes-tête? – GManNickG

+0

Neil est en train de dire que vos macros _LOGGER_H_ sont réservées en raison du trait de soulignement principal. Lisez ceci pour plus de détails: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier/228797#228797 –

+0

Personnellement j'utilise INC_library/exename_file_H , donc quelque chose comme INC_MYLIB_LOGGER_H. –

0

faire un pointeur const à un const objet.

static const Logger * const m_Instance; 

au lieu de

static Logger* m_Instance; 

Vous aurez besoin de beaucoup de corrections respectives dans la classe aussi.

1

Votre méthode get_instance renvoie Logger * et non Logger.

0

De manière générale, votre instance de Logger "m_Instance" doit être privée. Et votre fonction "GetLogger()" devrait vérifier si "m_Instance" n'a pas été instanciée (sinon instancier) puis retourner m_Instance.

0

Le modèle Singelton classique ressemble à ceci:

#ifndef THORS_ANVIL_MY_LOGGER_H 
#define THORS_ANVIL_MY_LOGGER_H 

class MyLogger 
{ 
    private: 
    // Private Constructor 
    MyLogger(); 
    // Stop the compiler generating methods of copy the object 
    MyLogger(MyLogger const& copy);   // Not Implemented. 
    MyLogger& operator=(MyLogger const& copy); // Not Implemented 

    public: 
    static MyLogger& getInstance() 
    { 
     // The only instance 
     // Guaranteed to be lazy initialized 
     // Guaranteed that it will be destroyed correctly 
     static MyLogger instance; 
     return instance; 
    } 
    template<typename T> 
    MyLogger& operator<<(T const& data) 
    { 
      // LOG 
      return *this; 
    } 
}; 

// continued below. 

Maintenant, nous avons le modèle de base sur la manière. Vous avez besoin de ce qui ressemble à une variable globale pour y accéder. Le plus simple est de tricher et de ne pas avoir de global, mais d'utiliser une référence locale.

Donc, dans le fichier d'en-tête avec votre définition de classe Logger ajouter ce qui suit.

namespace 
{ 
    MyLogger& logger = MyLogger::getInstance(); 
} 
#endif 

Ceci déclare une variable locale de fichier dans chaque unité de compilation qui inclut l'en-tête. Cela a été initialisé pour désigner l'instance de l'enregistreur qui est un singelton. Parce que vous devez inclure le fichier avant de pouvoir l'utiliser, il sera toujours déclaré avant toute utilisation et donc garanti d'être initialisé dans le bon ordre.

Dans le fichier principal:

#include "MyLogger.h" 
int main() 
{ 
    logger << "Plop"; 
} 
0

Il semble de ceci:

Le problème vient quand je veux utiliser le singleton dans plusieurs têtes. J'instancier dans game3d.h, qui est inclus par à peu près tous les en-têtes comme ceci:

Logger awesomelogger = Logger::getInstance(); 

que vous créez une nouvelle instance de awesomelogger dans chaque module qui comprend game3d.h et depuis cette déclaration a un lien externe, ces noms entreront en collision au moment de la liaison. Voir Linkage in Names with File Scope pour une explication des règles de liaison C++.

Une meilleure solution est de se passer de la création de la variable awesomelogger et d'appeler directement Logger::getInstance() directement là où vous en avez besoin.