読者です 読者をやめる 読者になる 読者になる

需要のないページ

プログラミングや趣味や。

定数M_PIが使えない

能書き

かなり間があいたので、小ネタ。

毎度ながら、知ってる人にとっては当たり前の内容を有難そうに書く。

 

遭遇したエラー

例えば、次のようなコードを書いたとする。

まあ、半径から円の面積を計算するだけの、よく見るコードだ。

 

ただし、円周率を自前で定義しているあたり、このコードはモダンでない。

_USE_MATH_DEFINESフラグを立ててcmathをインクルードしたほうがよい。

上記のコードのdefine文を単に置き換えると次のようになる。

しかし、このコードをコンパイルすると... 次のようなエラーが出る。

error C2065: 'M_PI': 定義されていない識別子です。
error C2065: 'M_PI': undeclared identifier

 

原因と対策

実はこれ、ヘッダファイルを読み込む順序が問題なのである。

エラーが出たコードでは、フラグを立てる前にiostreamを読み込んでいた。

 

iostreamヘッダのインクルードを追ってみると:

  • iostreamは、istreamをインクルードしている。
  • istreamは、ostreamをインクルードしている。
  • ostreamは、iosをインクルードしている。
  • iosは、xlocnumをインクルードしている。
  • xlocnumは、cmathをインクルードしている。

巡り巡って、iostreamをインクルードすると、cmathが読み込まれる。

もちろんその時点では_USE_MATH_DEFINESフラグは立っていない。

 

解決策は、次のとおり。

マクロの順序を変更しただけ。

ヘッダファイルに関するフラグは、include文より前に定義しよう。

 

雑記:前もって定義されている数学定数

#define _USE_MATH_DEFINES

フラグを立ててcmathをインクルードすると、次の数学定数を利用できる。

f:id:LouiS:20170506160658j:plain

ネイピア数含め対数周りの定数と、円周率に関する定数。

一番下の定数を、M_1_SQRT2と改名すべきと思うのは私だけだろうか?

 

雑記:ぼやき

iostreamに、cstdlibやcmathがインクルードされてるっていったい何なんだ。

いわゆる神モジュールになっている気が...

 

 

Qtでソケット通信を試してみた(UDP)

能書き

トランスポート層プロトコルは主に二種類ある。

  • 確実だけどめんどくさいTCP
  • 不確実だけど楽チンなUDP

今回はQtのQUdpSocketを用いたUDPのソケット通信を試してみた。

ネットワーク通信については詳述できないしないが、次のページが参考になる。

f:id:LouiS:20170324171137g:plain

 

また、QtのGUIの使い方は、以下のページで紹介したとおり。環境も同様。


ただし、モジュールの『Network』を有効にすること。

f:id:LouiS:20170324175701p:plain

 

サンプルコードの挙動

f:id:LouiS:20170324203640p:plain

Senderのエディットに文字列を入れてボタンを押すと、Recieverに反映される。

ただし、それぞれ別のアプリケーションとして起動する。それだけ。

 

このコードを実行するとき、このような警告が出ることがある。

両方のチェックボックスにチェックを入れて、アクセスを許可すること。

f:id:LouiS:20170324213759p:plain

 

サンプルコード―送信

送信用のウィジェットとして、次のようなものを用意する。

f:id:LouiS:20170324182843p:plain

clicked()シグナルと、ダイアログのsendDatagram()スロットを連結する。

 

まずヘッダファイル。

先のスロットと並んで、ソケットとポート番号を宣言している。

ポート番号は、ウェルノウン*1でなければ、適当に選んで大丈夫(だと思う)。 

ポート番号に関する解説は、このページが詳しい。

 

次に、ソースファイル。

