新規記事の投稿を行うことで、非表示にすることが可能です。
2018年06月29日
《その417》XAML(Imageタグ)
XAML(Imageタグ)
コードの文字数は多いですが、VS2017 は XAMLコードを自動生成してくれるので助かります。
◆ ソリューションエクスプローラー -> App##(Universal Windows)
Assets を右クリック
◆ 追加(D) -> 既存の項目(G)
◆ 取り込む画像ファイルを選択
◆ 表示(V) -> ツールボックス(X)
Imageコントロールをフォーム上にドラッグ&ドロップ
◆ 自動生成された Imageタグを編集します。
取り込んだ画像のサイズに合わせて、Height="80", Width="80" にしました。
また、HorizontalAlignment="Left" VerticalAlignment="Top" なので、
Margin は左と上だけ決めます。とりあえず 左100, 上100 にしました。
<Image HorizontalAlignment="Left" Height="80" Margin=
"100,100,0,0" VerticalAlignment="Top" Width="80"/>
◆ Image のプロパティで、img05.png を選択しました。
それと、コントロールに image という名前を付けました。
◆ 画像が表示されました。
◆ Imageタグに、Source="img05.png" が追加されています。
<Image x:Name="image" HorizontalAlignment="Left" Height="80" Margin=
"100,100,0,0" VerticalAlignment="Top" Width="80" Source="img05.png"/>
以上の操作は 本ブログ《368》で行った作業と全く同じですが、XAMLタグを確認するために復習してみました。
以下の "MainPage.xaml.cpp のように、左Margin と 上Margin を変数 x, y としておき、その値を変更することで画像を動かすことができます(これも《377》〜《393》辺りで確認済みです)。
今回は、XAML がテーマなので、画像は、単純に 左上から右下に動くだけです。
//
// MainPage.xaml.cpp
// MainPage クラスの実装。
//
#include "pch.h"
#include "MainPage.xaml.h"
using namespace App14;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
MainPage::MainPage()
{
InitializeComponent();
StartTimer();
}
float x;
float y;
void MainPage::StartTimer() {
auto timer = ref new Windows::UI::Xaml::DispatcherTimer();
TimeSpan span;
span.Duration = 1000;
timer->Interval = span;
timer->Start();
auto rcpt
= timer->Tick +=
ref new EventHandler<Object^>(this, &MainPage::OnTick);
}
void MainPage::OnTick(Object^ sender, Object^ e) {
x += 1; y += 1;
// Thickness(x, y, 0, 0) で、Margin(x, y, 0, 0) を指定しています。
image->Margin = Windows::UI::Xaml::Thickness(x, y, 0, 0);
}
《その416》XAML(TextBoxタグ)
XAML(TextBoxタグ)
本ブログの《364》〜《399》では、XAMLコードを 全て VS2017 に自動作成してもらいました。
このまま XAML の知識ゼロで先に進むのは ちょっと不安なので、基礎の基礎くらいは知っておきたいと思います。
XAML を使える人には、申しわけありません m(_ _)m
以下が、今回の手順です。
◆ ファイル(F) -> 新規作成(N) -> プロジェクト(P)
◆ Visual C++ -> Windowsユニバーサル -> 空白のアプリ(ユニバーサルWindows)
◆ そのまま OK で いいと思います。
◆ MainPage.xaml を開きます。
◆ 次の4行を追加して、Gridコントロールの中にTextBoxコントロールを4つ配置します。
<TextBox HorizontalAlignment
="Left" VerticalAlignment="Top" Text="あいうえお" />
<TextBox HorizontalAlignment
="Right" VerticalAlignment="Top" Text="かきくけこ" />
<TextBox HorizontalAlignment
="Left" VerticalAlignment="Bottom" Text="さしすせそ" />
<TextBox HorizontalAlignment
="Right" VerticalAlignment="Bottom" Text="たちつてと" />
コードを一から入力するのは大変なので、
表示(V) -> ツールボックス(X)
TextBoxコントロールをフォーム上の適当な位置にドラッグ&ドロップ
として、コードの自動生成後に、それを編集するようにします。
◆ デバッグ(D) -> デバッグなしで開始(H)
アプリケーションの実行画面です。
◆ App.xaml を開きます。
◆ コード中の Applicationタグをクリック
◆ プロパティの RequestedTheme を Dark に設定
◆ デバッグ(D) -> デバッグなしで開始(H)
アプリケーションの実行画面です。
2018年06月28日
《その415》Platform::Array
Platform::Array
Windows Runtime が提供する Platform::Array は、1次元配列を扱う参照型クラスです。
C++/CX においても、Platform::Array よりずっと使いやすい std::array(あるいは、
より強力な std::vector)を使うべきですが、ABI を介して配列の受け渡しをする場合には
Platform::Array型を使う必要があります。
※ABI : Application Binary Interface
Platform::Array は、std::array に似ています。
以下は、下記のプログラムに沿った説明です。
◆ Platform::Array型オブジェクト ary1, ary2 を作成します。
Array<String^>^ ary1 = ref new Array<String^>(3);
ary1[0] = "愛媛県";
ary1[1] = "神奈川県";
ary1[2] = "福島県";
Array<int>^ ary2 = { 100, 200, 300 };
◆ 配列の長さを確認します。
std::cout << ary1->Length << '\n';
std::cout << ary2->Length << '\n';
◆ foreach文で、全要素を出力表示させます。
for (auto x : ary1) {
// Platform::String^型を std::wstring型に変換します。
std::wstring wstr(x->Data());
std::wcout << wstr << ' ';
}
for (auto x : ary2) {
std::cout << x << ' ';
}
◆ ptr は ary2 の最初の要素へのポインタです。begin() は最初の要素へのポインタを返します。
int* ptr = ary2->begin();
◆ *ptr, *(ptr + 1) は、ary2 の 最初の要素 と 2番目の要素です。
また、end() は、最後の要素の次(実際には存在しない)へのポインタを返します。
したがって、*(ary2->end() - 1) は ary2 の最後の要素です。
std::cout << *ptr << ' '
<< *(ptr + 1) << ' '
<< *(ary2->end() - 1) << '\n';
#include <iostream>
#include <string>
using namespace Platform;
int main(Array<String^>^ args)
{
setlocale(LC_ALL, "Japanese");
Array<String^>^ ary1 = ref new Array<String^>(3);
ary1[0] = "愛媛県";
ary1[1] = "神奈川県";
ary1[2] = "福島県";
Array<int>^ ary2 = { 100, 200, 300 };
std::cout << ary1->Length << '\n';
std::cout << ary2->Length << '\n';
for (auto x : ary1) {
std::wstring wstr(x->Data());
std::wcout << wstr << ' ';
}
std::wcout <<'\n';
for (auto x : ary2) {
std::cout << x << ' ';
}
std::cout << '\n';
int* ptr = ary2->begin();
std::cout << *ptr << ' ' // 100
<< *(ptr + 1) << ' ' // 200
<< *(ary2->end() - 1) << '\n'; // 300
}
2018年06月27日
《その414》ガベージコレクション
ガベージコレクション
std::wstring* p
= new std::wstring(L"あいうえお");
で作成した文字列インスタンスを格納するメモリ領域は、明示的に
delete p;
として解放しない限り、メモリを占有し続けます。
for (int i = 0; i < 5000; i++)
for (int j = 0; j < 5000; j++)
std::wstring* p = new std::wstring(L"あいうえお");
のようなコードでは、メモリの解放をしないまま新しい文字列インスタンスを作り続けているので、メモリ確保に失敗してエラーになる可能性があります。
※メモリ確保に失敗した場合には、例外 bad_alloc が送出されます。
一方、
Platform::String^ s
= ref new Platform::String(L"あいうえお");
のように、ref new を用いて生成された参照クラスのインスタンスでは、不要になった場合にはシステムにより自動的にメモリ解放されます。
したがって、メモリ解放のための明示的なコードを必要としません。
このような、メモリの自動解放の仕組みのことを、ガベージコレクションと呼んでいます。
したがって、
for (int i = 0; i < 5000; i++)
for (int j = 0; j < 5000; j++)
Platform::String^ s
= ref new Platform::String(L"あいうえお");
std::wcout.imbue(std::locale("japanese"));
std::wcout << L"作業完了\n";
の場合には、例外 bad_alloc が送出されることなく、無事に "作業完了" します。
なお、上記の p はポインタと呼ばれるのに対して、上記の s はハンドルと呼ばれることがあります。
ポインタの場合、クラスのメンバにアクセスする際には、例えば、
p->length();
のようにしますが、ハンドルの場合も、同じように、
s->Length();
と書くことができます。
最初のプログラムでは、必要なメモリ解放をしていないため、例外 bad_alloc が送出されます。
#include <iostream>
int main(Platform::Array<Platform::String^>^ args)
{
try {
for (int i = 0; i < 5000; i++)
for (int j = 0; j < 5000; j++)
std::wstring* p
= new std::wstring(L"あいうえお");
std::wcout.imbue(std::locale("japanese"));
std::wcout << L"作業完了\n";
}
catch (std::bad_alloc& e) {
std::cout << e.what() << '\n';
}
}
次のプログラムでは、ガベージコレクションによるメモリの自動解放が行われるため、例外 bad_alloc は送出されません。
#include <iostream>
int main(Platform::Array<Platform::String^>^ args)
{
try {
for (int i = 0; i < 5000; i++)
for (int j = 0; j < 5000; j++)
Platform::String^ s
= ref new Platform::String(L"あいうえお");
std::wcout.imbue(std::locale("japanese"));
std::wcout << L"作業完了\n";
}
catch (std::bad_alloc& e) {
std::cout << e.what() << '\n';
}
}
2018年06月26日
《その413》文字列型の変換(Platform::String, wstring, wchar_t const*)
文字列型の変換(Platform::String, wstring, wchar_t const*)
下記のプログラムでは、
Platform::String型
wstring型
wchar_t const*型
の各文字列の型を 相互に変換しています。以下は、プログラムに沿っての説明になります。
【1】 unicodeの日本語文字が出力表示されるようにロケールを設定します。
setlocale(LC_ALL, "Japanese");
wcout.imbue(std::locale("japanese")); の記述も用いられます。
【2】 文字列リテラルで String^変数を初期化する際には、文字列の前に 'L' の挿入は必要ありません。
String^ str1 = "あいうえお_ABCDE";
String^ str1 = L"あいうえお_ABCDE"; としても、もちろんかまいません。
◆◆◆ String型 から wchar_t const型,wstring型 への変換
【3】 String型から wstring型への変換です。
wstring str2(str1->Data());
wstring str2(str1->Begin()); としても同じ結果です。
【4】 String型から wchar_t const*型への変換です。
wchar_t const* str3(str1->Data());
wchar_t const* str3(str1->Begin()); としても同じ結果です。
str2, str3 を出力表示します。
str2 は wstring型
str3 は wchar_t const*型です。
【5】 wstring の replace関数で、文字列の一部を置換えます。
wstring rep(L"かさたな");
str2.replace(1, 4, rep);
str2 を出力表示します。
◆◆◆ wchar_t const型 と wstring型 の間の相互変換
【6】 wstring型から wchar_t const*型への変換です。
wchar_t const* str4 = str2.c_str();
【7】 wchar_t const*型から wstring型への変換です。
wstring str5 = wstring(str3);
◆◆◆ wchar_t const型,wstring型 から String型 への変換
【8】 wchar_t const*型から String型への変換です。
String^ str6 = ref new String(str4);
【9】 wstring型から String型への変換です。
String^ str7 = ref new String(str5.c_str());
◆◆◆ auto を使用した String型 から wchar_t const*型 への変換
【10】 auto を使用した場合は、wchar_t const*型になります。
auto str8(str6->Data());
auto str9(str7->Data());
以下はプログラムです。
#include <iostream>
#include <string>
using namespace Platform;
using namespace std;
int main(Array<String^>^ args)
{
//【1】
setlocale(LC_ALL, "Japanese");
// wcout.imbue(std::locale("japanese"));
//【2】
String^ str1 = "あいうえお_ABCDE";
//【3】
wstring str2(str1->Data());
// wstring str2(str1->Begin());
//【4】
wchar_t const* str3(str1->Data());
// wchar_t const* str3(str1->Begin());
wcout << "(1) " << str2 << '\n';
wcout << "(2) " << str3 << '\n';
//【5】
wstring rep(L"かさたな");
str2.replace(1, 4, rep);
wcout << "(3) " << str2 << '\n';
//【6】
wchar_t const* str4 = str2.c_str();
//【7】
wstring str5 = wstring(str3);
//【8】
String^ str6 = ref new String(str4);
//【9】
String^ str7 = ref new String(str5.c_str());
//【10】
auto str8(str6->Data());
auto str9(str7->Data());
cout << "(4) " << typeid(str8).name() << '\n';
cout << "(5) " << typeid(str9).name() << '\n';
wcout << "(6) " << str8 << '\n';
wcout << "(7) " << str9 << '\n';
}
2018年06月25日
《その412》C++/CX のクラス
C++/CX のクラス
● C++/CX のクラスは、refクラスと呼ばれる参照型です。
● クラスオブジェクトのインスタンスはメモリのヒープ領域に置かれ、そのインスタンスを表す変数がインスタンスを参照します。
refクラスは例えば次のようにして作成します。
public ref class C_name sealed {
・・・・・・
};
※先頭の public はアクセス修飾子です。
Windows ランタイムの型は名前空間内で宣言されますが、public修飾子のあるクラスは、
宣言された名前空間外の Windowsランタイムコンポーネントから参照できます。
アクセス修飾子は省略することができ、省略した場合は、private になります。
※クラス名に続く sealed はクラス修飾子です。
sealed の場合は、基底クラスとして使うことが禁止されます。 クラス修飾子は省略できます。
以下は、下記プログラムについてです。
● ref クラスの変数を宣言する際には ^(ハット演算子)を使います。ref new でインスタンスが作られます。
C^ c_01 = ref new C;
● オブジェクトのインスタンスメソッドにアクセスするには -> を用います。
c_01->f();
#include <iostream>
ref class C {
public:
void f() { std::wcout << L"ABCDE" << "\n"; }
};
int main(Platform::Array<Platform::String^>^ args)
{
C^ c_01 = ref new C;
c_01->f();
}
2018年06月24日
《その411》int main(Array^ args)
int main(Array<String^>^ args)
前回《410》のプログラムの main関数は
int main(Platform::Array<Platform::String^>^ args) {
・・・・・・
}
のようになっていました。
その理由ですが、
int main() {
・・・・・・
}
として実行すると、
「warning C4447: スレッド モデルのない 'main' シグネチャが見つかりました。
'int main(Platform::Array<Platform::String^>^ args)' の使用を検討してください。」
という警告が出るためです。
つまり、コマンドライン引数を受け取る形式で記述せよ、ということのようです。
次のプログラムは、受け取ったコマンドライン引数を表示します。
プログラム名は test.exe, 与えている引数は "ABCDE", "12345", "XYZ" なので、
test ABCDE 12345 XYZ
と出力されるはずです。
#include <iostream>
#include <string>
int main(Platform::Array<Platform::String^>^ args) {
for (auto pstr : args) {
wchar_t const* wcs(pstr->Begin());
std::wcout << wcs << ' ';
}
std::cout << '\n';
}
2018年06月23日
《その410》C++/CX
C++/CX
本ブログの《364》〜《399》は、UWP(Universal Windows Platform)ベースの UWPアプリについての内容でした。その際の使用言語は C++/CX です。
作成した UWPアプリは、基礎的なものばかりですが、
・時計の作成
・画像の移動
・ポインタ位置・マウスの動作・ウィンドウサイズ 等の取得と その利用
・画像ファイルやテキストファイルの読み込み
・テキストファイルの編集と保存
等を実現するものでした。
UWPアプリを扱ってみて、非同期処理についての知識が欠かせないことを実感したので、
本ブログの《400》〜《409》では、スレッドやタスクといった内容について確認しました。
そして今回からは、あらためて、C++/CX言語について学習してみようと思います。
その際、UWPベースでは C++/CX言語そのものの学習にはやや大げさになってしまうので、
《001》〜《363》や《400》〜《409》で ずっと使ってきた、コンソールプロジェクトの形式で行います。
コンソール C++プロジェクトで C++/CX を利用できるようにする手順
ファイル(F) ―> 新規作成(N) ―> プロジェクト(P)
Visual C++ ―> 空のプロジェクト
プロジェクト(P) ―> 新しい項目の追加(W)
Visual C++ ―> C++ファイル(.cpp) ―> 追加(A)
cppプログラムの編集画面が表示されました。
ソリューションエクスプローラーで Project のスパナマークをクリックします。
構成プロパティ ―> 全般 ―> 文字セット ―> Unicode文字セットを使用する
構成プロパティ ―> C/C++ ―> 全般 ―> Windowsランタイム拡張機能の使用 ―> はい(/ZW)
構成プロパティ ―> C/C++ ―> コード生成 ―> 最小ビルドを有効にする ―> いいえ(/Gm)
構成プロパティ ―> C/C++ ―> 全般 ―> 追加の #usingディレクトリ
追加の #usingディレクトリに以下の2つのパスを記入します。
「 C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.16299.0 」
「 C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\
VC\Tools\MSVC\14.13.26128\lib\x86\store\references 」
※パスは Visual Studio のインストール状況によります(上記パスは私の使用環境でのものです)。
構成プロパティ ―> C/C++ ―> コマンドライン ―> 追加のオプション
「 /Zc:twoPhase- 」 を記入します。
※この設定項目は、VS2017 v15.3 以降の場合です。
構成プロパティ ―> リンカー ―> 詳細設定 ―> エントリポイント
「 mainCRTStartup 」を記入します。
以上の設定で、次のように C++/CX が使えるようになりました("ref new" などが使えます)。
#include <iostream>
ref class C
{
public:
void f(int n) {
for (int i = n; i < n + 10; i++)
std::cout << i << " ";
std::cout << '\n';
}
};
int main(Platform::Array<Platform::String^>^ args) {
(ref new C)->f(10);
}
2018年06月21日
《その409》タスク の実行
タスク の実行
下記のプログラムにおいて、タスク t1 は a の値を4乗した値を返します。
auto t1 = task<int>([&a]() {
return a = a * a * a * a ;
});
しかし、定義した直後の
cout << a << '\n';
では、4乗された値ではなく、最初の a の値が表示されます。
このことから、t1 は、生成されただけで実行はされていないことがわかります。
次の、
t1.wait();
cout << a << '\n';
では、4乗された値が表示されることから、タスクの実行完了を待つ wait がタスクを実行させることがわかります。
以下はプログラムです。
#include <iostream>
#include <ppltasks.h>
using namespace concurrency;
using namespace std;
int main() {
int a = 5;
auto t1 = task<int>([&a]() {
return a = a * a * a * a ;
});
cout << a << '\n';
t1.wait();
cout << a << '\n';
}
《その408》コンストラクタによる taskオブジェクトの生成
下記のプログラムでは、まず、create_task関数で taskオブジェクトを作っています。
then関数により処理が継続し、t1 〜 t4 の順に実行されます。
auto t1 = create_task([] { cout << "aaaaaa\n"; });
auto t2 = t1.then([] { return "xxxxxx"; });
auto t3 = t2.then([](string str) { cout << 100 << '\n'; });
auto t4 = t3.then([] { return 888; });
コンストラクタによる taskオブジェクトの生成
クラステンプレート task<> を用いて、
返却値無しの taskオブジェクは、コンストラクタ task<void> により、
返却値が int型の taskオブジェクは、コンストラクタ task<int> により、
返却値が string型の taskオブジェクは、コンストラクタ task<string> により、
それぞれ生成することができます。
t4 の終了を待って t5, t6 の処理が行われますが、どちらが先に完了するかは決まっていません。
t4.wait();
auto t5 = task<void>([] { cout << "bbbbbb\n"; });
auto t6 = task<string>([] { return "yyyyyy"; });
t5 の終了を待って t7, t8 の処理が行われますが、どちらが先に完了するかは決まっていません。
t5.wait();
auto t7 = task<void>([] { cout << 200 << '\n'; });
auto t8 = task<int>([] { return 999; });
以上から、
画面出力をする task の実行は、t1, t3, t5, t7 の順に行われることになります。
最後に、t7 の終了を待って t2, t4, t6, t8 の返却値を表示します。
t7.wait();
cout << t2.get() << '\n';
cout << t4.get() << '\n';
cout << t6.get() << '\n';
cout << t8.get() << '\n';
以下はプログラムです。
#include <iostream>
#include <ppltasks.h>
#include <string>
using namespace concurrency;
using namespace std;
int main() {
// create_task関数で taskオブジェクトを作る例
auto t1 = create_task([] { cout << "aaaaaa\n"; });
auto t2 = t1.then([] { return "xxxxxx"; });
auto t3 = t2.then([](string str) { cout << 100 << '\n'; });
auto t4 = t3.then([] { return 888; });
// コンストラクタで taskオブジェクトを作る例
t4.wait();
auto t5 = task<void>([] { cout << "bbbbbb\n"; });
auto t6 = task<string>([] { return "yyyyyy"; });
t5.wait();
auto t7 = task<void>([] { cout << 200 << '\n'; });
auto t8 = task<int>([] { return 999; });
t7.wait();
cout << t2.get() << '\n';
cout << t4.get() << '\n';
cout << t6.get() << '\n';
cout << t8.get() << '\n';
}