八葉の日記

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

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

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型:インスタンスを文字列出力可能であること

めも

Humble Object at XUnitPatterns.com


Example: Humble Dialog

Many development environments let us build the user interface
visually by dragging and dropping various visual objects ("widgets")
onto a canvas.
多くの開発環境では、さまざまなビジュアルオブジェクト(「ウィジェット」)を
キャンバスにドラッグアンドドロップすることで、ユーザーインターフェイスを視覚的に
構築できます。

They let us add behavior to these visual objects by selecting one of
the possible actions or events specific to that visual object and typing
logic into the code window the IDE presents us with.
これらのビジュアルオブジェクトには、ビジュアルオブジェクトに固有の可能なアクション
またはイベントの1つを選択し、IDEが提示するコードウィンドウにロジックを入力することによって、
ビジュアルオブジェクトにビヘイビアを追加できます。

This logic may involve invoking the application behind the user interface or it
may involve modifying the state of this or other visual objects.
このロジックは、ユーザインタフェースの背後でアプリケーションを呼び出すことを伴い得るか、
またはこのまたは他の視覚的オブジェクトの状態を変更することを含み得る。

Visual objects are very hard to test efficiently because they are tightly coupled
to the presentation framework that invokes them.
ビジュアルオブジェクトは、それらを呼び出すプレゼンテーションフレームワークに密接に結合されて
いるため、効率的にテストすることは非常に困難です。

The test would need to simulate that environment to provide the visual object with
all the information and facilities it requires.
テストでは、ビジュアルオブジェクトに必要なすべての情報と機能を提供するために、その環境を
シミュレートする必要があります。

This makes testing very complicated, so much so that many development teams don't
bother testing the presentation logic at all.
これはテストを非常に複雑にするので、多くの開発チームはプレゼンテーションロジックのテストを
全く気にしません。

This leads to Production Bugs caused by untested code and Untested Requirements.
これは、テストされていないコードとテストされていない要件によって引き起こされるProduction Bugsに
つながります。

To create the Humble Dialog, we extract all the logic from the view component into
a non-visual component that is testable via synchronous tests.
ハンブルダイアログを作成するために、ビューコンポーネントからロジックのすべてを非同期テストで
テスト可能な非ビジュアルコンポーネントに抽出します。

If this component needs to update the view object's (the Humble Dialog's) state, the Humble Dialog is passed in as an argument.
このコンポーネントがビューオブジェクト(ハンブルダイアログ)の状態を更新する必要がある場合、ハンブルダイアログが引数として渡されます。

When testing the non-visual component, we typically replace the Humble Dialog with a Mock Object that is configured with the indirect input values and the expected behavior (indirect outputs.)
非ビジュアルコンポーネントをテストするときは、通常、間接入力値と予想される動作(間接出力)で構成されたモックオブジェクトをハンブルダイアログに置き換えます。

In some GUI frameworks, where the Humble Dialog has to register itself with the framework for each event it wishes to see, the non-visual component can register itself instead of the Humble Dialog (as long as that doesn't introduce unmanageable dependencies on the context) and this makes the Humble Dialog even simpler because these events go directly to the non-visual component and require no delegation logic
Humble Dialogが見たい各イベントのフレームワークに自分自身を登録しなければならないGUIフレームワークでは、非ビジュアルコンポーネントは、ハンブルダイアログの代わりに自身を登録することができます。コンテキスト)、これらのイベントは非ビジュアルコンポーネントに直接移動し、委譲ロジックを必要としないため、Humble Dialogをさらに単純化します

The following code sample is from a VB view component (.ctl) with some non-trivial logic.
次のコードサンプルは、VBのビューコンポーネント(.ctl)のロジックです。

It is part of a custom plug-in we built for Mercury Interactive's TestDirector tool.
Mercury InteractiveのTestDirectorツール用に作成したカスタムプラグインの一部です。

///////////////////////////////////////////////////////////////////////////////
' Interface method, TestDirector will call this method
' to display the results.Public Sub ShowResultEx(TestSetKey As TdTestSetKey, _ TSTestKey As TdTestKey, _
ResultKey As TdResultKey)
Dim RpbFiles As OcsRpbFiles
Set RpbFiles = getTestResultFileNames(ResultKey)
ResultsFileName = RpbFiles.ActualResultFileName
ShowFileInBrowser ResultsFileName
End Sub

