お茶漬けびより

"あなたに教わったことを、噛んでいるのですよ" 五等分の花嫁 7巻 「最後の試験が五月の場合」より

Python 3.6 で MySQL データベースに接続

f:id:pickles-ochazuke:20171209164122j:plain

お久しぶりです。 9月に就職が決定して、そこから空いた一週間で北の方を旅行して、仕事が始まって最初の一ヶ月は給料なくて、11月に初というか3,4ヶ月ぶりの給料でなんとか生活を凌いでいます。
前の仕事は、C言語だったんですけど、今の場所はすることによって言語が変わるそうで、今は Python をメインに触っています。あと初めて SQL を仕事で使っていまして、知らないことだらけで初めてコンピュータサイエンスを学んだ時のようで楽しいです。

さて、今月はなるべく更新をしたいなと思っていますので、小さい内容でも更新していきたいと思っています。主に仕事で学んだことをまとめる方向にしたいので、PythonSQL の話になるかなーと。

では、本題です。 以下を参考にしています。

qiita.com

環境構築

Python のバージョンは、3.6.3 です。 バージョンの確認は、コマンドプロンプトpython --version を実行すれば確認できます。できなければ、パスが通ってないので、通しましょう。

まずは、必要なパッケージをインストールします。

pip install mysql-connector-python-rf

会社の環境だとプロキシの問題で、pip が上手くインストールしてくれないんですよね。そこら辺も覚えている範囲でまとめたい……。

mysql-connector-python も必要っぽいので、以下から落としてきます。
https://pypi.python.org/pypi/mysql-connector-python
こだわりがなければ最新のやつを落としてください。私は、mysql-connector-python-8.0.5.tar.gz を落としました。
落としたファイルの場所でコマンドプロンプトを開いて、以下を実行します。

pip install mysql-connector-python-8.0.5.tar.gz

そしたらインストールが成功するはずです。しなければ管理者権限でコマンドプロンプトを開いて、実行してみてください。 終わったら、pip list で出力されたパッケージリストに今入れた二つがあるか確認します。

MySQL をインストールしていなければ、インストールしましょう。ここでは、その説明は除きます。MySQL サーバを立ち上げて、データベースとテーブルを作成して、準備完了です。

MySQL データベース(DB)への接続

まずは、接続です。各引数は、自身の環境に合わせて変えてください。

import mysql.connector

conn = mysql.connector.connect(
    host = 'localhost',
    port = 3306,
    user = 'root',
    password = 'pass',
    database = 'test',
)

とくにエラーが発生しなければ成功しているはずです。この conn を使って、データベースに対していろいろ操作します。次に、接続が可能か調べるためには、以下を実行します。

connected = conn.is_connected()
print(connected)
if (not connected):
    conn.ping(True)

conn.is_connected() によって接続が可能か真偽値で返ってきます。True の場合は、接続が可能で、False の場合は、接続が不可能な状態です。conn.ping(True) は再接続を試みているのですが、conn.is_connected() が False なら再接続で解決はしなさそうですね……環境に合わせた対処が必要になると思います。

conn.ping(True) は、長い時間接続していると DB との接続が切れることがあるそうなので、その対処のために使うそうです。つまり、定期的に接続して切られないようにしている感じです。

クエリの実行

次にカーソルオブジェクトを作成します。カーソルというのは、データベースで使われる用語のようです。詳しくは知らないので、ここでは、クエリの実行と結果を受け取るために必要な DB への命令方法だと思えば問題ないと思います。

cur = conn.cursor()
cur.execute('select * from member')
table = cur.fetchall()
print(table)

conn.cursor() で、カーソルオブジェクトを作成し、cur.execute() で文字列をクエリとして DB に送ります。その結果を cur.fetchall() で受け取り、print() 出力しています。

ちなみに cur.fetchall() は、例えば SELECT の結果を全て受け取りますが、 cur.fetchone() は結果を一つだけ受け取ります。fetchone() は、何度も実行することで続きのデータを受け取ることができます。

例として、SELECT * FROM Table; というクエリを実行して、レコードが 3 つ現れるとします。fetchall() の場合は、この結果を全て受け取りますが、fetchone() では、レコードを 1 つだけ受け取ります。全て受け取る場合は、3 回実行する必要があるわけです。

