ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
         12
    3456789
    10111213141516
    17181920212223
    2425262728  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
       1234
    567891011
    12131415161718
    19202122232425
    26272829   
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28      
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
    1234567
    891011121314
    15161718192021
    22232425262728
           
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • MFCでCA2Wを使ってchar*文字列をwchar_t*文字列に変換する

    MFCでchar*からwchar_t*へ変換する話。この処理はMultiByteToWideChar関数で行えるが、MFCであれば、CA2Wという便利クラスでラップされているのでこれを使える。

    諸事情によりマルチバイト文字セットでプロジェクトを作らなければいけないが、使用するライブラリがconst wchar_t*を要求するという状況に遭遇して調べた。

    CA2W

    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と同じ長さの寿命を持てる。

    MultiByteToWideCharを直接使う場合

    下記のようになる。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

    などがある。

    Unreal Engine 5 フォリッジ機能でメッシュに草を生やしてみる

    フォリッジ機能を使って草を生やすには、フォリッジモードに入る。

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

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

    なお、作業を終わるときは選択モードに移行する。

    Quixel Bridgeから草をダウンロードして配置

    Unreal Engine 5とQuixelをリンクしてモデルをインポートしてみる

    Quixel Bridgeの導入

    アドオンは予め入っているが、アカウントをリンクする必要がある。

    メールアドレスやパスワードを入力しなければならないが、今自分がEpic Gamesのアカウントを要求されているのか、Quixelのアカウントを要求されているのかを確認しながら進めないと間違えて時間がかかる。

    私の場合、以前作ったQuixelのアカウントがあったせいか、Quixel側の設定をしろと言われたので、GO THERE! をクリックしてQuixelのページを開く。

    Quixelのサイトへ行ったら、プラン選択のところでUNREAL ENGINEを選び、連携の設定を行う。

    これで、再度Unreal Engine側に戻ってSign Inすると使用できるようになる。

    読み込み

    Bridgeのウィンドウ内の一覧からドラッグ&ドロップすることで簡単にシーンに読み込める。

    Rustの手続き的マクロを考える(3)synで構文解析(2)

    synでの構文解析がどのようになっているかを調べるために、以下のようなコードを書く。

    parse_macro_inputで作成された構文木myinputをトレースするために、syn::visit::Visitを使用。ノードを再帰的に処理する。処理内容は「ノードを文字列化してコンパイル時に表示する」というもの。

    lib.rs

    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
    }
    

    main.rs

    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でmyitemExprBinaryであるかどうかを判定し、もしそうなら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
    

    Rustの手続き的マクロを考える(3)synで構文解析(1)

    手続き型マクロは、与えられたトークンストリームを編集することでコードをコンパイル時に書き換えるのだが、この作業を楽にするためにsynクレートというものがあり、synを使うと構文木の操作がかなり楽にできる(らしい)。

    syn::parse_macro_input!で構文解析を行い、構文木(=AST=Abstract Syntax Tree)を作成する。

    この時、トークンストリームを、ItemFnやExprなどに変換して渡す必要がある。マクロに渡された内容が関数であればItemFn式であればExprに変換する。

    synの基本

    Lib.rs

    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"]を加える必要がある。

    [dependencies]
    syn = {version = "2.0", features = ["full"] }

    main.rs

    このマクロは、以下のようにして使用する。(注意 結果は何も変わらない)

    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);
    }
    

    quoteで文字列化して表示

    上記のマクロは何も起こらないので、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 }

    これをビルドすると、ビルド中に以下が表示される。

    Compiling mymain v0.1.0 (D:\mydev\mymacro-syn\mymain)
    ** 関数の構文木ができているか確認 *********************
    fn myfunction(i : i32) -> i32 { i + 1 }
    ** 式の構文木ができているか確認 *********************
    value = (seven + 5) * 2

    Rustの手続き型マクロを作成するために使用するsynを普通の用途で使ってみる

    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
                            );
                        }
                    }
                    _ => {}
                }
            }
    
        }
    
    }
    

    Cargo.toml

    [dependencies]
    syn = {version = "2.0", features = ["full","visit"] }
    quote = "1.0.33"

    実行結果

    ****************
    pub? pub
    attribute: derive
    attribute: inline
    func_name: myfunc
    arg: x i32
    arg: y i32

    Rustの手続き的マクロを考える(2)トークンの種類を特定し、書き換えてみる

    前回、トークンストリームを見てみた。もう少し詳しく見てみる。

    Rustの手続き的マクロを考える(1)トークンストリームを確認してみる

    トークンの種類を特定してみる

    main関数側

    以下のようなマクロを使用する側。この実行結果は「24」になる。

    extern crate mymacro;
    
    fn main() {
    
        let value:i32;
        let seven:i32 = 7;
        
    
        mymacro::print_tokens! {
    
            value = (seven + 5) * 2;
    
        }
    

    print!("結果 {}",value); }

    print_tokensマクロの実装

    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
    }
    
    Compiling mymain v0.1.0 (D:\dev\Rust\mymacro\mymain)
    受け取ったトークン: value = (seven + 5) * 2 ;
    これは識別子  value
    これは記号  =
    これはグループ (seven + 5)
    これは記号  *
    これはリテラル 2
    これは記号  ;
    Finished dev [unoptimized + debuginfo] target(s) in 0.85s

    トークンを書き換える

    トークンの扱いがわかったので、トークンを書き換えてみる。以下のマクロは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の手続き的マクロを考える(1)トークンストリームを確認してみる

    Rustには、「宣言的マクロ」(macro_rules!で定義する)と「手続き的マクロ」があり、「手続き的マクロ」にはさらにderiveマクロ、関数風マクロ、属性風マクロという分類がある。

    手続き型マクロの原理

    手続き型マクロは、マクロの呼び出し元から入ってきたコード片を「トークンストリーム」として受け取る。PGはこのトークンストリームを処理することでマクロに与えられたコードをコンパイル時に書き換えることができる。

    マクロを作る前に、トークンストリームを表示してみる。

    手続き型マクロの作り方

    手続き型マクロは、専用のクレートを作らなければいけない。

    今回は、main関数があるほうをmymain、マクロを定義する法をmymacroとしてクレートを作成する。

    cargo new --lib mymacro
    cargo new mymain

     

    mymacroクレート

    Cargo.toml

    Cargo.tomlに以下を記述する。

    [lib]
    proc-macro = true

    lib.rs

    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
    }
    

    mymainクレート

    Cargo.toml

    以下のdependenciesを追加。

    [dependencies]
    mymacro = { path = "../mymacro" }
    

    main.rs

    extern crate mymacro;
    
    
    fn main() {
    
        let mut value=5;
        
        mymacro::print_tokens! {
    
            value = value + 5;
    
        }
    
        print!("hello world {} ",value);
    }
    

    build

    mymainをbuildする。

    \mymain>cargo build

    すると、ビルド時に以下のようにトークンストリームが表示される(実行時ではない)。

    Compiling mymain v0.1.0 (D:\test\mymain)
    Received TokenStream: value = value + 5 ;
    *** value
    *** =
    *** value
    *** +
    *** 5
    *** ;
    Finished dev [unoptimized + debuginfo] target(s) in 0.32s

    紅皿を入れてキー配列を変える

    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を押したことになる。:を入力したい場合は左右どちらかの親指シフトで行える。

     

    Rustのライブラリ用mylibクレートを作って実行用のmymainクレートから呼び出す

    まず、cargoコマンドでそれぞれのプロジェクトを作成する。

    cargo new --lib mylib
    cargo new mymain

     

    これによって、同じ階層に二つのプロジェクトができる。

    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の編集

    lib.rs

    // mylib/src/lib.rs
    pub fn mylib_hello() {
        println!("Mylib: Hello, world!");
    }
    

    mymain

    main.rs

    extern crate mylib;
    
    fn main() {
        mylib::mylib_hello();
    }
    

    Cargo.toml

    [dependencies]
        
    mylib = { path = "../mylib" }

    ビルド

    mymainだけをcargo buildすれば、自動でmylibもビルドされてリンクされる。

    cd mymain
    cargo build
    cargo run