LoginSignup
4
5

More than 5 years have passed since last update.

C++でTODOアプリ作ってみた

Posted at

C++の勉強のために、コマンドライン上で動くTODOアプリを作りました。MySQL5.7を使います。macで作っています。

Github

作ったものはこれです。

開発環境

  • C++ (Apple LLVM version 8.1.0 (clang-802.0.42))
  • MySQL5.7.18
  • Mac Sierra

インストール

1. MySQLでDB・テーブルを作成

MySQLでデータベースを作成し、下記tasksテーブルを作成します。

mysql> desc tasks;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| task       | varchar(100) | NO   |     | NULL    |                |
| status     | tinyint(1)   | NO   |     | 0       |                |
| important  | tinyint(1)   | NO   |     | 0       |                |
| sort_no    | int(6)       | YES  |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

2. main.cppのDB接続設定を編集

main.cppの下記を環境に合わせて編集します。

main.cpp
#define DBHOST "localhost"
#define DBPORT 3306
#define DBUSER "root"
#define DBPASS "password"
#define DBNAME "todo"

3. makeします

コンパイルしているだけなので、todoという実行ファイルがカレントディレクトリに作成されます。

$ make

makefileはmacの環境用になっているので、環境に合わせて修正が必要になります。

使い方

下記はtodoをpathに追加した前提です。

todo helpでhelpが表示されます。

$ todo help
g <count>   show Incomplete <count> tasks
a <title>   add a <title> task
ao <title>  add a <title> task and check important
f <id>      finish the task <id>
r <id>      revert the task <id>
on <id>     check important to the <id> task
off <id>    remove check important to the <id> task
del <id>    delete the <id> task
h <count>   show completed <count> tasks 
clear       delete all completed tasks 
truncate    delete all tasks 

todoでタスク一覧が表示されます。

$ todo
5 * プログラム作る
7   Go勉強する
6   C++勉強する
4   歯を磨く
2   寝る

todo aでタスクを追加します。todo aoでタスクの追加と同時に重要マークをつけます。

$ todo a "new task"
$ todo
5 * プログラム作る
8   new task
7   Go勉強する
6   C++勉強する
4   歯を磨く
2   寝る

todo fでタスクを完了にします。

$ todo f 2
$ todo
5 * プログラム作る
8   new task
7   Go勉強する
6   C++勉強する
4   歯を磨く

todo onで重要マークがつけられます。重要なものは上に表示されます。todo offで重要マークを外せます。

$ todo on 8
$ todo
8 * new task
5 * プログラム作る
7   Go勉強する
6   C++勉強する
4   歯を磨く

todo hで完了したタスク一覧が表示されます。

$ todo h
2   寝る
1   夕飯を食べる
3   起きる

todo dで不要なタスクを削除できます。

$ todo d 3
$ todo h
2   寝る
1   夕飯を食べる

todo rで未完了状態に戻せます。
todo clearで完了済みタスクを全消去します。
todo truncateで、全タスクを削除し、初期化します。

コード

main.cpp
#include <iostream>
#include <mysql/mysql.h>
#include <boost/format.hpp>

#define DBHOST "localhost"
#define DBPORT 3306
#define DBUSER "root"
#define DBPASS "password"
#define DBNAME "todo"

using std::string;

void help()
{
    std::cout << "g <count>\tshow Incomplete <count> tasks\n";
    std::cout << "a <title>\tadd a <title> task\n";
    std::cout << "ao <title>\tadd a <title> task and check important\n";
    std::cout << "f <id>\t\tfinish the task <id>\n";
    std::cout << "r <id>\t\trevert the task <id>\n";
    std::cout << "on <id>\t\tcheck important to the <id> task\n";
    std::cout << "off <id>\tremove check important to the <id> task\n";
    std::cout << "del <id>\tdelete the <id> task\n";
    std::cout << "h <count>\tshow completed <count> tasks \n";
    std::cout << "clear\t\tdelete all completed tasks \n";
    std::cout << "truncate\tdelete all tasks \n";
}

class mysql
{
  protected:
    MYSQL *conn;
    MYSQL_RES *res;
    MYSQL_ROW row;
    void execute(string sql);

  public:
    mysql();
    ~mysql();
};