それぞれにメリット、デメリットがあるようですがここでは説明しません(というか知りません……)。ちなみに、レコードを全て受け取った状態で fetchone()print() で出力すると None と出力されるので、 for 文などで簡単に回せそうです。

以上が簡単な MySQL データベースへの接続方法です。重要なのは、 mysql.connector.connect(), connect.cursor(), cursor.execute(), cursor.fetchall() ですかね。

最後に一通り実行できるコードを載せておきます。

# -*- coding: utf-8 -*-
import mysql.connector

conn = mysql.connector.connect(
    host = 'localhost',
    port = 3306,
    user = 'root',
    password = 'pass',
    database = 'test',
)

connected = conn.is_connected()
print(connected)
if(not connected):
    conn.ping(True)

cur = conn.cursor()

cur.execute('SELECT * FROM member')

# table = cur.fetchall()
# print(table)

print(cur.statement)
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())
print(cur.fetchone())

参考

今回出てきた API のドキュメントを載せておきます。

MySQL :: MySQL Connector/Python Developer Guide :: 7.1 Connector/Python Connection Arguments

MySQL :: MySQL Connector/Python Developer Guide :: 10.2.25 MySQLConnection.is_connected() Method

MySQL :: MySQL Connector/Python Developer Guide :: 10.2.27 MySQLConnection.ping() Method

googletest を使ってみた

f:id:pickles-ochazuke:20170828184058j:plain

北海道の土産用お菓子は、どれもおいしくて最高です。

趣味でコードを書いてるけど、テストコードは全く書いてこず、仕事でテストコードを初めて書いて、良いテストコードを書くのは難しいなぁと実感し、ちょっとこれは練習しないとダメだなということで、テストコードを書く環境を作り始めました。

googletest を見つけたので、これでいいかと選択。使う理由は、情報が多そうなのと使いやすそうな感じが理由。

Windows 版の Visual Studio 2017 上で動かしたので、そのまとめを書いておきます。

googletest を落とす

まずは、googletest を落としてこないと始まりません。

github.com

落としたら解凍します。

Google Test を追加

google-master/googletest/msvc/2010/ の中に、gtest.sln というソリューションファイルがあるので、Visual Studio で開きます。 開くと、セキュリティ警告をしてくると思うので、内容を読んで、問題なければ OK を押します。そのあとプロジェクトの再ターゲットが出るので、設定に問題なければ OK を押します。

f:id:pickles-ochazuke:20170828180315p:plain

完了したら gtest.sln を閉じます。次にテスト対象のプロジェクトを用意します。今回は、TestProject というプロジェクトを作成しました。次に、ソリューションに googletest のプロジェクトを追加します。 ソリューションエクスプローラからソリューションを右クリックし、追加 > 既存のプロジェクト を選び、

f:id:pickles-ochazuke:20170828180445p:plain

先ほど解凍した googletest/msvc/2010/gtest.vcxproj を追加します。追加すると以下のようになります。

f:id:pickles-ochazuke:20170828180553p:plain

環境設定

TestProject には、何もコードが書かれていないので追加します。ここでは、main.cpp を追加しました。追加したら、まずは googletest を使えるようにしましょう。TestProjectプロパティを開き、構成プロパティ > C/C++ > 全般 のなかの追加のインクルードディレクトリに新しいパスを追加します。

f:id:pickles-ochazuke:20170828180835p:plain

そこに googletest/include を追加します。もちろん、googletest より前は、googletest を置いている場所のパスが入ります。追加したら OK をクリックし、設定を反映させます。次に、TestProject の参照を追加します。参照を右クリックすると参照の追加が出るので、それをクリックし、gtest にチェックを入れます。

f:id:pickles-ochazuke:20170828180933j:plain

最後に、gtest プロジェクトのプロパティを表示し、構成プロパティ > C/C++ > コード生成 からランタイムライブラリを環境に合わせて設定します。よく分からない場合は、テスト対象のプロジェクトと同じにすれば問題ないはずです。ここでは、構成Debug に変更し、ランタイムライブラリマルチスレッドデバッグDLL(/MDd) にしました。OK をクリックして、プロパティを閉じます。

f:id:pickles-ochazuke:20170828181004p:plain

テストコードの作成

