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) $^