23行目のwriteDatagram()第二引数には、通信先のIPアドレスを指定する。

  • QHostAddress::LocalHost
     自分自身と通信する、ループバックアドレス。実験段階で特に有用だろう。
  • QHostAddress::Broadcast
     同一ネットワーク上の全てのノードに送信するアドレス。ただし、ブロックするルータもある。
  • QHostAddress::QHostAddress( "xxx.xxx.xxx.xxx" )
     コンストラクタを用いて、IPv4アドレスを指定する方法。

 

18-21行目は、送信用のバイト列をうまく扱うための方便。

とりあえずは盲目に真似して、C++のストリームの恩恵に預かってよいだろう。

 

このように、送信側はただ『データを成形して』『送信する』だけで実装できる。

 

サンプルコード―受信側

受信側のウィジェットは単純で、ラベルが一つだけあるダイアログだ。

コードは次のとおり。

ヘッダファイルは送信側と酷似している。

ソースファイルに関しても、結局は『受信して』『データを解析して』いるだけ。

21行目から24行目は、このままでいろいろなニーズに使いまわせる。

 

受信側特有の処理は二つある。第一は、cppファイル9行目のbind()だ。

どのデータを受け取るか選ぶために、前もってポート番号を知らせる必要がある。

 

第二の特有の処理は、readyRead()シグナルと処理スロットを接続することだ。

サンプルコード10,11行目の処理にあたる。

readyRead()シグナルは、送信されたデータを読む準備が出来ると発呼される。 

 

まとめ

  • QUdpSocketクラスは、低信頼/高速の通信プロトコルを提供する。
  • 送信側/受信側が、共通のポート番号を知っておく必要がある。
  • 送受信にはQByteArrayクラスのバイト列を用いる。
    QDataStreamクラスを利用すると便利。
  • 受信側は、前もってbind処理を行っておくこと。
  • データが受信されると、readyRead()シグナルが発呼される。

案外単純である。

 

ストリームの取扱いに関しても、そのうち書く...かもしれない。

書かないかもしれない。 

 

ただし

ただし、このコードでは、短時間に連続して送られるデータには対応できない。

処理に手間取っているうちに送信されたデータは無視される。

全てのデータを扱う必要があるのなら、

hasPendingDatagrams()を用いたループ内に処理命令を書くこと。

リファレンスのサンプルコード片が参考になる。

 

これについてはそのうち書く。

*1:0~1023 予約されているポート

マルチスレッドQtアプリケーション(まとめ)

能書き

Qtでマルチスレッドを実現する方法にはいくつかある。

それらの方法や参考となるページをまとめる。

 

『マルチスレッドとは何か?』

これについては、検索すればいくらでも情報がヒットするので、特筆しない。

個人的には、『処理の流れが複数ある状態』と説明している。

 

継承を用いて実現する方法

以下のページに詳細を示した。

この方法は、おそらく最も素直な実装スタイルだと思うし、

私が参照したオライリーの入門書でもこれが紹介されていた。

 

しかし、いくつかの問題点を含むため、次の方法が現在では主流のようだ。

 

委譲を用いて実装する方法

以下のページに詳細を示した。

 

参考となるページ

QThread Class | Qt Core 5.8 | DetailsQObject Class | Qt Core 5.8 | moveToThread()

まずは公式リファレンス×2。

それぞれのページで、Qtのスレッドの扱いについて知見を深められる。

また、継承による方法、委譲による方法の両方のコードが紹介されている。

Qtでスレッドを使う前に知っておこう - Qiita

この中で唯一日本語のページ。

なぜ継承による実装が避けられるのか、その問題点を列挙している。

また、connect関数の第5引数に関しても詳説があり、非常に参考になる。

How To Really, Truly Use QThreads; The Full Explanation | Maya's Programming & Electronics Blog

委譲による方法を提言し、Qt公式リファレンスに強い影響を与えたエントリー。

従来の方法を批判する要点は次のとおり(稚拙な和訳/強引な意訳)。

  • 継承による従来の方法は、必要以上に複雑で不適当。
  • QThreadは、『スレッド』そのものではない。スレッド周辺を覆うラッパークラスだ。したがって、サブクラスがスレッドのように扱われるのは不適当。