最後にテストコードを作成します。今回は、main.cpp に直接テストコードを作成します。

#include "gtest\gtest.h"

TEST()
{
    EXPECT_EQ(0, 1 - 1);
}

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

main 関数内の ::testing::InitGoogleTest(&argc, argv)RUN_ALL_TEST() がテスト実行のために必要な処理です。TEST(){} 内の処理が実際にテストしたい内容になります。

これを実行すると以下のように表示されます。

f:id:pickles-ochazuke:20170828181137p:plain

以上で基本的な設定と使い方は、完了です。 詳しく知りたい方は、

入門ガイド — Google Test ドキュメント日本語訳

あたりを参照するといいでしょう。

おまけ

googletest は、include ディレクトリと msvc ディレクトリ、src ディレクトリが同じ階層にあれば問題なさそうなので、この三つをコピーして、ソリューションごとに置くと良さそうです。

おわり

以上が googletest の使い方でした。Visual Studio に googletest を入れていると Visual Studio さんに Test Adapter for Google Test をお勧めされたので、気が向いたら試してみようと思います。Community でも使えるんですかね……?

marketplace.visualstudio.com

JSON for Modern C++ を使ってみたメモ

メモ書き

github.com

JSON for Modern C++ とは?

以下、参照。

mattn.kaoriya.net

使い方

Github のページの json/src/json.hpp というファイルがあるので、これを include する。

#include "json.hpp"
using json = nlohmann::json;

この json クラスを使って、JSON ファイルを読んだり書いたりする。

以下のような JSON ファイルを使って、例を示していく。 ファイル名は、test.json として進めていく。

{
  "pi": 3.141,
  "happy": true,
  "name": "Niels",
  "nothing": null,
  "answer": {
    "everything": 42
  },
  "list": [1, 0, 2],
  "object": {
    "currency": "USD",
    "value": 42.99
  }
}

JSON ファイルの読み込み

JSON ファイルを読む必要があるので、まずはファイルを読む。

std::ifstream reading("test.json", std::ios::in);

ファイルの入出力は以下を参照。

qiita.com

ファイルを読んだら以下のようにして取り出す。

json j;
reading >> j;

これだけで、ファイルの中身がすべて json j に入っている。 試しに以下のように出力すると

reading >> j;
std::cout << j << std::endl;

以下のように出力される。

{"answer":{"everything":42},"happy":true,"list":[1,0,2],"name":"Niels","nothing":null,"object":{"currency":"USD","value":42.99},"pi":3.141}

改行はしてくれないっぽい。

このデータだけ取り出したいってときは、以下のようにする。

std::cout << j["pi"] << std::endl;
// 出力結果は、3.141

begin(), end() を使って取り出すことができる。

std::for_each(j.begin(), j.end(), [](auto it) {
    std::cout << it << std::endl;
});

結果は、以下のようなる。

{"everything":42}
true
[1,0,2]
"Niels"
null
{"currency":"USD","value":42.99}
3.141

begin, end を使うとアルファベット順になる。

C++17 だと以下のように書ける。

for (auto& elem : j) {
    std::cout << elem << std::endl;
}

JSON ファイルへ書き込み

未調査…

おわり

とても使いやすい。まだまだ調べ切れていないので、分かり次第更新していく予定。

簡単に Pimpl を使ってみる

名前は知っていたけど、使ったことがなかったので、試しに使ってみた。

Pimpl とは

にきび(pimple)ではありません。Pointer to Implementation (実装へのポインタ)を略して、Pimpl です。
このテクニックによって、プライベートな詳細を開示せずに済みます。つまりインタフェースと実装を切り離す方法です。

これは、実際にコードを見たほうが早いと思うので、先にコードを出します。

実装

とりあえず以下のようなコードを書いてみました。これは、まだ Pimpl を使っていません。

// Widget.h
#pragma once
#include <stdint.h>

class Widget
{
public:
    Widget();
    ~Widget();

    int32_t getNum();
private:
    int32_t num_ = 32;
};

// Widget.cpp
#include "Widget.h"
#include <iostream>

Widget::Widget()
{
    std::cout << "Constructor" << std::endl;
}

Widget::~Widget()
{
    std::cout << "Destructor" << std::endl;
}

