MFCでchar*からwchar_t*へ変換する話。この処理はMultiByteToWideChar関数で行えるが、MFCであれば、CA2Wという便利クラスでラップされているのでこれを使える。
諸事情によりマルチバイト文字セットでプロジェクトを作らなければいけないが、使用するライブラリがconst wchar_t*を要求するという状況に遭遇して調べた。
void CChildView::OnPaint() { CPaintDC dc(this); const char *szText = "テキスト"; wchar_t* wideText = ATL::CA2W(szText); ::TextOutW(dc.GetSafeHdc(), 0, 0, wideText,wcslen(wideText)); }
このCA2Wというクラスが何をやっているかというと、
1.CA2Wはコンストラクタにchar*が渡される
2.内部でwchar_t*のバッファを128byte確保
3.MultiByteToWideCharを呼び出してバッファへ格納(バッファサイズはテンプレート引数で指定可能)
4.出来上がった結果はoperator LPWSTR()によって暗黙キャストされて返却
普通、式内で作られたCA2Wオブジェクトは式が終わると同時に解放されるが、C++は一時オブジェクトが参照されている場合、現在のスコープの終わりまでライフタイムが延長されるという機能があり、これによってCA2WはwideTextと同じ長さの寿命を持てる。
下記のようになる。delete[]し忘れが怖い。CA2Wを使ったほうがいい。
void CChildView::OnPaint() { CPaintDC dc(this); const char *szText = "サンプルテキスト"; wchar_t* wideText = new wchar_t[128];
MultiByteToWideChar( CP_ACP, // コードページ。ANSI 0, // フラグ。0でいい szText, // 変換したい文字列 -1, // 文字列の長さ。-1でnull終端で自動計算 wideText,// 変換後の文字列を格納するバッファ 128 // 変換後の文字列を格納するバッファのサイズ ); ::TextOutW(dc.GetSafeHdc(), 0, 0, wideText,wcslen(wideText)); delete [] wideText; // 解放を忘れずに }
char* →wchar_t* の CA2W の他に、
TCHAR → wchar_t*の CT2W 、
wchar_t* → charの CW2A 、
などがある。
フォリッジ機能を使って草を生やすには、フォリッジモードに入る。

フォリッジモード内で、「ここにフォリッジをドロップ」という位置にアクタをドロップする。例としてConeをドロップしてみると、「フォリッジタイプアセットの位置を選択」というダイアログが出てくる。これは生やす草などのオブジェクトを管理するディレクトリを指定するもので、わかりやすければどこでもいい。

ペイントなどの要領でドラッグすればアクタを配置できる。

なお、作業を終わるときは選択モードに移行する。
アドオンは予め入っているが、アカウントをリンクする必要がある。
メールアドレスやパスワードを入力しなければならないが、今自分がEpic Gamesのアカウントを要求されているのか、Quixelのアカウントを要求されているのかを確認しながら進めないと間違えて時間がかかる。



私の場合、以前作ったQuixelのアカウントがあったせいか、Quixel側の設定をしろと言われたので、GO THERE! をクリックしてQuixelのページを開く。
Quixelのサイトへ行ったら、プラン選択のところでUNREAL ENGINEを選び、連携の設定を行う。
これで、再度Unreal Engine側に戻ってSign Inすると使用できるようになる。
Bridgeのウィンドウ内の一覧からドラッグ&ドロップすることで簡単にシーンに読み込める。
synでの構文解析がどのようになっているかを調べるために、以下のようなコードを書く。
parse_macro_inputで作成された構文木myinputをトレースするために、syn::visit::Visitを使用。ノードを再帰的に処理する。処理内容は「ノードを文字列化してコンパイル時に表示する」というもの。
extern crate proc_macro; use std::{string, str::FromStr}; use proc_macro::TokenStream; use quote::ToTokens; // to_token_stream() を使うために必要 use syn::{ parse_macro_input, Expr, // Cargo.toml 内 syn = {"2.0", features = ["full","visit"] } visit // "visit" が必要 }; use syn::visit::Visit; // vis.visit_expr() を使うために必要
// 構造体を定義 struct MyExprVisitor { indent: usize, // printでインデントしたいのでこのメンバを用意 }
// MyExprVisitor にvisit_expr()を実装 // C++でいうと、「class MyExprVisitor : public syn::visit::Visit」に近い impl<'a> syn::visit::Visit<'a> for MyExprVisitor { fn visit_expr(&mut self, node: &'a Expr) { // インデント for _ in 0..self.indent { print!(" "); } // ノードを文字列化 let astext = node.to_token_stream().to_string(); println!("{}", astext); self.indent += 1; // 再帰的に処理 syn::visit::visit_expr(self, node); self.indent -= 1; } }
// 式のトークンストリームを処理するマクロ #[proc_macro] pub fn tokenstream_analyze_for_formula(input: TokenStream) -> TokenStream { // トークンストリームを解析 let inputcopy = input.clone(); let myinput:Expr = parse_macro_input!(inputcopy as Expr); println!("** 式の構文木ができているか確認 *********************"); let mut myvis = MyExprVisitor{indent:0}; myvis.visit_expr(&myinput); input }
extern crate mymacro; fn main() { let value:i32; let seven:i32 = 7;
// 式に対してマクロを適用 mymacro::tokenstream_analyze_for_formula! { value = (seven + 5) * 2 }
print!("結果 {}",value);
}
Compiling mymain v0.1.0 (D:\mydev\Rust\mymacro-syn\mymain)
** 式の構文木ができているか確認 *********************
value = (seven + 5) * 2
value
(seven + 5) * 2
(seven + 5)
seven + 5
seven
5
2
Finished dev [unoptimized + debuginfo] target(s) in 0.49s
ただ文字列に変換しただけでは扱い方がよくわからないので、nodeをそれが何で(ここではExpr::Binary)、どのようなものが入っているのか(op,left,right)を取り出す。
// MyExprVisitor にvisit_expr()を実装 // C++でいうと、「class MyExprVisitor : public syn::visit::Visit」に近い impl<'a> syn::visit::Visit<'a> for MyExprVisitor { fn visit_expr(&mut self, node: &'a Expr) { let mut indentstr:String = "".to_string(); // インデント for _ in 0..self.indent { indentstr.push_str(" "); }; println!("{}***************",indentstr); match node { // Expr::Binaryはシンプルな加減乗除のような式 // matchでnodeがExpr::Binaryであるかどうかを判定し、もしそうならその内容をmyitemへ代入 Expr::Binary(myitem) => { // if letでmyitemがExprBinaryであるかどうかを判定し、もしそうならop,left,rightを取り出す if let ExprBinary{op:myop, left:myleft, right:myright, ..} = myitem{ println!("{} @ {}",indentstr, myop.to_token_stream()); println!("{} @ {}",indentstr, myleft.to_token_stream()); println!("{} @ {}",indentstr, myright.to_token_stream()); } }, _=>{} } println!("{}***************",indentstr); self.indent += 1; // 再帰的に処理 syn::visit::visit_expr(self, node); self.indent -= 1; } }
Compiling mymain v0.1.0 (D:\mydev\Rust\mymacro-syn\mymain)
** 式の構文木ができているか確認 *********************
***************
***************
***************
***************
***************
@ *
@ (seven + 5)
@ 2
***************
***************
***************
***************
@ +
@ seven
@ 5
***************
***************
***************
***************
***************
***************
***************
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
手続き型マクロは、与えられたトークンストリームを編集することでコードをコンパイル時に書き換えるのだが、この作業を楽にするためにsynクレートというものがあり、synを使うと構文木の操作がかなり楽にできる(らしい)。
syn::parse_macro_input!で構文解析を行い、構文木(=AST=Abstract Syntax Tree)を作成する。
この時、トークンストリームを、ItemFnやExprなどに変換して渡す必要がある。マクロに渡された内容が関数であればItemFn、式であればExprに変換する。
extern crate proc_macro; use proc_macro::TokenStream; use syn::{ parse_macro_input, Expr, // Cargo.toml 内 syn = {"2.0", features = ["full"] } ItemFn, // "full" が必要 };
// 関数を処理する場合 as ItemFn を使用 #[proc_macro] pub fn tokenstream_analyze_for_function(input: TokenStream) -> TokenStream { let inputcopy = input.clone(); let myinput:ItemFn = parse_macro_input!(inputcopy as ItemFn);// トークンストリームを解析 return input; }
// 式を処理する場合 as Expr を使用 #[proc_macro] pub fn tokenstream_analyze_for_formula(input: TokenStream) -> TokenStream { let inputcopy = input.clone(); let myinput:Expr = parse_macro_input!(inputcopy as Expr);// トークンストリームを解析 return input; }
synを使用するためにはCargo.tomlに以下の記述が必要。ItemFnを使用するためには、features=["full"]を加える必要がある。
このマクロは、以下のようにして使用する。(注意 結果は何も変わらない)
extern crate mymacro;
// 関数に対してマクロを適用 mymacro::tokenstream_analyze_for_function!{ fn myfunction(i:i32)->i32{ i + 1 } }
fn main() { let value:i32; let seven:i32 = 7;
// 式に対してマクロを適用 mymacro::tokenstream_analyze_for_formula! { value = (seven + 5) * 2 }
print!("結果 {}",value);
}
上記のマクロは何も起こらないので、syn::ItemFnやsyn::Expr型の変数myinputにちゃんと値が入っているのか確認したい。そのためにquoteクレートを使用して構文木を表示してみる。
extern crate proc_macro; use std::{string, str::FromStr}; use proc_macro::TokenStream; use syn::{ parse_macro_input, Expr, ItemFn // "full" が必要 }; use quote::ToTokens;
// 関数 #[proc_macro] pub fn tokenstream_analyze_for_function(input: TokenStream) -> TokenStream { // トークンストリームを解析 let inputcopy = input.clone(); let myinput:ItemFn = parse_macro_input!(inputcopy as ItemFn); println!("** 関数の構文木ができているか確認 *********************"); // quoteを使って文字列化 let astext = myinput.to_token_stream().to_string(); println!("{}", astext); input }
// 式 #[proc_macro] pub fn tokenstream_analyze_for_formula(input: TokenStream) -> TokenStream { // トークンストリームを解析 let inputcopy = input.clone(); let myinput:Expr = parse_macro_input!(inputcopy as Expr); println!("** 式の構文木ができているか確認 *********************"); // quoteを使って文字列化 let astext = myinput.to_token_stream().to_string(); println!("{}", astext); input }
これをビルドすると、ビルド中に以下が表示される。
RustのsynはRustで書かれたソースコードを解析するためのもので、手続き型マクロを実装するときに重宝するが、普通のプログラムを書く時にも使用できる。
文字列からコードを解析する場合、syn::parse_strを用いる。
fn main() { // 文字列でRustのコードを書く let my_rust_code = " #[derive(Debug, Clone)] #[inline] pub fn myfunc(&self,x:i32, y:i32) -> f32{ println!(\"Hello, world!\"); } " ; // ソースコードのパース let my_parse = syn::parse_str::<syn::ItemFn>(my_rust_code).unwrap(); let mut myfv:MyFnVisitor = MyFnVisitor{indent:0}; myfv.visit_item_fn(&my_parse); }
///////////////////////////// /// 以下、解析のための構造体並びに関数 struct MyFnVisitor{ indent:usize, } use quote::ToTokens; use syn::visit::Visit; impl<'a> syn::visit::Visit<'a> for MyFnVisitor{ // 関数をパースするには、visit_item_fnを実装する fn visit_item_fn(&mut self, node: &'a syn::ItemFn) { let mut indentstr:String = "".to_string(); for _ in 0..self.indent{ indentstr.push_str(" "); } println!("{}****************",indentstr); // 関数がパブリックかどうかをチェック if let syn::Visibility::Public(_pub) = node.vis { println!("{}pub? {}",indentstr,_pub.to_token_stream().to_string()); } // アトリビュートを取得 for attr in &node.attrs{ let id = attr.path().get_ident().unwrap(); println!("{}attribute: {}",indentstr,id); } // 関数名を取得 let function_name = &node.sig.ident; println!("{}func_name: {}",indentstr,function_name); // 引数を取得 for arg in node.sig.inputs.iter(){ match arg{ syn::FnArg::Typed(pat_type) => { if let syn::Pat::Ident(patident) = &*pat_type.pat{ let arg_name = &patident.ident; let arg_type = &pat_type.ty.to_token_stream().to_string(); println!("{}arg: {} {}",indentstr, &arg_name, &arg_type ); } } _ => {} } } } }
前回、トークンストリームを見てみた。もう少し詳しく見てみる。
Rustの手続き的マクロを考える(1)トークンストリームを確認してみる
以下のようなマクロを使用する側。この実行結果は「24」になる。
extern crate mymacro; fn main() { let value:i32; let seven:i32 = 7;
mymacro::print_tokens! { value = (seven + 5) * 2; }
print!("結果 {}",value); }
extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn print_tokens(input: TokenStream) -> TokenStream { // TokenStreamをStringに変換 let input_str = input.to_string(); println!("受け取ったトークン: {}", input_str);// ビルド時画面に表示 // トークンの種類を特定 for token in input.clone().into_iter() { if let proc_macro::TokenTree::Literal( theItem ) = token{ println!("これはリテラル {}", theItem); } else if let proc_macro::TokenTree::Punct( theItem ) = token{ println!("これは記号 {}",theItem); } else if let proc_macro::TokenTree::Ident( theItem ) = token{ println!("これは識別子 {}",theItem); } else if let proc_macro::TokenTree::Group( theItem ) = token{ println!("これはグループ {}", theItem); } else{ println!("不明 {:?}", token); } }; // 入力をそのまま返す input }
トークンの扱いがわかったので、トークンを書き換えてみる。以下のマクロはprint_tokensに渡された内容が「2」であった場合は値を二倍(すなわち4)にする。
マクロに渡されたTokenStreamのinputを直接書き換えることはできないので、TokenTreeのVecを用意し、そこにトークンをpushしていく。その過程で編集したい内容を見つけたら新たなトークンを作成して既存のトークンと置き換える。
最後に、TokenTreeのVecをTokenStreamに変換して返す。
このマクロを使用して最初のプログラムを実行した結果は「48」になる。
extern crate proc_macro; use std::{string, str::FromStr}; use proc_macro::TokenStream; #[proc_macro] pub fn print_tokens(input: TokenStream) -> TokenStream { // TokenStreamをStringに変換 let input_str = input.to_string(); println!("受け取ったトークン: {}", input_str);// ビルド時画面に表示 // inputを書き換えることができないので、出力用のTokenStreamを作成 let mut output = Vec::new(); // トークン毎に処理 for token in input.into_iter() { // 所有権が移動しないように ref theItemとする if let proc_macro::TokenTree::Literal( ref theItem ) = token{ println!("リテラルを発見 {}", theItem); let new_token; let ival = theItem.to_string().parse::<i32>().unwrap();// リテラルの値を取得 if ival == 2{ println!("リテラルの値が2の時だけtoken書き換え"); // 新しいトークンを作成 new_token = proc_macro::TokenTree::from( proc_macro::Literal::i32_unsuffixed( ival * 2 /*二倍にする*/) ); // 新しいトークンを出力用の配列に追加 output.push(new_token); } else{ // 2以外の時はそのまま出力用の配列に追加 output.push(token.clone()); } } else{ // リテラル以外はそのまま出力用の配列に追加 output.push(token.clone()); } }; // 新しく作成したトークンの配列をTokenStreamに変換して返却 return TokenStream::from_iter(output); }
Rustには、「宣言的マクロ」(macro_rules!で定義する)と「手続き的マクロ」があり、「手続き的マクロ」にはさらにderiveマクロ、関数風マクロ、属性風マクロという分類がある。
手続き型マクロは、マクロの呼び出し元から入ってきたコード片を「トークンストリーム」として受け取る。PGはこのトークンストリームを処理することでマクロに与えられたコードをコンパイル時に書き換えることができる。
マクロを作る前に、トークンストリームを表示してみる。
手続き型マクロは、専用のクレートを作らなければいけない。
今回は、main関数があるほうをmymain、マクロを定義する法をmymacroとしてクレートを作成する。
Cargo.tomlに以下を記述する。
extern crate proc_macro; use proc_macro::TokenStream; #[proc_macro] pub fn print_tokens(input: TokenStream) -> TokenStream { // TokenStreamをStringに変換 let input_str = input.to_string(); // 画面に表示 println!("Received TokenStream: {}", input_str); // inputを一つ一つのTokenに分割 for tk in input.clone().into_iter() { // 画面に表示 println!("*** {}", tk); }; // 入力をそのまま返す(または他のTokenStreamを返す) input }
以下のdependenciesを追加。
[dependencies] mymacro = { path = "../mymacro" }
extern crate mymacro; fn main() { let mut value=5; mymacro::print_tokens! { value = value + 5; } print!("hello world {} ",value); }
mymainをbuildする。
すると、ビルド時に以下のようにトークンストリームが表示される(実行時ではない)。
ChatGPT4で簡単なコードなら自動生成できるようになったので、結果的に日本語入力頻度が上がっている。日本語入力をするなら親指シフト、親指シフトを使うなら紅皿が必要になる。
OSDNからダウンロードできるが、正直OSDNは遅くて待っていられない。
公式のGithubからもダウンロードできるので個人的にはこちらを推奨したい。
https://github.com/k-ayaki/benizara/releases/tag/ver.0.1.6.03
注意点として、benizara_01603.zipがmsiのインストーラ版、exeを直接導入したい場合、Source code (zip) を導入。これは、紅皿が「AutoHotKeyのスクリプトを実行ファイル化したもの」なので、紅皿本体のプログラムというものが(ほぼ)ないからではないかと思う。
ローカルディレクトリにある.bnzファイルが各キーを押した場合の配列を定義している。
例えば、.\NICOLA配列.bnzのデフォルトの設定ではBackspaceを右小指で入力できないので、このファイルを(念のため)コピーして、:キーのローマ字シフトなしの位置に「後」を書き込む。するとただ:キーを入力した場合はBackspaceを押したことになる。:を入力したい場合は左右どちらかの親指シフトで行える。
まず、cargoコマンドでそれぞれのプロジェクトを作成する。
これによって、同じ階層に二つのプロジェクトができる。
root ├─mylib │ │ .gitignore │ │ Cargo.lock │ │ Cargo.toml │ │ │ ├─.vscode │ │ launch.json │ │ │ ├─src │ │ lib.rs │ │ │ └─target │ │ .rustc_info.json │ │ CACHEDIR.TAG │ │ │ └─debug │ └─mymain │ .gitignore │ Cargo.lock │ Cargo.toml │ ├─src │ main.rs │ └─target │ .rustc_info.json │ CACHEDIR.TAG │ └─debugu
// mylib/src/lib.rs pub fn mylib_hello() { println!("Mylib: Hello, world!"); }
extern crate mylib; fn main() { mylib::mylib_hello(); }
[dependencies]
mylib = { path = "../mylib" }
mymainだけをcargo buildすれば、自動でmylibもビルドされてリンクされる。