具体的に実装する際の主な注意点は次のとおり(稚拙な和訳/強引な意訳)。

  • 別スレッドで動かすQObjectのコンストラクタでヒープ領域を確保しないこと。QObjectをインスタンス化したときは、まだ旧スレッド上にある。
  • deleteLater()スロットを適切に用いれば、クラッシュを回避できる。

マルチスレッドQtアプリケーション(2)

能書き

前回の記事の続きとして見てもよいだろうし、そうでなくてもよいだろう。

 

マルチスレッドをQtで再現する方法はいくつかあるが、

その中で、QThreadにタスクを委譲するものを紹介する。

 

簡単な例

まず、特に簡単なコードを示す。

異なるスレッド間で直接関数を呼び出すことは危険だが、ここでは無視する。

その他もいろいろいい加減なコードであるが、許してもらいたい。

 

実行結果は次のとおり。

Main: QThread(0x24230a65230)
Func: QThread(0x24230a68820)

マルチスレッドが実現されていることがわかる。

 

必要な手順

委譲を用いてマルチスレッドを実現するために必要な手順は以下のとおり。

  1. 別スレッドで実行したい処理を担うクラスを、QObjectを継承して作る。
  2. そのインスタンス(subObject)をヒープ領域に生成する。
     SubObject *subObject = new SubObject();
  3. QThreadのインスタンス(subThread)をヒープ領域に生成する。
     QThread *subThread = new QThread();
  4. subObjectの所有権をsubThreadに移す。
     subObject->moveToThread( subThread );
  5. subThreadの処理を開始する。
     subThread->start();

上記の処理は、source.cppの17-19行目に実装されている。

 

4番目の手順、5番目の手順を抜かしても、ビルドエラーは生じない。

予期通りの動作をしないときは、このようなミスをしている恐れがある。

 

異なるスレッド間で直接関数を呼び出してはいけない理由

例えば、先のコードを一部改造して、次のようなコードを書いたとする。

subThreadのstart()をコメントアウトし、シグナル/スロットを追加している。

 

実行結果は次のとおりである。

Main: QThread(0x1b9868d5300)
Func: QThread(0x1b9868d89b0)
emit complete

結果を見てわかるように、スロットが呼び出されていない!

 

これはなぜか。

実はスロットは、シグナルが発呼された直後に呼び出されるのではない。

スロットが所属するスレッドのキューに組み込まれるだけだ。

スレッドが開始されなければ、もちろんキューは処理されない。

それために、先の実行結果が出たのである。

 

直接関数を呼び出してしまっては、スレッドが開始していようがいまいが、

関係なしに即時実行してしまうこととなる。

マルチスレッドQtアプリケーション(1)

能書き

マルチスレッドをQtで再現する方法はいくつかあるが、

その中で、QThreadの継承を利用するものを紹介する。

 

コード:特にシンプルな例

まずはシンプルな例として、次のコードを書いてみた。

ヒープ領域を解放していなかったり、プログラムの終了条件がなかったり、

いろいろといい加減だが許してもらおう。

 

Q_DECL_OVERRIDEは、C++11以降ではoverrideに置換される。

このキーワードはもし無くてもエラーは生じない。

しかし、明記することで予期せぬ人的バグを回避できる。

 

QThreadのインスタンスをstart()すると、run()が内部的に呼び出される。

 

実行結果は次のとおりである。

Main: QThread(0x1881fe16160)
Sub: SubThread(0x1881fe17100)

実行結果は毎回異なるが、マルチスレッドが達成されていることは確認できる。

 

しかし、この方法には思わぬ落とし穴がある。

 

コード:うまくいかない例

直前の例に、一部を追加しただけのものだ。

マルチスレッドに親しんだ人から見れば、奇ッ怪なコードに見えることだろう。

しかし、直感的には実現できそうに写る。

コンストラクタ、メンバ関数、スロットとその呼び出しを追加した。  

 