int32_t Widget::getNum()
{
    return num_;
}

Widget::getNum() を呼ぶことで、内部の num のデータを取れるクラスです。このクラスを使う側(ユーザ)は、データの取り方が分かればいいので、変数 num があることを知らせる必要はありません。なので、これを実装ファイル(Widget.cpp)に入れてやります。

// Widget.h
#pragma once
#include <stdint.h>

class Widget
{
public:
    Widget();
    ~Widget();

    int32_t getNum();
private:
    class Impl;
    Impl* impl_;
};

// Widget.cpp
#include "Widget.h"
#include <iostream>

class Widget::Impl
{
public:
    Impl() { std::cout << "Impl Constructor" << std::endl; }
    ~Impl() { std::cout << "Impl Destructor" << std::endl; }

    int32_t num = 32;
};

Widget::Widget()
    : impl_(new Impl())
{
    std::cout << "Constructor" << std::endl;
}


Widget::~Widget()
{
    std::cout << "Destructor" << std::endl;
    delete impl_;
    impl_ = nullptr;
}

int32_t Widget::getNum()
{
    return impl_->num;
}

まずヘッダから見ていきます。private にメンバ変数 num がありましたが、それが消えて、代わりに クラス Impl の前方宣言とそのクラス Impl のポインタを追加しました。

次に実装側ですが、初めに クラス Imple の定義を行っています。その中にさっきは、Widget.h にあったメンバ変数が Widget::Impl に移動しています。

Widget のコンストラクタの初期化子リストに impl_ が追加され、デストラクタでは、impl_ の開放を行っています。最後に、Widget::getNum() は、impl_のメンバ変数 num をアクセスして、値を返すように変更されています。

このように、詳細を実装ファイル内に隠してしまうのが、Pimpl です。

Pimpl を実装するときの注意

Pimpl を書くときに、いくつか注意することがあります(C++ に慣れている方は大したことではないですが)。

まず、Pimpl となる変数(今回だと impl_)の型は、ポインタ(Impl*)でないといけません。当たり前ですが、ヘッダ側は、Impl の実装内容を知らないので、Impl のオブジェクトを持てません。

Impl の詳細を書くときは、必ず Impl を利用する処理よりも先に書くようにします。今回だと Widget::Widget() よりも先に書く必要があります。これも Impl の詳細を知らないと使えないためです。

最後に、デストラクタでは必ず Impl の変数を開放するようにします。ポインタなので、当然ですね。

Pimpl をスマートポインタにする

Pimpl をスマートポインタに変更することで、デストラクタ内で Pimpl を開放処理を書かなくて済みます。例えば以下のようにします。

std::unique_ptr<Impl> impl_;

デメリット

Pimpl を使うことでいくつかデメリットがあります。

  • コンストラクタでオブジェクトの割当、デストラクタでオブジェクトを破棄する必要がある
  • すべてのプライベートメンバは、Pimpl を通じてアクセスしないといけない
  • Pimpl をプライベートにすると、Pimpl を持つクラス(Widget)しか Pimpl のメンバにアクセスできない
  • プライベート仮想メソッドを作れない。オーバライドする場合、パブリックにして、派生クラスがオーバライドできるようにしないといけない
  • Pimpl(impl_) から、それを持つクラス(Widget)のパブリックなメンバ変数にアクセスするには、Pimpl にそのクラスへのポインタを追加するか、そのメソッドにクラスを渡す必要がある
  • const メソッド内で Pimpl(impl_)のメンバ変数を変更してもコンパイラは検知することができない

いくつかデメリットはありますが、実装する側のデメリットであり、利用者に対してのデメリットはないので、使わない理由にはならないでしょう。

メリット

次は、メリットです。

  • ユーザ側が見ることになるヘッダファイルの内容がすっきりする。
  • 実装の詳細をユーザ側に見られることがなくなり、不正な方法でアクセスすることもできなくなる。
  • ヘッダのインクルードファイルが減るので、結合度が減る。
  • コンパイル時間が高速化される
  • バイナリ互換性が向上する
  • Pimpl は要求時に作ることもできるので、ネットワーク接続など制限のあるリソースやコストの高いリソースの割当のときに役に立つ。

などなどあります。詳細は、参考にした本を見てください(最後に載せています)

