ぬの部屋(仮)
nu-no-he-ya
  •      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
           
  • C++/CLIでTask

    スレッドは生成・破棄に時間がかかる(下手すりゃ秒単位)。そこでスレッドプール等の運用方法が考案され、効率化が図られてきた。

    しかしそれらを実装するのは結構な手間で、プログラマが一々手を煩わせるべきではない。

    そのような要望から生まれたのがTaskで、効率的なスレッドの運用を内部で勝手にやってくれる。

     

    という事らしい。

    参考文献

    https://stackoverflow.com/questions/31531697/c-cli-using-iprogress-in-task
    https://teratail.com/questions/82895

     

     

    using namespace System;
    //パラメータ受け渡し用のクラス
    public ref class CParam {
    public:
      int i;
    };
    //一つのタスクとなるクラス
    public ref class MyClass {
      CParam^ param;
      System::Threading::Tasks::Task^ myTask;
    public:
      MyClass(int k) {
        param = gcnew CParam;
        param->i = k;
      }
      void TaskMethod(Object^ obj);//スレッドで走る関数
      void Start();
      void Wait();
    };
    //スレッドを開始する関数
    void MyClass::Start() {
      myTask = System::Threading::Tasks::Task::Factory->StartNew(
        gcnew System::Action<Object^>(this, &MyClass::TaskMethod), (Object^)param);
    }
    
    //終了まで待機
    void MyClass::Wait() {
      myTask->Wait();
    }
    //処理本体
    void MyClass::TaskMethod(Object^ obj) {
      CParam^ cp = (CParam^)obj;
      for (int j = 0; j < 100; j++) {
        Console::WriteLine(System::String::Format("{0}", cp->i + j));
      }
      
    }
    /////////////////////////////////////////////////////
    int main(array<System::String ^> ^args)
    {
    
      MyClass^ mc1 = gcnew MyClass(100);
      MyClass^ mc2 = gcnew MyClass(200);
      MyClass^ mc3 = gcnew MyClass(300);
    
      mc1->Start();
      mc2->Start();
      mc3->Start();
    
      mc1->Wait();
      mc2->Wait();
      mc3->Wait();
    
    
    
      Console::Read();
      return 0;
    }
    

     

     

     

     

    WordPressで、あるカテゴリを親に含む現在の記事に対し、その親以下のカテゴリ一覧を表示する

    ちょっと自分でも何言ってるのかわからない。

    以下のような状態で、(article)の記事で、

    categoryB
         CAT_B
             cat_b1
             cat_b2
         CAT_BB
             cat_bb1
             cat_bb2

    という一覧を出したい。rootまで遡らず、categoryB以下のカテゴリ一覧だけを取得したいわけである。

     

    root
       categoryA
          CAT_A
             cat_a1
             cat_a2
          CAT_AA
            cat_aa1
            cat_aa2
       categoryB
          CAT_B
             cat_b1
             cat_b2
          CAT_BB
             cat_bb1
             cat_bb2    (article)
       categoryC
          CAT_C
             cat_c1
             cat_c2
           CAT_CC
             cat_cc1
             cat_cc2

     

    原理は

    ①まず注目カテゴリとして、現在のページのカテゴリを取得       get_queried_object()
    ②そのカテゴリの親カテゴリを取得        $catobj->parent
    ③そのカテゴリが上記でいうところのcategoryBなら、wp_list_categoriesで一覧表示
    ④そうでないなら、注目カテゴリを②に更新して、②に戻る       get_category

     

      <?php 
        $catobj = get_queried_object(); /*現在のカテゴリIDを取得*/
        /*星の部屋カテゴリと、その下のカテゴリまでを取得*/
        while(true){
          $parentid = $catobj->parent; //注目中のカテゴリの親カテゴリを取得
          if($parentid == 0 || $parentid == 121) //それが「ほしいカテゴリ階層のトップ」ならループを抜ける。(0判定は無限ループ対策)
            break;
          $catobj = get_category($parentid); //親カテゴリ取得。ない場合は0 //parentidに「ほしいカテゴリ階層のトップ」が入っていないなら、それを次の注目カテゴリに設定
        }
        if( $parentid != 0 ){ //0の時は欲しいカテゴリ階層ではないということ(何かが間違っている)ので何もしない
            echo '<ul class="childofcategory">';
      	    wp_list_categories('child_of='.$catobj->term_id.'&depth=0&hide_empty=0&title_li=');//リストはこの関数で$catobj->term_idを指定して取得 
            echo '</ul>';
        }
      ?>
    

     

    私はカテゴリを階層にすると絶対にPHPが面倒になると確信を持っていたので避けているのだが、そんなこととは全く関係ない☆は普通に階層分けしているので結局やる羽目になった。まあこっちの技術力がないからあれをしないでというのは何なので別にいいのだが。。。

     

     

    視野が狭いとどういう時に苦労するか

     

    以下はアマゾンギフト券(メール)を模したものである。このサイトでアフィリエイトを始めようと一念発起したわけだが、以前にもアフィリエイトに挑戦し、支払方法を選択してくださいの状態で5年だか10年位前に作って放置していたので、アマゾンギフト券を選択したところ意外なことにそこそこまとまった金額が届いた。

    さて、この中で、私はギフト券番号を探すのに3分くらいかかった。金額も受取人名も有効期限も注文番号も、10秒以内に把握したが、ギフト券番号だけは最後までわからなかった。

    なぜか。それがこの記事の本題である。

     

     

    ¥3,000のAmazonギフト券をお送りします。このメッセージは削除せず大切に保管してください。
    このギフト券を使用して注文するには以下のギフト券番号 が必要になります

      Amazon.co.jp ギフト券  
      金額:
    ¥3,000
    ギフト券番号:
    ABCD-EFGHIJ-KLMN
    Amazonギフト券のご利用方法
    card_footer
     
    お受取人:空鳥 夜鳥
    贈り主:Amazon Associates Program
    メッセージ: Here is your Amazon Associates payment.
    有効期限:2028/06/18 注文番号: xxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      買い物をする | Amazonギフト券をアカウントに追加する | Amazonギフト券の使い方  

     

     

    順を追ってみてみよう。

    ①まず、メールが届く。タイトルから、これがアマゾンギフト券であることを把握する。

    ②そこから、デジタルな金券の特性上、アルファベットないし数字あるいはその両方で構成された無機質な文字の並び(場合によってはそれがハイフンで区切られている)が必要になるので、恐らくそれがこのメールの中に書かれているであろうことが推測できる。

    ③メールを開ける。以下がその本文である。

    a. ここで、まずエリアCを無視する。重要度はともかく、すぐに必要な情報が小さな字で書いてあるわけがないからだ。

    b. 次に、エリアAを読み、必要な情報の呼称が「ギフト券番号」であることを確認する。

    c. エリアBを流し読みし、保留にする。ギフト券番号がわからなけれは意味のない情報だ。

    amsc1

    ④ 上記アマゾンギフト券を見ながら、このメールで最も重要な情報は何か、を考える。まずわかるのは「アマゾンギフト券」のロゴだ。ここに情報は何もない。

    ⑤ そして次に書かれた情報が金額である。文字の大きさ的にも、位置的にも、目立つように書かれている。

    ⑥ 次に名前だ。自分の名前は自分にとって特別な情報であるから、殆ど自動的に注目するようになっている

    ⑦ その次が「Amazon Associates Program」の文字だ。「お受取人」「贈り主」の文字は薄い黄色で見づらく、その下の「メッセージ」は細字である。そして周囲は白い空間で囲まれているので、自分の名前で近くまで視線誘導されれば、次に目立つのがこの部分なのだ。

    ⑧ さて。簡単に得られる情報はこれで終わりなので、ギフト券番号を探さなければならない。

    ここでギフト券番号はとても重要な情報なので、大きな文字か、太字(濃い字)で書かれている可能性が高い。

    ⑨まず大きな字を探す。3000円の表記が見つかるが、これより大きい字はない。ギフト券番号は金額より遥かに重要な情報なので(金額は買い物までに分かればいいが、ギフト券番号がわからないとそもそもそのステップへ行けない)、少なくとも金額と同じサイズかそれ以上の大きさで書かれているはずだ。ところがそんな文字列はない。つまり、ギフト券番号は大きな文字で書かれてはいないことが予測できる

    ⑩ となると太字で書かれている可能性である。探すと自分の名前贈り主、そして

    買い物をする
    Amazonギフト券をアカウントに追加する
    Amazonギフト券の使い方

    という最下部の項目しか該当しない。つまり、ギフト券番号は大きな文字で書かれてもいないし、太字でもないという事になる。ここまでをまとめると、ギフト券番号の特徴は

    普通のフォントかつ普通以下の文字サイズ

    数字、アルファベット、ハイフンの何れかで構成されている

    そこそこ長い文字列

    であることが推測できる。

    ⑪アルファベットあるいは数字を探すと、2028/06/18が見つかる。次に注文番号が見つかり、これかとも思うが、「ギフト券番号」ではない。後は上の方の「¥3,000のAmazonギフト券をお送りします。~」の文章の中に書いてあるかとも思ったが違う。残るはギフト券中央、下線付きの「Amazonギフト券のご利用方法」という項目だが、これも違う。するとこの中にはギフト券番号が書かれていないといことになり、後可能性があるのは「Amazonギフト券のご利用方法」から始まるメール中の説明文だけになる。

    ⑫説明文A,Bを読むが、(当然だが)書いていない。やはり上のカードを模した画像の中にあると考える方が妥当だ。

    ⑬仕方がないので読みづらい黄色い文字を読む。「金額」まあ違うと分かっていても一つ一つ確認するのである。そして「ギフト券番号」を見つける。なんと金額より小さく、名前よりも薄い字で書かれていたのである。わからないわけだ。

     

    視野が狭いというのは、普通言う所の「対象がボケて見える」とは勝手が違う。情報の内容がわからない以前に、情報の場所がわからないのだ。だから常に経験から「どこに、どんな形で」情報があるかを予測し、それに基づいて捜索を行っている。

    逆に言えば予測が外れた場合には情報を発見できない。大きいとかいった特徴は関係なく、「予想通りの格好をしているか」だけが重要なのである。

     

    目立つというのは、広い視野でもって「広く浅く全体の状態を把握したときに、ある一か所がその他と比較して異質であれば、そこに注意がいく」という現象である。

    つまり一塊の情報からの絞り込みに有効なのであって、そもそも一度にとらえることのできる情報が少ない場合には役に立たないのだ。

     

    とはいえ現実にはここまで極論を考える必要はない(少なくとも私の場合は、だが)。なぜかというと文章に対する姿勢がまずページのある領域(面)→文章(線)→文字(点)という順番で意識を集中していくこと自体は恐らく同じだからだ。

    そうすると、文字を大きくするというのが無効だという事がわかる。なぜか?大きくなればなるほどそれは「面」に近くなるからだ。点を可能な限り点のままで強調する方法を考えると、必然的に色や太さといったところに落ち着くのである。

     

    vray for Blenderでオブジェクトにfog (2)

    vrayでオブジェクトにフォグを指定するもう一つの方法として、GlassノードのColorを白にしたうえでVolumeにFogを指定する方法がある。

     

    blender_fog_21

     

    blender_fog_22

     

     

     

     

    C++CLIでテキストファイルの入出力

    テキストファイルの書き込みにはStreamWriterを用いる。System::Text::Encodingでエンコーディングの指定が可能

    void FileWrite(System::String^ filepathname)
    {
      System::IO::StreamWriter^ sw = gcnew System::IO::StreamWriter(
        filepathname, //ファイル名
        false, //追記モード
        System::Text::Encoding::UTF8
        //System::Text::Encoding::GetEncoding("shift_jis") //文字コード
        );
    
    
      sw->Write("吾輩は猫である。名前はまだ無い。\n");
      sw->Write("どこで生れたかとんと見当がつかぬ。\n");
      sw->Write("何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。\n");
      sw->Write("吾輩はここで始めて人間というものを見た。\n");
      sw->Write("しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。\n");
      sw->Write("この書生というのは時々我々を捕えて煮て食うという話である。\n");
    
      sw->Close();
    }
    

     

    テキストファイルの読み込みにはStreamReaderを使用する。

    一行ずつの読み込みにはReadLineを用いる。ファイル終端判定はPeekを使うのが一般的のようだが、参考文献によると模範解答ではないらしい。ここではEndOfStreamを使っている

    void FileRead(System::String^ filepathname)
    {
    
      //"C:\test\1.txt"をShift-JISコードとして開く
      System::IO::StreamReader^ sr = gcnew System::IO::StreamReader(
        "C:\\test\\sample.txt",
        System::Text::Encoding::Encoding::UTF8);
    
      while(sr->EndOfStream != true ){
        System::String^ r = sr->ReadLine(); 
        System::Console::WriteLine( r );//ReadLineは改行を含まない
      }
    
      sr->Close();
    }
    

     

     

    int main(array<System::String ^> ^args){
    
      FileWrite("C:\\test\\sample.txt");
      FileRead("C:\\test\\sample.txt");
    
      System::Console::Read();
    }
    

     

    参考文献

    https://dobon.net/vb/dotnet/file/readfile.html

    https://www.aozora.gr.jp/cards/000148/files/789_14547.html

    http://d.hatena.ne.jp/saiya_moebius/20090116/1232072712

    グローバルフック マウスフック

    グローバルフックをするには、DLLにする必要がある。

    また、イベントがあったことを知るために、FindWindowで送信先のウィンドウクラス名を指定してSendMessageしている。

    マウスフックの時は、SetWindowsHookExに渡す引数はWH_MOUSEとなる。また、フックプロシージャのwParamにはイベントが入っている。

     

    DLL側

    dllmain.h

    #pragma once
    #include <Windows.h>
    BOOL APIENTRY DllMain(HMODULE hModule,
      DWORD  ul_reason_for_call,
      LPVOID lpReserved
    );
    HINSTANCE GetDllInstance();
    

    dllmain.cpp

    HINSTANCE hDLLInstance = nullptr;
    BOOL APIENTRY DllMain(HMODULE hModule,
      DWORD  ul_reason_for_call,
      LPVOID lpReserved
    )
    {
      hDLLInstance = hModule;
    
      return TRUE;
    }
    HINSTANCE GetDllInstance() {
      return hDLLInstance;
    }
    

    フックをかけるときにインスタンスハンドルが必要になるので、DllMainの中で保存しておく。

     

    DLLDefs.h

    DLL側にはプリプロセッサにDLL_EXPORT_DOをセットする。

    #ifdef  DLL_EXPORT_DO
    /*  DLLを作る場合   */
    #define  __DLL_PORT  extern "C" __declspec(dllexport)
    #define  __DLL_PORT_CLS __declspec(dllexport)
    #else
    /*  DLLを使う場合   */
    #define  __DLL_PORT  extern "C" __declspec(dllimport)   
    #define  __DLL_PORT_CLS __declspec(dllimport)
    #endif
    

    このファイルは次のMouseHook.hでincludeされるが、結果的にexe側でもincludeされることになるので、両方のプロジェクトから読める必要がある。

     

     

    MouseHook.h

    #pragma once
    #include <Windows.h>
    #include "../DLLDefs.h"
    
    //マウスがクリックされたら、本体のプログラムへこのメッセージを送信する
    constexpr UINT WM_HOOKED_MOUSEDOWN = WM_USER + 2;
    
    class __DLL_PORT_CLS CMouseHook {
      static HHOOK hHook;
      static LRESULT CALLBACK MouseProcedure(int p_nCode, WPARAM p_wParam, LPARAM p_lParam);
    public:
      BOOL Set();
      BOOL Unset();
      ~CMouseHook();
    };
    

    フック関数とフックハンドルはstaticにする。

     

    MouseHook.cpp

    #include "MouseHook.h"
    #include "dllmain.h"
    #include <tchar.h>
    
    #pragma warning(disable:4996)
    #pragma data_seg( ".CMouseHookHandle" )
    HHOOK CMouseHook::hHook = nullptr;
    #pragma data_seg()
    
    BOOL CMouseHook::Set() {//フックをセットする関数
      hHook = ::SetWindowsHookEx(WH_MOUSE //種類はマウスフック
        , (HOOKPROC)CMouseHook::MouseProcedure
        , GetDllInstance(), 0);//このDLLのインスタンスハンドルを指定
    
      if (!hHook)
        return FALSE;
    
      return TRUE;
    }
    
    BOOL CMouseHook::Unset() {//フックを外す
      BOOL ret = FALSE;
    
      if (hHook)
        ret = ::UnhookWindowsHookEx(hHook);
    
      hHook = nullptr;
      return ret;
    }
    
    //フックの処理
    LRESULT CALLBACK CMouseHook::MouseProcedure(int p_nCode, WPARAM p_wParam, LPARAM p_lParam) {
      if (p_nCode < 0)
        return ::CallNextHookEx(hHook, p_nCode, p_wParam, p_lParam);
    
      if (p_nCode == HC_ACTION) {
    
        MOUSEHOOKSTRUCT *pmh = (MOUSEHOOKSTRUCT *)p_lParam;
    
        if (pmh) {
          if (p_wParam == WM_LBUTTONDOWN || p_wParam == WM_NCLBUTTONDOWN) {
                                      //このNU_WND_CLASS_NAMEは、exe側で作成するウィンドウのウィンドウクラス名
            PostMessage(FindWindow(_T("NU_WND_CLASS_NAME"), nullptr), WM_HOOKED_MOUSEDOWN, p_wParam, p_lParam);
                                                                       //exe側にWM_HOOKED_MOUSEDOWNを送る
          }
        }
      }
      return ::CallNextHookEx(hHook, p_nCode, p_wParam, p_lParam);;
    }
    CMouseHook::~CMouseHook() {
      Unset();
    }
    

     

    MouseHook.def

    SECTIONS
     .CMouseHookHandle	READ WRITE SHARED
    

     

    exe側

    #pragma comment(lib, "MouseHook.lib")
    #include "../MouseHook/MouseHook.h"
    #include<windows.h>
    #include <tchar.h>
    //ウィンドウプロシージャ
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
      switch (msg) {
      case WM_DESTROY:
        DestroyWindow(hwnd);
        PostQuitMessage(0);
        return 0;
      case WM_HOOKED_MOUSEDOWN:
        OutputDebugString(_T("どこかの窓でマウスがクリックされました\n"));
        return 0;
      }
      return DefWindowProc(hwnd, msg, wp, lp);
    }
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
      PSTR lpCmdLine, int nCmdShow) {
      HWND hwnd;
      MSG msg;
      WNDCLASS winc;
    
      //ウィンドウクラスの登録
      winc.style = CS_HREDRAW | CS_VREDRAW;
      winc.lpfnWndProc = WndProc;
      winc.cbClsExtra = winc.cbWndExtra = 0;
      winc.hInstance = hInstance;
      winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      winc.hCursor = LoadCursor(NULL, IDC_ARROW);
      winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      winc.lpszMenuName = NULL;
      winc.lpszClassName = TEXT("NU_WND_CLASS_NAME");
    
      if (!RegisterClass(&winc)) return 0;
    
    
      //フックをかける
      CMouseHook hm;
      hm.Set();
    
      //ウィンドウ作成
      hwnd = CreateWindow(
        TEXT("NU_WND_CLASS_NAME"), TEXT("test"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        100, 100, 200, 200, NULL, NULL,
        hInstance, NULL
      );
    
      if (hwnd == NULL) return 0;
    
      while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
    
      //フックを外す
      hm.Unset();
    
      return msg.wParam;
    }
    

     

     

    vray for Blenderでオブジェクトにfog

    VRAY for Blenderでfogを使う方法。

    オブジェクトのマテリアルではなく、Worldの方のEffectsにフォグを設定し、「FogをObjectの中限定で発生させる」という考え方をするらしい。

    複数のオブジェクトをフォグにする場合、Effects ContainerにAddを使い、二つ以上の設定を束ねてWorld Outputへ接続する。

     

    vray_fog_nodes

     

    vray_fog

    はじめてのCUDAプログラム

    CUDAをやってみたくなった。

    インストールがもの凄く簡単になっていて、Visual C++ 2017 Communityが入っているなら

    https://developer.nvidia.com/cuda-downloads

    からCUDA Toolkitをダウンロードしてインストールするだけで、VC++のプロジェクトにCUDAのプロジェクトが追加される。

     

     

    以下、初めてのCUDAプログラム

    #include "cuda_runtime.h"
    #include "device_launch_parameters.h"
    #include <stdio.h>
    #include <cstring>
    __global__ void hello(char *c) {
      c[0] = 'h';
      c[1] = 'e';
      c[2] = 'l';
      c[3] = 'l';
      c[4] = 'o';
      c[5] = '\0';
    }
    int main(void) {
      const size_t LEN = strlen("hello") + 1;//(CPU側)
      char c_cpu[LEN];//(CPU側)
      char *c_gpu;//(CPU側)
    
      cudaMalloc( (void**)&c_gpu, LEN);//GPU側にメモリを確保
    
      hello <<<1, 1 >>>( c_gpu );//GPU側の関数を呼出
    
      cudaMemcpy(&c_cpu, c_gpu, LEN, cudaMemcpyDeviceToHost);//GPU側から実行結果を取得
    
      cudaFree(c_gpu);//GPU側のメモリを解放
    
      puts(c_cpu);//(CPU側)
    
      getchar();//(CPU側)
    
      return 0;
    }
    

     

    cudaMalloc

    GPU側のメモリを確保するための関数。

     

    関数名 cudaMalloc
    戻り値 cudaError_t
    引数
    void** devPtr GPU側で確保したメモリのアドレスを格納するためのvoid**型の変数
    size_t  size 確保するサイズをバイト単位で指定

    参考URI:

    https://www.cs.cmu.edu/afs/cs/academic/class/15668-s11/www/cuda-doc/html/group__CUDART__MEMORY_gc63ffd93e344b939d6399199d8b12fef.html

     

    cudaFree

    cudaMallocで確保したメモリを開放する

    関数名 cudaFree
    戻り値 cudaError_t
     
    void *  devPtr cudaMallocで確保した領域のアドレス

    参考URI

    https://www.cs.cmu.edu/afs/cs/academic/class/15668-s11/www/cuda-doc/html/group__CUDART__MEMORY_gb17fef862d4d1fefb9dba35bd62a187e.html#gb17fef862d4d1fefb9dba35bd62a187e

    C++CLIでメンバ関数をデリゲート経由で呼び出す

    特に解説することはない。

     

    ref class CTest{
    public:
      int func(double v){
        return (int)v * (int)v;
      }
      delegate int DelegateToFuncT(double v);//デリゲート型を定義
    };
    
    ///////////////////////////////////////////////////
    
    int main(array<System::String ^> ^args)
    {
    
      CTest^ ct = gcnew CTest();
    
      //デリゲート作成
      CTest::DelegateToFuncT^ dfunc = gcnew CTest::DelegateToFuncT(ct,&CTest::func);
    
      int ret = dfunc( 5.5 );//呼出
    
      Console::WriteLine( System::String::Format("** {0}\n",ret) );
    
      Console::ReadLine();
    
      return 0;
    }
    

     

     

     

     

     

    C++CLIでdll内のクラスを使う時の error C2011: ‘***’ : ‘class’ 型の再定義

    #pragma onceを忘れたとかそんな話ではない。

    すべてCLIで、testclidll.dll内にクラスClass1を作成し、それをexe側に参照を追加してClass1を使おうとしたとき、

     

    error C2011: ‘testclidll::Class1’ : ‘class’ 型の再定義

     

    が発生する

     

    再現手順 (Visual Studio 2017 community)

    問題1

    1.[ファイル] – [新規作成] – [プロジェクト] から、「CLRコンソールアプリケーション」を作成(名前をexe_and_dll_testとする)

    2.[ファイル] – [新規作成] – [プロジェクト] から、「クラスライブラリ」を作成(名前をtestclidllとする)

    3.testclidll.hを以下のようにする

    // testclidll.h
    #pragma once
    using namespace System;
    namespace testclidll {
    
    
      public ref class Class1
      {
        int c;
      public:
        void Set(int d);
        int Get();
    
      };
    }
    

     

    4.testclidll.cppを以下のようにする

    namespace testclidll {
    
      void Class1::Set(int d){
        c = d;
      }
      int Class1::Get(){
        return c;
      }
    }
    

     

    5.exe側を以下のようにする

    #include "stdafx.h"
    using namespace System;
    #include "../testclidll/testclidll.h"
    int main(array<System::String ^> ^args)
    {
    
      testclidll::Class1^ c = gcnew testclidll::Class1;
    
      c->Set(10);
    
      Console::WriteLine(System::String::Format("{0}\n",c->Get()));
        return 0;
    }
    

     

    6.DLLをコンパイルする

    7.ソリューションエクスプローラから、exe側のプロジェクトを右クリックし、[参照…]を選択

     プロパティページから、[共通プロパティ]-[Frameworkと参照]で「新しい参照の追加…」ボタンを押し、コンパイルしたdllを追加する

    8.exe側をコンパイルする

     

    2>e:\mycodes\exe_and_dll_test\exe_and_dll_test\../testclidll/testclidll.h(12): error C2011: ‘testclidll::Class1’ : ‘class’ 型の再定義
    2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : ‘testclidll::Class1’ の宣言を確認してください。
    2>exe_and_dll_test.cpp(14): error C2027: 認識できない型 ‘testclidll::Class1’ が使われています。
    2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : ‘testclidll::Class1’ の宣言を確認してください。
    2>exe_and_dll_test.cpp(14): error C2227: ‘->Set’ : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
    2>exe_and_dll_test.cpp(18): error C2027: 認識できない型 ‘testclidll::Class1’ が使われています。
    2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : ‘testclidll::Class1’ の宣言を確認してください。
    2>exe_and_dll_test.cpp(18): error C2227: ‘->Get’ : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。

     

    解決策1

    まず、C/C++ではdllをリンクするときにも必ずヘッダファイルをincludeしなければならないが、.NETではdllが情報を持っている(?)ので、そこへさらにインクルードをかけると定義が二重に行われる(のだと思う)。従って、

    #include "stdafx.h"
    using namespace System;
    //#include "../testclidll/testclidll.h" //このincludeはしてはいけない
    int main(array<System::String ^> ^args)
    {
    
      testclidll::Class1^ c = gcnew testclidll::Class1;
    
      c->Set(10);
    
      Console::WriteLine(System::String::Format("{0}\n",c->Get()));
        return 0;
    }
    

     

    問題2

    再現手順

    1. testclidll.hを、以下のように修正する

    // testclidll.h
    #pragma once
    using namespace System;
    namespace testclidll {
    
      enum ReturnT{ //型を追加
        True,False,
      };
    
      public ref class Class1
      {
        int c;
      public:
        void Set(int d);
        int Get();
    
      ReturnT isOdd(); //メンバが奇数かどうかを返す関数を追加。戻り値はユーザー定義型のReturnT
    
      };
    }
    

     

    2.testclidll.cppを以下のように修正する

    // これは メイン DLL ファイルです。
    #include "stdafx.h"
    #include "testclidll.h"
    namespace testclidll {
    
      void Class1::Set(int d){
        c = d;
      }
      int Class1::Get(){
        return c;
      }
    
      //メンバが奇数かどうかを返す関数の定義
      ReturnT Class1::isOdd(){
    
        if( c % 2 != 0 )
          return True;
    
        return False;
    
      }
    }
    

     

    3.exe側を以下のように修正する

    #include "stdafx.h"
    using namespace System;
    //#include "../testclidll/testclidll.h"
    int main(array<System::String ^> ^args)
    {
    
      testclidll::Class1^ c = gcnew testclidll::Class1;
    
      c->Set(10);
      
      testclidll::ReturnT t = c->isOdd();//奇数かどうかの判定を追加
    
      Console::WriteLine(System::String::Format("{0}\n",c->Get()));
        return 0;
    }
    

     

    4.testclidllをコンパイルする

     

    5.exe側をコンパイルする

     

    コンバイルエラー

    2>exe_and_dll_test.cpp(16): error C2039: ‘ReturnT’ : ‘testclidll’ のメンバーではありません。
    2>exe_and_dll_test.cpp(16): error C2065: ‘ReturnT’ : 定義されていない識別子です。
    2>exe_and_dll_test.cpp(16): error C2146: 構文エラー : ‘;’ が、識別子 ‘t’ の前に必要です。
    2>exe_and_dll_test.cpp(16): error C2065: ‘t’ : 定義されていない識別子です。

     

    これは、クラスの情報は参照したdllの中にあるが、enumのような定数の情報は入っていない(?)ので、定義がないと怒られる。そこで、testclidll.hのincludeを復活させてみる。

    #include "stdafx.h"
    using namespace System;
    #include "../testclidll/testclidll.h" //やっぱりいるのか?
    int main(array<System::String ^> ^args)
    {
    

     

    1>e:\mycodes\exe_and_dll_test\exe_and_dll_test\../testclidll/testclidll.h(14): error C2011: ‘testclidll::Class1’ : ‘class’ 型の再定義
    1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : ‘testclidll::Class1’ の宣言を確認してください。
    1>exe_and_dll_test.cpp(14): error C2027: 認識できない型 ‘testclidll::Class1’ が使われています。
    1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : ‘testclidll::Class1’ の宣言を確認してください。
    1>exe_and_dll_test.cpp(14): error C2227: ‘->Set’ : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
    1>exe_and_dll_test.cpp(16): error C2027: 認識できない型 ‘testclidll::Class1’ が使われています。
    1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : ‘testclidll::Class1’ の宣言を確認してください。
    1>exe_and_dll_test.cpp(16): error C2227: ‘->isOdd’ : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
    1>exe_and_dll_test.cpp(18): error C2027: 認識できない型 ‘testclidll::Class1’ が使われています。
    1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : ‘testclidll::Class1’ の宣言を確認してください。
    1>exe_and_dll_test.cpp(18): error C2227: ‘->Get’ : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。

     

    当然のように最初のエラーが再発する。

     

    解決策2

    enumの定義を別のヘッダファイルへ移動する

    testclidllプロジェクト内、defvalheader.hを追加

    namespace testclidll {
    
      enum ReturnT{
        True,False,
      };
    }
    

    それに伴い、dll本体側であるtestclidll.hからenumの定義を消し、includeにする

    // testclidll.h
    #pragma once
    #include "defvalheader.h" //ReturnTの定義を読み込む
    using namespace System;
    namespace testclidll {
    
      public ref class Class1
      {
        int c;
      public:
        void Set(int d);
        int Get();
    
        ReturnT isOdd();
    
      };
    }
    

     

    exe側では、testclidll.hではなく、defvalheader.hを読み込む

    #include "stdafx.h"
    using namespace System;
    #include "../testclidll/defvalheader.h"
    int main(array<System::String ^> ^args)
    {
    
      testclidll::Class1^ c = gcnew testclidll::Class1;
    
      c->Set(10);
      
      testclidll::ReturnT t = c->isOdd();
    
      Console::WriteLine(System::String::Format("{0}\n",c->Get()));
        return 0;
    }