実行結果は、次のとおりである。

Main: QThread(0x1b755747c70)
Constructor: QThread(0x1b755747c70)
Func: QThread(0x1b755747c70)
Sub: SubThread(0x1b755749ad0)
Slot: QThread(0x1b755747c70)

この実行結果の意味がわかるだろうか?

五つの出力のうち、下四つはSubThreadのメンバ関数によるものである。

しかし、スレッドのIDを見てみると、『Sub』以外はすべて同一である。

 

内的に呼び出される『run』関数以外、マルチスレッドの恩恵を受けられない。

 

原因と解決策

『原因』などと書いたが、これはQtの仕様通りの動作である。

 

『うまくいかない例』のようなインターフェースのまま、どう設計すればよいか?

継承ではなく、委譲を用いることでその要求を達成できる。

louis-needless.hatenablog.com

 

留意点

異なるスレッド間の関数呼び出しは本当は危険らしい。

面倒でもシグナル/スロットを用いると安全に実装できる。

既存のVisual StudioプロジェクトをQt用に変換する方法:オマージュ

前書き

この記事は、次のページを尋常じゃなく参考にしている。

参考というより、実行環境が違うだけだ。

 

同時に、先日の私の記事の解決編でもある。


環境

  • Windows10
  • Visual Studio 2015 Community
  • Qt 5.6.0
  • Add-in 2.0.0 for Qt5

アドインなどが正しくインストールされている前提。

 

VC++コード

例えば、次のようなコードを書いたとする。

本当は宣言と実装は分けるべきだが... ここでは許してもらおう。

このヘッダをどこかにインクルードすると、エラーが発生するはずだ。

(発生しない場合は、このページに辿り着かないと思う。)

 

解決策

ちょっと長い。

1. テキストエディタで『プロジェクト名.vcxproj』を編集。

赤線を引かれた要素を追加する。

f:id:LouiS:20170321175411j:plain

バージョンによって多少キーワードは異なると思う。

ウィザードで作成したQtプロジェクトの同要素を確認すると確実だ。

2. プロジェクトを64bit版にしておく。

プルダウンリストからx64を選んでおく。

f:id:LouiS:20170321183035p:plain

この設定をしていないと、ビルド時に次のようなエラーが出ることがある。

The following error occurred:
There's no Qt version assigned to this project for platform Win32. Please use the 'change Qt version' feature and choose a valid Qt version for this platform.

Qtのバージョンを変えるのもありっちゃあり。

3. Visual StudioでプロジェクトをQt用に変換。

メニューバーから、『Convert Project to Qt VS Tools Project』を選択。

警告が出るが、『はい』を選択すると、プロジェクトが変換される。

f:id:LouiS:20170321181331p:plain

左方のリストでソリューションを選択していると表示されない。注意。

4. 必要なモジュールを追加。

メニューバーから、『Qt Project Settings』を選択。

『Qt Modules』タブから、必要なモジュールにチェックを入れる。

f:id:LouiS:20170321182300p:plain

『Core』,『GUI』,『Widgets』があれば大体の簡単なアプリケーションは動く。

必要なモジュールを調べるには、リファレンスを参照のこと。

5. 完了!と思いきや

私の環境では、この状態でビルドするとエラーが多発する。

『追加のインクルードディレクトリ』『追加のライブラリディレクトリ』

この二つを、手動で書き直してやる必要があった。

6. それでもうまくいかないときは

MOCが適用されていない恐れがある。

ヘッダファイルを適当に編集したのちにリビルドすると上手くいくかもしれない。

それこそ、空白を入れて消すだけの編集でも構わない。

Visual Studio 2015でQt GUIプログラムを組んでみる(2)

前回の続き

louis-needless.hatenablog.com

このページの続き。

 

f:id:LouiS:20170306161515j:plain

このGUIは、QPushButtonQLineEditを使っている。

