티스토리 뷰

목차

    반응형

    [디자인패턴] Qt 싱글톤 패턴, 로그 생성 프로그램


    Qt에서 디자인패턴 중 싱글톤 패턴 기반으로 로그 파일을 생성하는 프로그램입니다. Qt도 C++을 기초로 제작된 언어라 싱글톤 패턴 소스는 C++의 것을 그대로 사용했습니다. 특별한 경우가 아닌 이상, C++ 디자인패턴 소스는 Qt에서도 구동됩니다.


    이 포스트의 전체 프로젝트 파일은 아래 링크를 누르시면 받을 수 있습니다.


    [디자인패턴] Qt 싱글톤패턴 프로젝트 - SingletonFileIO.zip [링크]


    [디자인패턴] Qt 싱글톤 패턴, 로그 생성 프로그램[Singleton design pattern Qt]


    우선, 실행 화면부터 보시죠. 메인 화면에는 메시지 레벨 3단계가 표시됩니다. UI는 꽤 단순하죠? 로그를 생성할 때, 메시지 레벨이 존재합니다. 위의 버튼에 보이는 ERROR, WARNING, INFORMATION으로 총 3개입니다.


    디자인패턴 프로그램 코드상에서 레벨을 지정해 Qt Creator의 디버그 창에서만 메시지를 찍을 것인지, 해당 내용을 로그 파일에 남길 것인지 결정할 수 있습니다.


    그리고 레벨 3단계는 로그 파일에도 그대로 찍히게 됩니다.


    디자인패턴 Qt강좌 싱글톤[Singleton design pattern Qt]

    위의 그림 중 왼쪽은 Qt Creator의 디버그 창(Application Output, 편하게 디버그 창으로 부름)입니다.


    우측은 실제 프로그램이고요. 버튼을 누를 때마다 좌측처럼 메시지가 출력됩니다. 저 출력되는 메시지가 파일에도 쓰이죠. 물론, 위에서 설명했다시피, 설정에 따라 파일에 쓰이는 메시지는 매번 다를 수 있습니다.


    싱글톤 Qt 강좌 디자인패턴[Singleton design pattern Qt]


    디자인패턴 중 싱글톤 패턴으로 생성한 파일입니다. 파일 내용을 알려드리겠습니다.


    15 - 시時

    23 - 분分

    36 - 초秒

    135 - 밀리 세컨드 3자리


    [ERROR] - 로그 레벨


    print - 그냥 찍어본거


    153 - 인자1

    55 - 인자2

    asdfasdf - 인자3


    간단하죠? 이젠 소스를 조금 살펴보겠습니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
     
    // [디자인패턴] 싱글턴 패턴 사용을 위한 전방 선언
    bool LogSingleton::destroyed = false;
    LogSingleton* LogSingleton::pInst = 0;
     
    // 메인 시작
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
     
        LogSingleton::getInstance().SetDir("C:/");
        LogSingleton::getInstance().SetLevel(LEVEL_WARNING);
        LogSingleton::getInstance().SetName("test");
        LogSingleton::getInstance().SetDate("yyyyMMdd");
        LogSingleton::getInstance().SetTime("hhmmsszzz");
     
     
        LogSingleton::getInstance().Print(LEVEL_ERROR, "print %d %d %s"15355"asdfasdf");
     
        // 임시 버튼 이벤트, 싱글턴 패턴
        connect(ui->btn_err_, &QPushButton::clicked, this, [=]() {
            LogSingleton::getInstance().Print(LEVEL_ERROR, "print %d %d %s"15355"asdfasdf");
        });
     
        connect(ui->btn_war_, &QPushButton::clicked, this, [=]() {
            LogSingleton::getInstance().Print(LEVEL_WARNING, "print %d %d %s"15355"asdfasdf");
        });
     
        connect(ui->btn_info_, &QPushButton::clicked, this, [=]() {
            LogSingleton::getInstance().Print(LEVEL_INFORMATION, "print %d %d %s"15355"asdfasdf");
        });
    }
     
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    cs


    뭐.. 특별한 건 없어요. 핵심적인 건 LogSingleton의 Set() 함수들입니다.

    파일을 생성한 디렉터리와 로그 파일에 기록할 로그 레벨을 설정해야 합니다.


    1
    2
    3
    4
    5
    6
    7
    8
    #ifndef COMMON_H
    #define COMMON_H
     
    enum WriteLevel {
        LEVEL_ERROR = 0,
        LEVEL_WARNING,
        LEVEL_INFORMATION
    };
    cs


    저는 로그 레벨을 경고 단계까지 주었습니다.


    그러면 INFORMATION에 해당하는 메시지는 디버그 창에만 뜨고, 실제 파일엔 쓰이지 않습니다. 나머진... 뭐 ... 그냥저냥 설정하면 되고요.


    1
    2
    3
    4
    5
    LogSingleton::getInstance().SetDir("C:/");
    LogSingleton::getInstance().SetLevel(LEVEL_WARNING);
    LogSingleton::getInstance().SetName("test");
    LogSingleton::getInstance().SetDate("yyyyMMdd");
    LogSingleton::getInstance().SetTime("hhmmsszzz");
    cs


    로그 파일은 LogFile 클래스에 존재하는데 아래처럼 되어 있어요.


    실제로 이 코드를 사용하실 분들은 여기서 로그 파일의 스타일을 꾸미시면 됩니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    #ifndef LOGFILE_H
    #define LOGFILE_H
     
    #include <QDebug>
    #include <QString>
    #include <QDateTime>
     
    #include <QFile>
    #include <QTextStream>
     
    #include "Common.h"
     
    class LogFile // 디자인패턴, 싱글턴 패턴
    {
        // Log 타입
        struct status {
            QString dir;
            int level;
            QString name;
            QString date;
            QString time;
        };
     
    public:
        LogFile();
     
    private:
        typedef status Status;
        Status status_;
     
    public// setter
        void Dir(const QString dir) {
            status_.dir.clear();
            status_.dir.append(dir);
        }
     
        void Level(const int level) {
            status_.level = level;
        }
     
        void Name(const QString name) {
            status_.name.clear();
            status_.name.append(name);
        }
     
        void Date(const QString date) {
            status_.date.clear();
            status_.date.append(date);
        }
     
        void Time(const QString time) {
            status_.time.clear();
            status_.time.append(time);
        }
     
    public:
        void Print(const int level, const QString msg);
    };
     
    #endif // LOGFILE_H
    cs


    typedef 키워드는 정말 마음에 들어요. 내 마음대로 주무를 수 있다는 것이 이토록 좋을 줄이야 ㅎㅎㅎ.


    싱글톤 싱글턴 패턴 Qt[Singleton design pattern Qt]


    근데, 한 가지 문제가 있습니다. Qt 5.3.2를 기준으로 C++11에서 지원하는 가변 인자 템플릿이 적용되질 않습니다. 인자의 수를 정해놓고 사용하는 데엔 문제가 없지만, 인자의 수를 정해놓지 않으면 에러가 발생하네요. (이거 때문에 디자인패턴 구성이 조금 망가짐)


    아마도 5.3.2에서는 지원이 안 되나 봅니다. 저는 이런저런 시도를 해보았으나 자꾸 안되길래 접었어요. 이 부분에 대해서 아는 분들은 댓글이나 다른 포스트로 알려주시면 감사하겠습니다.

    그래서, 어쩔 수 없이 C++에 C 코드를 넣었습니다. 아래처럼요.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdlib.h>        /* for malloc, NULL, size_t */
    #include <stdarg.h>        /* for va_ stuff */
    #include <string.h>        /* for strcat et al. */
     
    .....
    .....
     
    public// 출력 메시지 전달
        void Print(const int level, char* err, ... ) {
            char log_content[1024= {0,};
     
            va_list ap;
            va_start(ap, err);
            vsprintf_s(log_content + strlen(log_content), 1024, err, ap);
            va_end(ap);
     
            log_file_->Print(level, log_content);
        }
    cs


    C언어에서는 가변인자를 이용할 수 있도록 va_list란 함수를 제공합니다. 소스는 간단해요.


    필요에 따라 2개, 또는 3개의 헤더 파일을 인클루드하고, 위에 나온 소스를 그대로 사용하시면 됩니다. 이렇게 사용하시면, 인자의 개수에 상관없이 Print() 함수를 이용할 수 있어요. 개발자에도 유용한 함수죠.


    Singleton 싱글톤 싱글턴 패턴 Qt 강좌[[Singleton design pattern Qt] 디자인패턴] 싱글톤 패턴 개념


    마지막으로 싱글톤 패턴, 디자인패턴 구현 소스.


    저는 여기서 참조 했습니다(C++에서 싱글톤 패턴 구현하기 [링크]). 이글루스 gimmesilver란 분이 작성해 놓은 코드가 좋아서 그대로 사용했으니, 다른 분들도 참조해 보세요.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    #ifndef LOGSINGLETON_H /* 디자인패턴 클래스 */
    #define LOGSINGLETON_H
     
    #include <QDebug>
    #include <stdio.h>
    #include <stdlib.h>        /* for malloc, NULL, size_t */
    #include <stdarg.h>        /* for va_ stuff */
    #include <string.h>        /* for strcat et al. */
     
    #include "logfile.h"
    #include "Common.h"
     
    class LogSingleton { // 싱글턴 패턴
    public:
        static LogSingleton &getInstance() {
            if (destroyed) {
                new(pInst) LogSingleton; // 2)
                atexit(killSingleton);
     
                destroyed = false;
            } else if (pInst == 0) {
                create();
            }
     
            return *pInst;
        }
     
    private:
        LogSingleton() { log_file_ = new LogFile(); }
        LogSingleton(const LogSingleton & other);
     
        ~LogSingleton() {
            delete log_file_;
     
            destroyed = true;  // 1)
        }
     
        static void create() {
            static LogSingleton inst;
     
            pInst = &inst;
        }
     
        static void killSingleton() {
            pInst->~LogSingleton();  // 3)
        }
     
        static bool destroyed;
        static LogSingleton *pInst;
     
        LogFile *log_file_;
     
    public// 로그를 찍는 상태를 설정
        void SetDir(const QString dir) { log_file_->Dir(dir);}
        void SetLevel(const int level) { log_file_->Level(level); }
        void SetName(const QString name) { log_file_->Name(name); }
        void SetDate(const QString date) { log_file_->Date(date); }
        void SetTime(const QString time) {log_file_->Time(time); }
     
    public// 출력 메시지 전달
        void Print(const int level, char* err, ... ) {
            char log_content[1024= {0,};
     
            va_list ap;
            va_start(ap, err);
            vsprintf_s(log_content + strlen(log_content), 1024, err, ap);
            va_end(ap);
     
            log_file_->Print(level, log_content);
        }
    };
     
    #endif // LOGSINGLETON_H
    cs


    잘 이해가 안 되는 부분은 댓글로 적어주세요.


    [디자인패턴] Qt 싱글톤 패턴, 로그 생성 프로그램

    반응형