Function getTestResultFileNames(ResultKey As Variant) _ As OcsRpbFiles
On Error GoTo Error
Dim Attachments As Collection
Dim thisTest As Run
Dim RpbFiles As New OcsRpbFiles
Call EnsureConnectedToTd
Set Attachments = testManager.GetAllAttachmentsOfRunTest(ResultKey)
Call RpbFiles.LoadFromCollection(Attachments, "RunTest")
Set getTestResultFileNames = RpbFiles
Exit Function
Error: ' do something ...End Function
Example VbViewWithLogic embedded from VB6/HumbleDialog-Not/ResultViewerPane.ctl
///////////////////////////////////////////////////////////////////////////////

=========================================================
Ideally, we would like to test the logic but we are unable to construct the objects passed in as parameters because they don't have public constructors.
理想的には、ロジックをテストしたいのですが、パブリックコンストラクタを持たないため、渡されたオブジェクトをパラメータとして構築することはできません。

Passing in objects of some other type isn't possible either because the types of the function parameters are hard-coded to be specific concrete classes.
関数のパラメータの型が特定の具象クラスにハードコードされているため、他の型のオブジェクトを渡すことはできません。

We can do an Extract Testable Component (page X) refactoring on the executable to create the testable component leaving behind just the Humble Dialog as an empty shell.
テスト可能なコンポーネントを作成して空のシェルとしてHumble Dialogだけを残すように、実行可能ファイルに対してTestable Component(page X)のリファクタリングを行うことができます。

This typically involves doing several Extract Method refactorings (already done in the original example to make the refactoring easier to understand), one for each chunk of logic that we want to move.
これには、通常、リファクタリングを理解しやすくするために元の例で行っていたいくつかの抽出メソッドリファクタリングを実行します。これは、移動するロジックの各チャンクに対して1つです。

We then do an Extract Class refactoring to create our new testable component class.
その後、Extract Classリファクタリングを実行して、新しいテスト可能コンポーネントクラスを作成します。
The Extract Class refactoring may include both Move Method[Fowler] refactorings and Move Field[Fowler] refactoring to move the logic and the data it requires from the Humble Dialog to the new testable component.
エクストラクトクラスリファクタリングには、Moveメソッド[Fowler]リファクタリングとMove Field [Fowler]リファクタリングの両方が含まれており、必要なロジックとデータをハンブルダイアログから新しいテスト可能コンポーネントに移動できます。
Here's the same view converted to a Humble Dialog:
ここでは、同じビューをハンブルダイアログに変換します:

///////////////////////////////////////////////////////////////////////////////
' Interface method, TestDirector will call this method
' to display the results.Public Sub ShowResultEx(TestSetKey As TdTestSetKey, _ TSTestKey As TdTestKey, _
ResultKey As TdResultKey)
Dim RpbFiles As OcsRpbFiles
Call EnsureImplExists
Set RpbFiles = Implementation.getTestResultFileNames(ResultKey)
ResultsFileName = RpbFiles.ActualResultFileName
ShowFileInBrowser ResultsFileName
End Sub

Private Sub EnsureImplExists() If Implementation Is Nothing Then Set Implementation = New OcsScriptViewerImpl
End If
End Sub
Example VbViewAsHumbleDialog embedded from VB6/HumbleDialog/ResultViewerPane.ctl
///////////////////////////////////////////////////////////////////////////////

以下は、ハンブルオブジェクトが呼び出すテスト可能なコンポーネントOcsScriptViewerImplです:
///////////////////////////////////////////////////////////////////////////////
' ResultViewer Implemenation:
Public Function getTestResultFileNames(ResultKey As Variant) _ As OcsRpbFiles
On Error GoTo Error

Dim Attachments As Collection
Dim thisTest As Run
Dim RpbFiles As New OcsRpbFiles
Call EnsureConnectedToTd
Set Attachments = testManager.GetAllAttachmentsOfRunTest(ResultKey)
Call RpbFiles.LoadFromCollection(Attachments, "RunTest")
Set getTestResultFileNames = RpbFiles
Exit Function
Error: ' do something ...End Function
///////////////////////////////////////////////////////////////////////////////

We could now instantiate this OcsScriptViewerImpl class easily and write VbUnit tests for it.
I've omitted the tests for space reasons because they don't really show anything particularly interesting.
このOcsScriptViewerImplクラスを簡単にインスタンス化し、VbUnitテストを作成することができます。私はスペースの理由でテストを省略しました。なぜなら、特に面白いことは何も実際に表示していないからです。