ぬの部屋(仮)
nu-no-he-ya
  •    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
           
  • 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

    VTK9.3をwindowsでStatic Link LibraryとしてCMakeする

    WindowsでVTK9.3をビルドする。

    BUILD_SHARED_LIBS を OFF

    Static Link Libraryとしてビルドするには、BUILD_SHARED_LIBS をOFFにする。

    ただし、Windowsでこのチェックを外すと、以下のエラーが起こる可能性がある。

    3>vtkCommonCore-9.3.lib(vtkSMPToolsAPI.obj) : error LNK2019: 未解決の外部シンボル "public: bool __cdecl vtk::detail::smp::vtkSMPToolsImpl<1>::IsParallelScope(void)" (?IsParallelScope@?$vtkSMPToolsImpl@$00@smp@detail@vtk@@QEAA_NXZ) が関数 "public: bool __cdecl vtk::detail::smp::vtkSMPToolsAPI::IsParallelScope(void)" (?IsParallelScope@vtkSMPToolsAPI@smp@detail@vtk@@QEAA_NXZ) で参照されました
    3>D:\myDevelop\mydev\VTK\solution\bin\Release\vtkProbeOpenGLVersion-9.3.exe : fatal error LNK1120: 1 件の未解決の外部参照
    3>プロジェクト "vtkProbeOpenGLVersion.vcxproj" のビルドが終了しました -- 失敗。

    そこで次の設定が必要

    VTK_SMP_ENABLE_STDTHREAD を OFF

    上記vtkSMPToolsAPIのエラーを避けるため、VTK_SMP_ENABLE_STDTHREADをOFFに設定。

    参考

    VTK failed to build due to error LNK2019 and error LNK1120 on Windows with msvc

    https://discourse.vtk.org/t/vtk-failed-to-build-due-to-error-lnk2019-and-error-lnk1120-on-windows-with-msvc/11671/16

    感想

    情報自体は大したことはない。ただ確認するためのVTKのビルドが長すぎる。

    MFCのCMainFrame上でCSplitterWndを使い画面分割

    CSplitterWndでCMainFrameを画面分割する。構造としては、MFCが作成したシングルドキュメントのウィンドウは、CMainFrameを親とし、その子にツールバー、CChildView、ステータスバーが乗っている。このCChildViewをCSplitterWndで置き換えることで実現する。

    1.MFCで単一ドキュメントのプロジェクト作成

    ドキュメント/ビューアーキテクチャは無効にしておく。

    2.CChildView m_wndView を削除

    CChildViewのオブジェクトをCSplitterWndのオブジェクトで置き換えるため、コード上のm_wndViewという変数がかかわるコードをすべて無効化する。m_wndViewの定義をコメントアウトする、あるいは検索すれば簡単に見つかる。

    // MainFrm.h : CMainFrame クラスのインターフェイス
    //
    
    #pragma once
    #include "ChildView.h"
    
    class CMainFrame : public CFrameWnd
    {
    	
    public:
    	CMainFrame() noexcept;
    protected: 
    	DECLARE_DYNAMIC(CMainFrame)
    
    
    /* 略 */
    
    protected:  // コントロール バー用メンバー
    	CToolBar          m_wndToolBar;
    	CStatusBar        m_wndStatusBar;
    	// 使用しない CChildView    m_wndView; 
    
    /* 略 */
    };
    

    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
    		return -1;
    
    	//// フレームのクライアント領域全体を占めるビューを作成します。
    	//if (!m_wndView.Create(nullptr, nullptr, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, nullptr))
    	//{
    	//	TRACE0("ビュー ウィンドウを作成できませんでした。\n");
    	//	return -1;
    	//}
    
    	/* 略 */
    
    	return 0;
    }
    

    void CMainFrame::OnSetFocus(CWnd* /*pOldWnd*/)
    {
    	// ビュー ウィンドウにフォーカスを与えます。
    	//m_wndView.SetFocus();
    }
    

    3.CViewを継承したCLeftView,CRightViewを作成

    4.CSplitterWnd m_wndSplitter;を定義

    protected:  // コントロール バー用メンバー
    	CToolBar          m_wndToolBar;
    	CStatusBar        m_wndStatusBar;
    	// 使用しない CChildView    m_wndView; 
    	CSplitterWnd m_wndSplitter;// スプリッターの定義
    

    5.OnCreateClientをオーバーライドしCSplitterWndの設定を追加

    CMainFrameでOnCreateClientをオーバーライドする。

    OnCreateClientにはスプリッタの初期設定を記述する。

    BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
    {
    	// TODO: ここに特定なコードを追加するか、もしくは基底クラスを呼び出してください。
    	// スプリッタウィンドウの作成
    	m_wndSplitter.CreateStatic(this, 1, 2);
    
    	// ペインの作成(例:左側にCLeftView、右側にCRightViewを配置)
    	m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CLeftView), CSize(200, 100), pContext);
    	m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CRightView), CSize(100, 100), pContext);
    
    	return CFrameWnd::OnCreateClient(lpcs, pContext);
    }
    

    6.実行例

    CLeftView,CRightViewに着色すると結果がわかりやすい

    void CLeftView::OnDraw(CDC* pDC)
    {
    	CDocument* pDoc = GetDocument();
    	// TODO: 描画コードをここに追加してください。
    
    	CRect rect;
    	this->GetClientRect(&rect);
    	pDC->FillSolidRect(&rect,RGB(255,0,0));
    }
    

    CFileDialogのファイル形式のフィルタの指定について

    CFileDialogのフィルタ指定の書式は、コンストラクタ指定の場合は "表示用文字列|*.拡張子|"、m_ofn.lpstrFilter指定の場合、"表示用文字列\0*.拡張子\0"となる。

    ただし注意があり、m_ofn.lpstrFilterで指定する場合、CStringを使ってはいけない。たぶん、\0が見つかるとそこで文字が切られてしまうため、最初の一つしか選択肢に出てこない。

    拡張子の指定方法

    以下指定例。プロジェクトはマルチバイト文字セットに設定してある。

    m_ofn.lpstrFilter指定

      CFileDialog fileDialog(FALSE, "*.bmp", "output.bmp");
      const char* filter =
        "BMPファイル\0*.bmp\0"
        "JPGファイル\0*.jpg\0"
        "PNGファイル\0*.png\0"
        "全てのファイル\0*.*\0\0";
      fileDialog.m_ofn.lpstrFilter = filter;
      fileDialog.DoModal();
    

    コンストラクタ指定

      CFileDialog fileDialog(
        FALSE, "*.bmp", "output.bmp", 
        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
        "BMPファイル|*.bmp|"
        "JPGファイル|*.jpg|"
        "PNGファイル|*.png|"
        "全てのファイル|*.*||"
        );
      fileDialog.DoModal();
    

    CStringを使う場合の注意

    CStringは\0を含められない(?)ので、m_ofn.lpstrFilterに与える文字列をCStringにしてはいけない。

    m_ofn.lpstrFilter指定

      CFileDialog fileDialog(FALSE, "*.bmp", "output.bmp");
    
      // これは失敗する。
      CString filter =
        "BMPファイル\0*.bmp\0"
        "JPGファイル\0*.jpg\0"
        "PNGファイル\0*.png\0"
        "全てのファイル\0*.*\0\0";
    
      fileDialog.m_ofn.lpstrFilter = (LPCTSTR)filter;
      fileDialog.DoModal();
    

    コンストラクタ指定

      CFileDialog fileDialog(
        FALSE, "*.bmp", "output.bmp", 
        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
        // これは成功する
        (LPCTSTR)CString(
          "BMPファイル|*.bmp|"
          "JPGファイル|*.jpg|"
          "PNGファイル|*.png|"
          "全てのファイル|*.*||"
        )
        );
      fileDialog.DoModal();
    

    指定した拡張子を判別

    指定した拡張子は、上から1スタートで番号が振られている。

      fileDialog(FALSE, "bmp", "output.bmp");
    
      const char* filter =
        "BMPファイル\0*.bmp\0"     // index == 1
        "JPGファイル\0*.jpg\0"     // index == 2
        "PNGファイル\0*.png\0"     // index == 3
        "全てのファイル\0*.*\0\0";
    
      fileDialog.m_ofn.lpstrFilter = (LPCTSTR)filter;
    
      if (fileDialog.DoModal() == IDOK) {
    
    
        // 選択されたファイルのインデックスを取得
        int filterIndex = fileDialog.m_ofn.nFilterIndex;
    
        switch (filterIndex) {
        case 1:
          MessageBoxA("BMP");
          break;
        case 2:
          MessageBoxA("JPG");
          break;
        case 3:
          MessageBoxA("PNG");
          break;
        case 4:
          MessageBoxA("*.*");
          break;
    
        }
        
        // なお入力されたファイル名
        CString filePath = fileDialog.GetPathName();
        MessageBoxA(filePath);
      }
    

    wxFormBuilderを使う

    wxWidgetsのGUIを作るためのデザイナー。いろいろな言語に対応しているが私はC++用コードを出力するために使用してみる。

    wxFormBuilderでデザインした後はGenerate CodeでC++のソースコードに変換できるので、.hと.cppを読み込めば簡単に組み込める。

    ダウンロード

    https://github.com/wxFormBuilder/wxFormBuilder/releases

    Githubにビルド済みライブラリページへのリンクが張ってあるのでダウンロードすれば起動できる。

    基本的な使い方

    1.FormsからFrameなどを追加

    2.LayoutからwxBoxSizerなどを追加

    3.CommonからwxButtonなどを追加

    4.File → Save から.fbp形式で保存

    5.File → Generate Code

    Generate Codeをすると、fbp と同じ場所に .h/.cpp が作成される。

    C++プロジェクトへ導入

    noname.h / noname.cpp をプロジェクトに追加し、noname.h をincludeする。この中にMyFrame1等の名前でフレーム類が定義してあるので、OnInitの中でそのインスタンスを作成する。

    #ifndef WX_PRECOMP
    #include <wx/wx.h>
    #endif
    
    #include <wx/gdicmn.h> // wxPointに必要
    #include <wx/frame.h>  // wxFrameに必要
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // wxFormBuilder で出力したファイル。内部で MyFrame1 が定義されている
    #include "noname.h" ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxAppはここから開始。自分で書く class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame1* frame = new MyFrame1(0,-1,"Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // エントリポイントも自分で定義。WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);