実際にはQHBoxLayoutも利用しているが、まあ無くても問題はない。

 

シグナルとスロット

せっかくボタンを付けたのだから、なにか応答がないと面白くない。

Qtでは、シグナル/スロットという独自の機構を用いてアクションを処理する。

 

アクションが検出されると、シグナルが送信され、スロットが呼び出される。

当面はスロットだけ設計ができれば問題ないだろう。

 

シグナルとスロットを実装してみる

ここでは、以下の簡単な応答を実装する。

『ボタンをクリックすると、ダイアログのタイトルを、エディタに入力された文字列に更新する』

 

シグナル/スロットは、QtDesigner上で設計することができる。

f:id:LouiS:20170314121539p:plain

ドックから上の項目を選択するか、F4キーを押せば、編集モードに移行できる。

 

次のようにして、ウィジェットどうしを接続する。

  1. 配置したPushButtonをクリック
  2. クリックした指を離さないまま
  3. 配置したダイアログ上でドロップ

そうすると、以下のようなウィンドウがポップアップするはずだ。

f:id:LouiS:20170314122210p:plain

この画面で、PushButtonとDialogを『接続』する―

実際に、CUIでコーディングする際は、『connect』というキーワードを用いる。

 

左側のリストがシグナルのリスト、右側はスロットのそれだ。

単純にクリック応答にしたいので、左側からは『clicked()』を選択する。

 

スロットはさきほど考えた、『ダイアログのタイトルを、エディタに入力された文字列に更新する』だけの要求を満たさなければならない。

しかし、そんなに都合のよいスロットは、デフォルトで存在しない。

作るのだ。

 

スロットを作る

次のようにして、先のシグナルとオリジナルのスロットを接続する。

  1. スロットリスト最下方の『編集』ボタンをクリック
  2. スロットの一覧が表示されるので、直下の『+』ボタンをクリック
  3. 『setTitleByInput()』と入力
  4. 『OK』をクリックし、ポップアップダイアログを閉じる
  5. スロットリストに追加された『setTitleByInput()』を選択
  6. 『OK』をクリックし、ダイアログを閉じる

以下のように、接続が青色になるはずだ。
(追記2017/03/21:接続が青色になるのはスロットが実装されているとき)

f:id:LouiS:20170314124344p:plain

 

スロットをコーディングする

残念ながら、ここで終わりではない。

ここまで、『clicked()』と『setTitleByInput()』を接続した。

しかし、肝心の処理内容はなにも書いていない。

 

まず、QtGuiSample.hppを、以下のように書き換える。

setTitleByInput()というメンバ関数をプロトタイプ宣言しただけだ。

しかし、スロットを明示するために、『private slots:』のラベルが必須となる。

 

次に、QtGuiSample.cppを、以下のように書き換える。

setTitleByInput()を実装する内容だ。

JavaGUI設計をしたことのある人なら、難なく理解できるだろう。

 

なお、『text()』は、オブジェクトlineEditのメンバ変数のgetterだ。

Qtは、『getXX()』という命名を慣習的にしない。

 

試してみる

以上で完成だ。プログラムを実行してみる。

f:id:LouiS:20170314132915p:plain

入力フォームに適当な文字列を入力して、ボタンをクリックする。

 

f:id:LouiS:20170314132914p:plain

要求通りの動作が確認できた。

Visual Studio 2015でQt GUIプログラムを組んでみる(1)

能書き

前にVS上でQtが動かないという記事を書いたが、結論から言うと動いた。

適当なこと言ってすみません。

 

環境

  • Windows10
  • Visual Studio 2015 Community
  • Qt Designer 5.6.0
  • Add-in 2.0.0 for Qt5

アドインなどが正しくインストールされている前提。

 

プロジェクトを作ってみる

ウィザードを活用してプロジェクトの骨組みを作る。

f:id:LouiS:20170306153513p:plain

『QtGUIApplication』を選択。

プロジェクト名とパスをちょいちょい決めて、次の画面へ。

 