コピーセマンティクス

Pimpl を持つクラスをコピーした場合、当然 Pimpl もコピーされる。この状態で、コピー元か、コピー先のオブジェクトを開放したとき、もう片方のオブジェクトが Pimpl にアクセスすると Pimpl はすでに開放されているため、エラーが発生します。

これを防ぐためには、3つの方法があります。

  • コピーコンストラクタ、代入演算子delete(暗黙定義の禁止) する
  • コピーコンストラクタ、代入演算子を明示的に定義する
  • スマートポインタを利用する

個人的には、スマートポインタが楽だと思います。コピーを禁止したい場合は、std::unique_ptr, コピーを許可するなら std::shared_ptr でいいと思います。 スマートポインタを使えば、コピーセマンティクスを書く必要もなくなります。

おわり

Pimpl は、有効に使えると強力な反面、注意する点が多い方法だと感じました。デメリットは小さいので、積極的に使っていきたいですが、プライベートをなんでもかんでも Pimpl に実装するのも違うと思うので、よく考えて使いたいと思います。

Pimpl の書き方をメインに書いたので、どう活用するかは分かりにくかったかもしれません。そういった内容は、参考にした本に載っているのでそちらを参照するといいでしょう。

今回のコードは、以下に上げています。

github.com

参考にした資料です。

C++のためのAPIデザイン

C++のためのAPIデザイン

拡張式 Factory Method

f:id:pickles-ochazuke:20170715173147j:plain

ナッツ食べてます。そこそこおいしいです。

前回、FactoryMethod を作りましたが、今回は、それを改造します。

pickles-ochazuke.hatenablog.com

基本的な FactoryMethod だと、新しいクラスを作ったときに毎度 FactoryMethod 側も新しいクラス用の処理を追加しないといけません。そこで、新しいクラスへの対応を実行中に行えるようにします。

概要

大まかに書くと、抽象基本クラスを継承した新しいクラス側に、"自身のオブジェクトを作成し、それを返す" コールバック関数を作ります。そのコールバック関数と key となる文字列を FactoryMethod に渡し、FactoryMethod 側は、その関係を覚えておきます。実際にオブジェクトが必要になったときは、key となる文字列を渡すことで、それに対応したコールバック関数が FactoryMethod 内で呼ばれ、作成されたオブジェクトが返ってくる。という感じです。

図にすると以下のような感じでしょうか。

f:id:pickles-ochazuke:20170715194958p:plain

上記は、Callback の登録のみしか書いていませんが、削除もできたほうがいいので、実装時には削除用の関数も作ります。

FactoryClass

まずシーケンス図にあった FactoryClass のヘッダは以下のとおりです。 クラス名は、前回の続きなので SlimeFactory となっています。 また、SlimeFactory で作られるオブジェクトは、すべて抽象基本クラスの ISlime を継承しています。

#pragma once
#include <string>
#include <map>
#include <functional>
#include "ISlime.h"

class SlimeFactory
{
public:
    using DoMakeCallback = std::function<ISlime*()>;

    SlimeFactory()  = delete;
    ~SlimeFactory() = delete;
    static void RegisterSlime(const std::string& type, DoMakeCallback cb);
    static void UnregisterSlime(const std::string& type);
    static ISlime* DoMakeSlime(const std::string& type);

private:
    using CallbackMap = std::map<std::string, DoMakeCallback>;
    static CallbackMap DoMakes_;

};

上から順番に見ていきます。まず、using DoMakeCallback = std::function<ISlime*()> ですが、 これは、コールバック関数の型を定義しています。using DoMakeCallback は書くのを楽にしているだけで、std::function<ISlime*()> が大事です。これが、登録するコールバック関数の型になります。登録したいクラスは、コールバック関数を作るとき、この型(ISlime*())を守る必要があります。std::function は、関数ポインタ用のクラスだと思っておけば問題ないです。

コンストラクタとデストラクタは、今回必要ないので delete しています。実際に FactoryClass を作るときは、1つしか作られないようにシングルトンにするといいでしょう。

static void RegisterSlime(const std::string& type, DoMakeCallback cb) は、コールバック関数を登録するための関数です。登録したいコールバック関数があるとき、この関数を呼び出して、引数に key となる文字列とコールバック関数を渡してやります。

