八葉の日記

日々、感じたことをまとめる場として利用する

C++ Core Guidelines C.21

C++ Core Guidelinesの自分用翻訳


C.21: If you define or =delete any copy, move, or destructor function, define or =delete them all

Reason

The semantics of copy, move, and destruction are closely related, so if one needs to be declared, the odds are that others need consideration too.
コピー、移動、破壊のセマンティクスは密接に関連しているので、1つを宣言する必要がある場合、他のものも考慮する必要がある確率が高いです。


コピー/移動/デストラクタ関数のいずれかを宣言します。たとえ `=default` や `=delete` であっても、コピー/移動/デストラクタ関数を宣言すると、暗黙のうちに としてでも、コピー/移動/デストラクタ関数を宣言すると、移動コンストラクタと移動代入演算子の暗黙の宣言が抑制されます。
移動コンストラクタや移動代入演算子は、たとえ を宣言すると、暗黙のうちに生成されたコピー コンストラクタや や暗黙のうちに生成されるコピー代入演算子は削除されたものとして定義されます。
したがって、これらのいずれかが宣言されると同時に、他のものもすべて宣言して、すべての移動候補をより高価なコピーに変えてしまうような好ましくない影響を避けるべきです。をより高価なコピーに変えてしまったり、 クラスを移動専用にしてしまったりするような好ましくない効果を避けるために、 他のものもすべて宣言しておくべきです。

Example, bad

struct M2 {   // bad: incomplete set of copy/move/destructor operations
 public:
        // ...
        // ... no copy or move operations ...
        ~M2() { delete[] rep; }
 private:
        pair<int, int>* rep;  // zero-terminated set of pairs
};

void use()
{
    M2 x;
    M2 y;
    // ...
    x = y;   // the default assignment
    // ...
 }

デストラクタに「特別な注意」が必要だったことを考えると(ここでは、deallocateする)、暗黙的に定義されたコピーとムーブの代入演算子が正しくなる可能性は低い(ここでは、double deletionを得ることになる)。

unique_ptrについて

C++ではポインタ管理用にunique_ptrが追加されており、これの使い方と解決できる問題をちょっとまとめ。

生ポインタの問題

生ポインタ(rawポインタ)をそのまま使うということはC言語からずっと行われており、C、C++に慣れた方なら言語もうみんな違和感がなくなっている。
しかし、rawポインタの操作には実はどうしても不明瞭な部分があり、それをプログラマーが解決している。

例えば、

Widget* createWidget(int key)

という関数があったとして、これを利用する部分が、

int function()
{
    Widget* p = createWidget(key);
    if (p != nullptr)

    bool ret = p->show();
    if (ret)
    {
        delete p; 
        return -1;
    }
     
     delete p;
     return 1;
}

となっている。

rawポインタだと実は上記のコードを見るだけでは安全とは断言できなくて、
実は次の点が安全か疑問が残るんです。

解放時にdelete pとしているけど本当に大丈夫?

createWidgetの宣言からわかるのはWidget*だけだから、解放の仕方は次の1~3のパターンがあって、(4は注意すること)
コーディング時にプログラマが解放の仕方を調べて安全にする必要がある。

  1. delete不要。例:戻り値がstaticな領域のポインタ、クラスメンバのポインタ
  2. delete[]。例:配列のポインタを返す
  3. 解放用の関数がある。例:free関数がある
  4. 全パスで一度だけ解放する。(リーク対策、2重開放対策)

unique_ptr

unique_ptrを使用するとcreateWidgetを使用する側は受け取ったポインタの消し方を考える必要がなくなる。

std::unique_ptr<Widget>
createWidget()
{
	std::unique_ptr<Widget> p;
	p.reset(new Widget());
	return p;
}

int main()
{
	auto p = createWidget();
	if (p)
	{
		return -1;
	}

	bool ret = p->show();
	if (ret)
	{
		return -1;
	}

	return 1;
}

unique_ptrはただ一人がrawポインタを所有するという特性を持つため、createWidgetからポインタの所有権を移譲されたため、

  1. delete不要。例:戻り値がstaticな領域のポインタ、クラスメンバのポインタ

 →この場合はそもそも戻り値の型を参照型にすればいいと思う

  1. delete[]。例:配列のポインタを返す

 →解放の仕方はunique_ptrで配列だと勝手にdelete []を呼んでくれる

  1. 解放用の関数がある。例:free関数がある

 →deleterを定義できる

  1. 全パスで一度だけ解放する。(リーク対策、2重開放対策)

 →RAIIになっているので必ずスコープを外れると解放される。ポインタの受け渡しはmoveのため2重開放を起こさない

