2
\$\begingroup\$

I am currently learning how to use PDcurses and decided to make a clock with it. **NOTE: this Watch that uses the abstract factory design pattern is not the same as this question!!. This question is a rework of the previous.

I settled on abstract factory design pattern because there are two families of objects that can be created: a Date family(segmented + digital) and a Time family(segmented + digital), although i still have doubts that just making free functions such as:

void printSegmentedTime(WINDOW*);
void printSegmentedDate(WINDOW*);
void printDigitalTime(WINDOW*);
void printDigitalDate(WINDOW*);

would be a better choice. Any thoughts on what choice would be a better approach?

clock.h:

#ifndef CLOCK_H
#define CLOCK_H
#include "curses.h"
#include <memory>

namespace curses_clock
{
    class AbstractView
    {
    public:
        virtual void printView() = 0;

        explicit AbstractView(WINDOW* view_win)
            : view_win_{ view_win } {}

        virtual ~AbstractView() { delwin(view_win_); }

    protected:
        WINDOW* view_win_;
    };

    class SegmentedTime final : public AbstractView
    {
    public:
        virtual void printView() override;

        explicit SegmentedTime(WINDOW* segmented_win)
            : AbstractView{ segmented_win } {}
    };

    class SegmentedDate final : public AbstractView
    {
    public:
        virtual void printView() override;

        explicit SegmentedDate(WINDOW* segmented_win)
            : AbstractView{ segmented_win } {}
    };

    class DigitalTime final : public AbstractView
    {
    public:
        virtual void printView() override;

        explicit DigitalTime(WINDOW* digital_win)
            : AbstractView{ digital_win } {}
    };

    class DigitalDate final : public AbstractView
    {
    public:
        virtual void printView() override;

        explicit DigitalDate(WINDOW* digital_win)
            : AbstractView{ digital_win } {}
    };

    class ViewFactoryInterface
    {
    public:
        virtual std::unique_ptr<AbstractView> createSegmentedView(int x, int y) = 0;
        virtual std::unique_ptr<AbstractView> createDigitalView(int x, int y) = 0;

        virtual ~ViewFactoryInterface() = default;
    };

    class TimeFactory final : public ViewFactoryInterface
    {
    public:
        virtual std::unique_ptr<AbstractView> createSegmentedView(int x, int y) override;
        virtual std::unique_ptr<AbstractView> createDigitalView(int x, int y) override;
    };

    class DateFactory final : public ViewFactoryInterface
    {
    public:
        virtual std::unique_ptr<AbstractView> createSegmentedView(int x, int y) override;
        virtual std::unique_ptr<AbstractView> createDigitalView(int x, int y) override;
    };
}

#endif

clock.cpp:

#include "clock.h"
#include <sstream>
#include <iomanip>
#include <string>
#include <chrono>
#include <ctime>

namespace
{
    using SysClock = std::chrono::system_clock;

    constexpr int      SEGMENTED_DATE_WIDTH{ 43 };
    constexpr int      SEGMENTED_TIME_WIDTH{ 33 };
    constexpr int      SECOND_SEPARATOR_X  { 19 };
    constexpr int      DIGITAL_DATE_WIDTH  { 10 };
    constexpr int      FIRST_SEPARATOR_X   { 9  };
    constexpr int      DIGITAL_TIME_WIDTH  { 8  };
    constexpr int      SEGMENTED_HEIGHT    { 5  };
    constexpr int      DIGIT_PADDING       { 5  };
    constexpr int      DIGITAL_HEIGHT      { 1  };

    struct MDY
    {
        int month_;
        int day_;
        int year_;
    };

    struct HMS
    {
        int hours_;
        int minutes_;
        int seconds_;
    };

    std::tm getTimeDate()
    {
        SysClock::time_point now = SysClock::now();
        time_t raw_time = SysClock::to_time_t(now);

        std::tm time_info;
        localtime_s(&time_info, &raw_time);
        return time_info;
    }

    HMS toHMS(const std::tm& time_data)
    {
        int hours{ time_data.tm_hour };
        int minutes{ time_data.tm_min };
        int seconds{ time_data.tm_sec };
        return{ hours, minutes, seconds };
    }

    MDY toMDY(const std::tm& time_data)
    {
        int month = time_data.tm_mon + 1;
        int day = time_data.tm_mday;
        int year = time_data.tm_year + 1900;
        return{ month, day, year };
    }

    std::string toStr(HMS hms) {
        std::stringstream ss;
        ss << std::setfill('0') << std::setw(2) << hms.hours_ << ':'
                                  << std::setw(2) << hms.minutes_ << ':'
                                  << std::setw(2) << hms.seconds_;
        return ss.str();
    }

    std::string toStr(MDY mdy) {
        std::stringstream ss;
        ss << std::setfill('0') << std::setw(2) << mdy.month_ << '.'
                                  << std::setw(2) << mdy.day_ << '.'
                                  << mdy.year_;
        return ss.str();
    }