mysql::mysql()
{
    conn = mysql_init(NULL);
    if (!mysql_real_connect(conn, DBHOST, DBUSER, DBPASS, DBNAME, DBPORT, NULL, 0))
    {
        fprintf(stderr, "%s\n", mysql_error(conn));
        exit(1);
    }
}

mysql::~mysql()
{
    mysql_free_result(res);
    mysql_close(conn);
}

void mysql::execute(string sql)
{
    if (mysql_query(conn, sql.c_str())) //todo sanitaize?
    {
        fprintf(stderr, "%s\n", mysql_error(conn));
        exit(1);
    }
    res = mysql_use_result(conn);
}

class todo : public mysql
{
    void check_id(int id);

  public:
    void get(int limit, string mode);
    void add(string task, string mode);
    void fin(int id, string mode);
    void star(int id, string mode);
    void del(int id);
    void clear();
    void truncate();
};

void todo::check_id(int id)
{
    if (!id)
    {
        std::cerr << "invalid id\n";
        exit(1);
    }
}

void todo::get(int limit = 20, string mode = "get")
{
    string sql = "select * from tasks where status = %1%";
    sql += " order by important desc,updated_at desc limit %2%";
    limit = !limit ? 20 : limit;
    int status = mode == "his" ? 1 : 0;
    sql = (boost::format(sql) % status % limit).str();
    execute(sql);
    string star;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        star = atoi(row[3]) ? " *" : "  ";
        std::cout << row[0] << star << "\t" << row[1] << std::endl;
    }
}

void todo::add(string task, string mode = "add")
{
    if (task == "")
    {
        std::cerr << "invalid task\n";
        exit(1);
    }
    int star = mode == "add" ? 0 : 1;
    string sql = "insert into tasks";
    sql += " (task, important, updated_at) values ('%1%', %2%, now())";
    sql = (boost::format(sql) % task % star).str();
    execute(sql);
}

void todo::del(int id)
{
    check_id(id);
    string sql = "delete from tasks where id = %1%";
    sql = (boost::format(sql) % id).str();
    execute(sql);
}

void todo::fin(int id, string mode = "fin")
{
    check_id(id);
    int status = mode == "rev" ? 0 : 1;
    string sql = "update tasks set status = %1%, important = 0,";
    sql += " updated_at = now() where id = %2%";
    sql = (boost::format(sql) % status % id).str();
    execute(sql);
}

void todo::star(int id, string mode = "on")
{
    check_id(id);
    int status = mode == "on" ? 1 : 0;
    string sql = "update tasks set important = %1%,";
    sql += " updated_at = now() where id = %2%";
    sql = (boost::format(sql) % status % id).str();
    execute(sql);
}

void todo::clear()
{
    execute("delete from tasks where status = 1");
}

void todo::truncate()
{
    execute("truncate table tasks");
}

int check_int(std::string str)
{
    try
    {
        return stoi(str);
    }
    catch (...)
    {
        return 0;
    }
}

void action(todo *td, string mode, string param)
{
    if (mode == "g")
    {
        td->get(check_int(param));
    }
    else if (mode == "a")
    {
        td->add(param);
    }
    else if (mode == "ao")
    {
        td->add(param, "star");
    }
    else if (mode == "d")
    {
        td->del(check_int(param));
    }
    else if (mode == "f")
    {
        td->fin(check_int(param));
    }
    else if (mode == "r")
    {
        td->fin(check_int(param), "rev");
    }
    else if (mode == "h")
    {
        td->get(check_int(param), "his");
    }
    else if (mode == "on")
    {
        td->star(check_int(param));
    }
    else if (mode == "off")
    {
        td->star(check_int(param), "off");
    }
    else if (mode == "clear")
    {
        td->clear();
    }
    else if (mode == "truncate")
    {
        td->truncate();
    }
    else
    {
        help();
    }
}

int main(int argc, char *argv[])
{
    todo td;
    if (argc < 2)
    {
        td.get();
    }
    else
    {
        action(&td, argv[1], argc < 3 ? "" : argv[2]);
    }
    return 0;
}

makefile

LDLIBS   = -lm -lmysqlclient
FLAGS = -Wall -I/usr/local/include
LDFLAGS  = -L/usr/local/lib

todo: main.cpp
    c++ -O2 -o $@ $(LDLIBS) $(FLAGS) $(LDFLAGS) $^
4
5
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5