deleterを定義できる
auto
createWidget()
{
	auto deleter = [](Widget* p)
	{
		//
		Widget::free(p);
	};

	std::unique_ptr<Widget, decltype(deleter)> p(nullptr, deleter);
	p.reset(new Widget());
	return p;
}

int main()
{
	auto p = createWidget();
	if (p)
	{
		return -1;
	}

	bool ret = p->show();
	if (ret)
	{
		return -1;
	}

	return 1;
}

Futureについてメモ

Future:未来→先物時間がかかる処理を行う際に、とりあえずFutureだけをもらってあとで結果を取得する。
非同期処理の結果を受け取るということを簡単に行うための仕組み
〇例

int foo() { /* 重たい処理 */ }

int main()
{
  // 新たなスレッドで関数foo()を非同期実行
  {
  std::future<int> f = std::async(std::launch::async, foo);
    //fooが別スレッドで実行されている  
    
    /* 何か別の処理 */

    int result = f.get();
    //getで結果が取得できるまでまつ
 }

■疑問
並列に処理を行ってスループットは上げられるけど、結局getで待つの?
これだと重い処理がファイルIOとかで時間の予測も難しいから非同期にしたいというときに使えない。
→処理終了時の振舞いを登録する仕組みがある

■Future then

#include <future>

using namespace std;
    
int main() {
    future<int> f1 = async([]() { return 123; });
    future<string> f2 = f1.then([](future<int> f) {
                                                           return f.get().to_string(); }
                                                        );
}

f1が完了した後の動作をthenに書くことができる。
C++14では上記の書き方はできない。
  将来的には対応するリストにまだある。
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3558.pdf

■Futureを使うことのメリット
以下の書籍がとても参考になりました。使い方の説明はWebにたくさんあるのですが、設計として何を意図しているかを説明してくれています。

f:id:konboi_kun:20190331213638j:plainf:id:konboi_kun:20190331213730j:plain

マルチスレッドを意識しているのは誰?
Host:スレッド起動しているから
FutureData:Dataのget/setで競合を防ぐ必要があるから

実際にDataを作成する一番ロジックが入るRealDataに非同期を意識しなくてよくなる。
これって、Humble Object :https://msdn.microsoft.com/ja-jp/magazine/dn818494.aspxにつながるのだろうか。

■オマケ
Qtを使って時間のかかる非同期処理をさせながら、UIを止めずに操作できるか試している
GitHub - DaisukeSawada/Future_Training

Ruby gem インストール場所確認

gem environmentを使うとでてくる。
INSTALLATION DIRECTORY:~~

VSCodeRubyデバッグする方法で調べてた
VSCodeでRubyコードのデバッグができる環境を構築する(2017年2月現在) - 土屋つかさのテクノロジーは今か無しか

Modern C++ enumにはスコープが設定できる

職場がC++11/14に対応してきたのですが、今までなんちゃってModern C++だったので復習を兼ねてメモ。

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

項目10:enumにはスコープを設ける

enum class Color { black, red, blue}; // C++11からはclassを付けるとスコープを限定できる

auto black = false;
//OK. C++98であるようにenum ColorだとNGになる
//enumの中の値が実質グローバル変数のようになる
//過去は対策として、enum Color = {color_black, ....}のようなことをしていた。

if (red < 10.2) //NG. 暗黙の型変換はできない
{
//
}

//別ヘッダファイルでは前方宣言可能
enum class Color; //前方宣言可能

まとめ:
・スコープを限定する→列挙子がグローバル変数になるのを避ける
・暗黙の型変換が不可になる
・前方宣言が減らせる→コンパイル時のファイル依存が減る

The type signature for ‘factorial’ lacks an accompanying binding

参考書とかで以下のコードをghciで入力しようと、タイトルのようなエラーがでると思います。
うまくいかないときはファイルに書いてロードするといい。
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial(n-1)

ghciで上のコード一行ずつ入力してもエラーがでます。(宣言はあるけど定義がないなど、、、)
だから、上のコードを一旦拡張子.hsのファイルに記述して:lでロードするといいです。

Haskellメモ

■型クラス

Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool

=>の前にあるのが型クラス制約というもので、"型aはEq型でないといけない"ことを意味する。
なので、全体では"==は同じ型aの引数をとって、型aをとってBOOLを返す関数です。そして型aはEq型のインスタンスである必要があります"
ということを意味しています。

型クラスの例とサポートしている(しないといけないふるまい)
Eq型:==、/=
Ord型:>、<、>=、<=
Show型:インスタンスを文字列出力可能であること