static void UnregisterSlime(const std::string& type) は、先ほどとは逆で、登録済みのコールバック関数を削除する関数です。使うときは、登録したときに渡した文字列と同じ文字列を引数に渡すだけです。

static ISlime* DoMakeSlime(const std::string& type) は、オブジェクトが必要になったときに呼びます。引数に登録時の文字列を渡してやることで、生成されたオブジェクトが返ってきます。

using CallbackMap = std::map<std::string, DoMakeCallback> は、これも型を定義しているだけです。std::map<std::string, DoMakeCallback> は、コールバック関数を登録するための型になります。std::stringkey となり、DoMakeCallback が登録されたコールバック関数です。

最後に、static CallbackMap DoMakes_ ですが、これは、コールバック関数が登録される場所になります。

次に、ソースファイルを見ていきます。

#include "HealSlime.h"
#include <iostream>

// インスタンス化
SlimeFactory::CallbackMap SlimeFactory::DoMakes_;

void SlimeFactory::RegisterSlime(const std::string& type, DoMakeCallback cb)
{
    DoMakes_[type] = cb;
}

void SlimeFactory::UnregisterSlime(const std::string& type)
{
    DoMakes_.erase(type);
}

ISlime* SlimeFactory::DoMakeSlime(const std::string& type)
{
    if (type == "Slime") {
        return new Slime();
    }
    else if (type == "SheSlime") {
        return new SheSlime();
    }
    else if (type == "HealSlime") {
        return new HealSlime();
    }

    CallbackMap::iterator it = DoMakes_.find(type);
    if (it != DoMakes_.end()) {
        if (!it->second) { std::cout << "呼び出せない" << std::endl; return nullptr; }
        return (it->second)();
    }

    return nullptr;
}

上から順番に見ていきましょう。 まずは、SlimeFactory::CallbackMap SlimeFactory::DoMakes_ で static の変数をインスタンス化してやります。

RegisterSlime(const std::string& type, DoMakeCallback cb) は、登録なので、登録用の変数 DoMakes_ にコールバック関数を登録しています。

UnregisterSlime(const std::string& type) は、削除なので、type を元に DoMakes_ に登録されているコールバック関数を削除しています。

DoMakeSlime(const std::string& type) は、type を元に作成すべきオブジェクトを決め、作成し、そのオブジェクトを返します。
Slime, SheSlime, HealSlime は、前回作成した内容です。そこを抜けると type と関連するコールバック関数があるか探します(CallbackMap::iterator it = DoMakes_.find(type);)。見つかれば、そのコールバック関数が呼べるか確認(if (it != DoMakes_.end()))し、問題なければ、呼び出して、返ってきたオブジェクトをそのまま返します(return (it->second)())。

以上が拡張式の FactoryMethod になります。

登録するためのコールバック関数の中身は、以下のように書きます。

static ISlime * DoMakeMyself()
{
    return new MySlime();
}

このメソッドを RegisterSlime の第二引数に渡してやれば、DoMakes_ に登録されます。

実行

実際に使うときは、以下のように使います。

 std::function<ISlime*()> func = MySlime::DoMakeMyself;
    SlimeFactory::RegisterSlime("MySlime", func);

    ISlime* slime = SlimeFactory::DoMakeSlime("MySlime");

結果は、以下です。

ぼくが考えた最強のスライム が現れた!

続行するには何かキーを押してください . . .

以上のように、新しいクラスを作ったときは、FactoryClass 側で何も追加する必要なく、対応することが出来ました。

おわり

実際に作ってみると、することは難しそうだけど、作ってみたら簡単だった。ように感じました。コールバック関数の登録も削除も一行で済み、呼び出すときもfindで探して、呼び出すだけですので。

今回の難しいところは、拡張式の FactoryMethod の作り方よりも、関数ポインタ、std::map、を理解しているかどうかだと思います。さらに言ってしまえば、std::map は今回採用しているだけなので、何か key を渡されたときに、それに関係する関数ポインタを返せるならどう作ってもいいわけです。なので、今回一番重要なのは、関数ポインタを理解しているかどうかなのだと思いました。

最後に、コードと参考資料を載せておきます。

github.com

C++のためのAPIデザイン

C++のためのAPIデザイン