Несколько подходов к классу лесозаготовки


Я делаю персональный логгер и до сих пор я придумать 3 решения. Я их упростить этот пример, чтобы подчеркнуть мою дилемму. Я хочу знать плюсы и минусы свои подходы и способы сделать их лучше. Также я должен быть промывочный поток как можно чаще и почему?

Файлы templog

Эта версия будет создан и уничтожен на каждый журнал. Завершающий вынуждены на уничтожение.

class TempLog
{
public:
    std::ostream& log()
    {
        return std::cout;
    }
    ~TempLog()
    {
        std::cout << "\n";
    }
};

SingletonLog_ver_1

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

class SingletonLog_ver_1
{
public:
    static SingletonLog_ver_1 instance()
    {
        static SingletonLog_ver_1 instance;
        return instance;
    }

    std::ostream& log()
    {
        static bool firstLine = true;
        if (firstLine)
            firstLine = false;
        else
            std::cout << "\n";

        return std::cout;
    }

private:
    SingletonLog_ver_1() {};
};

SingletonLog_ver_2

Сейчас пока мне нравится то, что здесь происходит больше, чем за предыдущие два примера, кажется менее элегантный код. Есть еще более элегантный способ добиться такой функциональности?

class SingletonLog_ver_2
{
public:
    static SingletonLog_ver_2 instance()
    {
        static SingletonLog_ver_2 instance;
        return instance;
    }

    std::pair<std::ostream&, std::unique_ptr<TempObj_NewLine>> log()
    {   
        return std::pair<std::ostream&, std::unique_ptr<TempObj_NewLine>>(std::cout, std::make_unique<TempObj_NewLine>());
    }

private:
    SingletonLog_ver_2() {};
};

TempObj_NewLine

class TempObj_NewLine
{
public:
    TempObj_NewLine() {};
    ~TempObj_NewLine()
    {
        std::cout << "\n";
    }
};

Использование 3 лесозаготовителей:

int main()
{
    TempLog().log() << "first log!" << " first log, same line.";
    TempLog().log() << "second log." << "second line.";

    SingletonLog_ver_2::instance().log().first << "first line singleton log!"<< " first line, again.";
    SingletonLog_ver_2::instance().log().first << "second line singleton log!";

    SingletonLog_ver_1::instance().log() << "first line singleton - hacky endline." << " first line, again.";
    SingletonLog_ver_1::instance().log() << "second line singleton - hacky endline";

    std::cin.get();
}


Комментарии
3 ответа

Синглтоны

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

class Log {
...
};

Log logInstance;

Вы тогда просто обратитесь к logInstance вместо SingletonLog::instance().

Правильное именование

Ваш класс имеет функцию log()однако эта функция не регистрирует ничего. Вместо этого он просто возвращает std::ostream ссылка. Более подходящее название для функции будет get_stream().

Если вы намерены всегда использовать его в качестве SingletonLog::instance().log() << "some string"тогда может быть лучше просто добавить operator <<() перегрузка на входе класса. Это имеет несколько преимуществ:


  1. Это меньше набирать: SingletonLog::instance() << "some string"или при использовании альтернативный способ создания синглтона: logInstance << "some string".

  2. Вы на самом деле можете прочитать содержимое строки в свой класс, и проверить, будет ли она заканчивается переводом строки или нет.

Топить

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

Другие предложения

Вы действительно хотите использовать << оператор? Если вы предпочли бы использовать printf() стиль, а затем рассмотреть вопрос об использовании fmtlib, который обеспечивает тип-безопасный способ для форматирования строк printf() или Python .format() путь. Ваш класс ведение журнала может выглядеть так:

#include <fmt/format.h>

class Log {
template <typename... Args>
void log(const std::string &format, const Args &... args) {
std::cout << fmt::format(format, args...);
}
};

Log logInstance;

И использовать его как:

logInstance.log("First line: {}\nSecond line: {}\n", "first entry", "second entry");

3
ответ дан 18 марта 2018 в 02:03 Источник Поделиться

Вы выбрали хорошую реализацию, но затем сделал что-то странное

Ваш Singleton реализация хорошая -это каноническая реализация в C++ в любом случае, его главное ограничение в том, что это не потокобезопасными. Но вы делаете что-то, что кажется бессмысленным, который возвращает копию статический экземпляр, инициализированный внутри instance() функции, в то время как традиционный метод возвращает ссылку. Насколько я понимаю ваш код, у вас столько экземпляров вашего регистратора имеются вызовы instance(). Это рассчитано?

Каноническая форма:

static S& getInstance() { // note the return by reference
static S instance;
return instance;
}

Хорошие трюки, но отсутствие функциональности

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

Я считаю, что вы должны расширить ваш дизайн немного. Синглтон не значит, что вам придется ограничить себя в один регистратор. Например, вы могли бы иметь несколько экземпляров, возвращенных как error_log() (что бы написать std::cerr и автоматически заподлицо), или event_log().

Вам действительно нужно использовать синглтон?

Регистраторы действительно, насколько я знаю, один из более или менее принял этот весьма спорный дизайн модели. Но вы уверены, что вам это нужно? Я пользуюсь подобными std::cout, std::cerr или std::clogпростые глобальные объекты, не встречая странных жучков. Они не будут достаточно хорошо, если мне нужно больше функциональности, как потокобезопасность, время штамповка и такие, но и ваши предложенные реализации. Я под впечатлением, что вам нужно реализовать Синглтон больше, чем в рамках журнала, в то время как должно быть наоборот.

2
ответ дан 19 марта 2018 в 05:03 Источник Поделиться

В то время как третье решение SingletonLog_ver2 предлагает необходимый функционал, это трудно для использования/повторного использования и позволяет за нецелевое использование - назначение SingletonLog_ver_2::instance().log().second и, следовательно, избежать разрушение.

Решение ниже работает с этими вещами, передав его поток.
Я изменил имена классов для лучшего readablility.

class StreamDelegate
{
public:
StreamDelegate(std::ostream& os)
:
os(os)
{}

~StreamDelegate()
{
os << "\n";
}
template<class T>
StreamDelegate& operator<<(T&& output)
{
os << std::forward<T>(output);
return *this;
}

StreamDelegate(const StreamDelegate&) = delete;
StreamDelegate& operator=(const StreamDelegate&) = delete;

StreamDelegate(StreamDelegate&&) = default;
StreamDelegate& operator=(StreamDelegate&&) = default;

private:
std::ostream& os;
};

class SingletonLog
{
public:
static SingletonLog& instance(std::ostream& os)
{
static SingletonLog instance(os);
return instance;
}

class StreamDelegate log()
{

return StreamDelegate(os);
}

private:
SingletonLog(std::ostream& os)
:
os(os)
{
}

std::ostream& os;
};

int main()
{

SingletonLog::instance(std::cout).log() << "First line: first entry. " << "First line: second entry";
SingletonLog::instance(std::cout).log() << "Second line";

std::cin.get();
}

Данное решение было представлено на переполнение стека.

0
ответ дан 18 марта 2018 в 12:03 Источник Поделиться