    void printDigit(WINDOW* win, int y, int x, int num) {
        static const char* digits[]{
            { " -- "
                "|  |"
                "    "
                "|  |"
                " -- " },
            { "    "
                "   |"
                "    "
                "   |"
                "    " },
            { " -- "
                "   |"
                " -- "
                "|   "
                " -- " },
            { " -- "
                "   |"
                " -- "
                "   |"
                " -- " },
            { "    "
                "|  |"
                " -- "
                "   |"
                "    " },
            { " -- "
                "|   "
                " -- "
                "   |"
                " -- " },
            { " -- "
                "|   "
                " -- "
                "|  |"
                " -- " },
            { " -- "
                "   |"
                "    "
                "   |"
                "    " },
            { " -- "
                "|  |"
                " -- "
                "|  |"
                " -- " },
            { " -- "
                "|  |"
                " -- "
                "   |"
                " -- " }
        };

        if (num < 0 || num > 9)
            throw std::runtime_error("Digit out of range, must be (0 < digit < 9)");

        for (auto line{ digits[num] }; *line; line += 4) {
            for (auto ch{ line }; ch < line + 4; ++ch)
                mvwaddch(win, y, x++, *ch);
          ++y; x -= 4;
        }
    }
}

void curses_clock::SegmentedTime::printView()
{
    std::string hms{ toStr(toHMS(getTimeDate())) };

    int digits[6]{
        hms[0] - '0', hms[1] - '0',
        hms[3] - '0', hms[4] - '0',
        hms[6] - '0', hms[7] - '0'
    };

    int xpos{};
    for (const auto& digit : digits)
        printDigit(view_win_, 0, DIGIT_PADDING * xpos++, digit);

    for (auto x : { FIRST_SEPARATOR_X, SECOND_SEPARATOR_X })
        for (auto y : { 1, 3 })
            mvwaddch(view_win_, y, x, '*');

    wrefresh(view_win_);
}

void curses_clock::SegmentedDate::printView()
{
    std::string mdy{ toStr(toMDY(getTimeDate())) };

    int digits[8]{
        mdy[0] - '0', mdy[1] - '0',
        mdy[3] - '0', mdy[4] - '0',
        mdy[6] - '0', mdy[7] - '0',
        mdy[8] - '0', mdy[9] - '0'
    };

    int xpos{};
    for (const auto& digit : digits)
        printDigit(view_win_, 0, DIGIT_PADDING * xpos++, digit);

    for (auto x : { FIRST_SEPARATOR_X, SECOND_SEPARATOR_X })
        mvwaddch(view_win_, 4, x, '*');

    wrefresh(view_win_);
}

void curses_clock::DigitalTime::printView()
{
    mvwprintw(view_win_, 0, 0, toStr(toHMS(getTimeDate())).c_str());
    wrefresh(view_win_);
}

void curses_clock::DigitalDate::printView()
{
    mvwprintw(view_win_, 0, 0, toStr(toMDY(getTimeDate())).c_str());
    wrefresh(view_win_);
}

std::unique_ptr<curses_clock::AbstractView> curses_clock::TimeFactory::createSegmentedView(int x, int y)
{
    return std::make_unique<curses_clock::SegmentedTime>(newwin(SEGMENTED_HEIGHT, SEGMENTED_TIME_WIDTH, y, x));
}

std::unique_ptr<curses_clock::AbstractView> curses_clock::TimeFactory::createDigitalView(int x, int y)
{
    return std::make_unique<curses_clock::DigitalTime>(newwin(DIGITAL_HEIGHT, DIGITAL_TIME_WIDTH, y, x));
}

std::unique_ptr<curses_clock::AbstractView> curses_clock::DateFactory::createSegmentedView(int x, int y)
{
    return std::make_unique<curses_clock::SegmentedDate>(newwin(SEGMENTED_HEIGHT, SEGMENTED_DATE_WIDTH, y, x));
}

std::unique_ptr<curses_clock::AbstractView> curses_clock::DateFactory::createDigitalView(int x, int y)
{
    return std::make_unique<curses_clock::DigitalDate>(newwin(DIGITAL_HEIGHT, DIGITAL_DATE_WIDTH, y, x));
}

source.cpp:

#include "clock.h"
#include <chrono>

using namespace std::chrono_literals;
using namespace curses_clock;

using SteadyClock = std::chrono::steady_clock;

int main()
{
    std::unique_ptr<ViewFactoryInterface> time_fact{ std::make_unique<TimeFactory>() };
    std::unique_ptr<ViewFactoryInterface> date_fact{ std::make_unique<DateFactory>() };

    initscr();
    curs_set(0);
    noecho();
    refresh();

    std::shared_ptr<AbstractView> time{ std::move(time_fact->createSegmentedView(0,0)) };
    std::shared_ptr<AbstractView> date{ std::move(date_fact->createDigitalView(0,8)) };

    SteadyClock::time_point sec_point{ SteadyClock::now() - 1s };
    SteadyClock::time_point day_point{ SteadyClock::now() - 24h };

    while (1) {
        if (SteadyClock::now() - sec_point >= 1s) {
            sec_point = SteadyClock::now();
            time->printView();
        }
        if (SteadyClock::now() - day_point >= 24h) {
            day_point = SteadyClock::now();
            date->printView();
        }
    }

    getch();
    endwin();
    return 0;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.