Как я могу сделать экземпляр моего синглтона const? - PullRequest
0 голосов
/ 19 июля 2009

Я пытаюсь создать оболочку для стандартного класса ведения журналов Ogre (3D-движок с открытым исходным кодом). Я хочу, чтобы он имел тот же синтаксис, что и std::cerr, а также вывод на cerr при работе в Linux. Вот что у меня есть:

#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

Теперь, это работает нормально, и этот синглтон инстанцируется в .cpp:

#include "logger.h"

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

Проблема возникает, когда я хочу использовать синглтон в нескольких заголовках. Я создаю его экземпляр в game3d.h, который включен почти всеми заголовками, подобными этим:

Logger awesomelogger = Logger::getInstance();

К сожалению, это дает множество ошибок о заголовках, пытающихся переопределить awesomelogger.

Я хочу сделать это константой, из-за которой это исчезнет, ​​но это приведет к новым ошибкам. Вот что я попробовал:

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;
}

У меня вопрос: как я могу сделать экземпляр этого класса константным ИЛИ как я могу переписать этот класс, но все еще могу сделать awesomelogger << "output" << s_Stuff << "\n";

Ответы [ 6 ]

1 голос
/ 19 июля 2009

Ваш метод get_instance возвращает Logger *, а не Logger.

1 голос
/ 19 июля 2009

Не относитесь к вашему вопросу, но отметьте, что такие имена, как _LOGGER_H_ и __LogManager_H__ зарезервированы в C ++ - вы не можете использовать их в своем собственном коде. Если вы не понимаете правил, касающихся подчеркивания в начале имени (или двойного подчеркивания в любом месте), не используйте их.

Теперь, что касается вашего вопроса, регистратор явно не постоянен. Классический способ предоставления доступа к синглтону - это вариант:

static Logger* getInstance() { 
    static Logger logger;
    return & logger; 
}
0 голосов
/ 20 июля 2009

Это звучит так:

Проблема возникает, когда я хочу использовать синглтон в нескольких заголовках. Я создаю его экземпляр в game3d.h, в который включены почти все заголовки, подобные этому:

 Logger awesomelogger = Logger::getInstance();

То, что вы создаете новый экземпляр awesomelogger в каждом модуле, который включает game3d.h, и поскольку это объявление имеет внешнюю связь, эти имена будут конфликтовать во время ссылки.

См. Связывание в именах с областью файлов для объяснения правил связывания C ++.

Лучшее решение - обойтись без создания переменной awesomelogger и просто вызывать Logger::getInstance() непосредственно там, где вам нужно.

0 голосов
/ 20 июля 2009

Классический рисунок Сингелтона выглядит так:

#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.

Теперь у нас есть базовый шаблон. Вам нужно что-то похожее на глобальную переменную для доступа к ней. Самый простой способ - обмануть и не иметь глобальный, а использовать локальную ссылку.

Итак, в заголовочном файле вместе с определением класса Logger добавьте следующее.

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

Это объявляет локальную переменную файла в каждой единице компиляции, которая включает заголовок. Это было инициализировано для ссылки на экземпляр регистратора, который является сингелтоном. Поскольку вам необходимо включить файл перед его использованием, он всегда будет объявлен перед любым использованием и, следовательно, гарантированно будет инициализирован в правильном порядке.

В основном файле:

#include "MyLogger.h"
int main()
{
    logger << "Plop";
}
0 голосов
/ 19 июля 2009

Вообще говоря, ваш экземпляр Logger "m_Instance" должен быть закрытым. И ваша функция "GetLogger ()" должна проверить, не был ли создан экземпляр m_Instance (если не создан), а затем вернуть m_Instance.

0 голосов
/ 19 июля 2009

делает указатель const на объект const .

static const Logger * const m_Instance;

вместо

static Logger* m_Instance;

Вам также понадобится много соответствующих исправлений в классе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...