f:id:LouiS:20170306154258p:plain

全ての項目が自動的に埋められる。

基底のクラス(Base Class)を、QDialogに変更しておく。

ヘッダファイルの拡張子がhからhppに変更されているのはただの好みだ。

 

f:id:LouiS:20170306154847p:plain

モジュールの選択を求められるが、とりあえずはデフォルトでよいだろう。

 

ケルトンコードがむにゃむにゃと生成される。

内容を人手で変更してよいのは、基本的に次の五つ。

  • Form Files
    • QtGuiSample.ui
  • Header Files
    • QtGuiSample.hpp
  • Resource Files
    • QtGuiSample.qrc
  • Source Files
    • main.cpp
    • QtGuiSample.cpp

Generated Filesフィルタに含まれるファイルは絶対にいじってはいけない。

また、ここではqrcファイルの編集はしないこととする。

 

GUIを作ってみる

ケルトンコードを実行すると、次のようなダイアログが表示される。

f:id:LouiS:20170306160656p:plain

ダイアログベースなので非常にシンプルだ。

 

このままではつまらないので、簡単なウィジェットを作ってみたいと思う。

Qt Designerがインストールされていれば、コードなしにGUIを設計できる。

  1. QtGUISample.uiを開く。Qt Designerが起動するはずだ。
  2. 左側のリストからパーツをドラッグ&ドロップして、下記のGUIを完成させる。
    いろいろ試してみてほしい。

f:id:LouiS:20170306161515j:plain

 

長くなったので、残りは別のページに貼る。

louis-needless.hatenablog.com

Visual Studio2015を敢えて英語化してみる

環境

 

能書き

近日Visual Studio 2017がリリースされるというのに、時期外れの投稿。

VSを日本語化する情報は多く見られるが、意外と英語化の記事は見当たらない。

 

なぜ英語化するかと言うと、かっこいいから情報が豊富なため。

それに、一度環境を作ってしまえば、簡単に日英の言語を切り替えられる。

 

インストールまでの手順

まずLanguage packをインストールする。

Download Microsoft Visual Studio 2015 Language Pack from Official Microsoft Download Center

なぜか日本語サイトからダウンロードしたLanguage packはうまく動作しない。

英語サイトからダウンロードしよう。

 

ダウンロードしたexeファイルを開き、インストール開始。

しばらく時間がかかるので、散歩でもしていよう。

 

インストール後の手順

  1. Visual Studioを開く。
  2. メニューバーから「ツール」→「オプション」を選択。
  3. 画面左のリストから「環境」→「国際対応の設定」を選択。
  4. 「言語」を「English」に設定。
  5. Visual Studioを起動しなおす。

f:id:LouiS:20170225144557j:plain

コンピュータのリブートは必要ない。

 

エラーが出るとき

トラブルでインストールが中断された場合、VSを起動できないことがある。

Microsoft Visual Studio で、インストールされている言語リソースのバージョンの不一致が検出されました。

このようなときは、exeファイルを再度開き、リペアしてしまえばよい。

 

QLabelに画像を貼り付ける

クラスを選ぶ

Qtにおいて、画像を表示できるウィジェットは、(たぶん)主に三種類ある。

単純に画像を表示するだけなら、QLabelが簡単そうだ。

 

画像行列自体を扱うクラスも、四種類ほどある。

リファレンスによると、画像の入出力および加工にはQImageが、

画像の表示にはQPixmapが適しているらしい。

 

使ってみる

  • fullpath... 画像ファイルのフルパスのポインタ (QString*)
  • mLabel... 貼り付ける先のラベルのポインタ (QLabel*)

が既に得られているとき、次のように画像を貼り付けられる。

せっかくなのでラベルのサイズに画像を合わせてみた。

なお、メンバ関数scaleの第2引数で引き伸ばし方を設定できる。

詳しくはQPixmap::scaledまで。

 

例外処理してないのはご愛嬌。