#include <iostream> // メモリリークを検出した位置を表示するためのマクロ #define _CRTDBG_MAP_ALLOC // メモリリークの検出 #include <crtdbg.h> // メモリリークしたメモリのnewした場所のファイル名と行数を表示する // #define _CRTDBG_MAP_ALLOC で動く機能のはずなのだがなぜか機能しないので自分で定義する #define new ::new(_NORMAL_BLOCK, __FILE__, __LINE__)
class Dummy { public: char* myleak() { char* p = new char[3] {0, 0, 0}; // メモリリークしている (19行目) return p; } void mysafety() { int* q = new int[3] {1, 1, 1}; // メモリリークしていない (24行目) printf("%d %d %d\n", q[0], q[1], q[2]); delete [] q; } Dummy() {} ~Dummy() {} };
int main() { // アプリケーション終了時に_CrtDumpMemoryLeaks()が呼ばれるように指示 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); Dummy d; std::cout << "Hello World!\n"; char* c = d.myleak(); d.mysafety(); // 出力内容と出力先 // https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/crtsetreportmode?view=msvc-170 // _CRT_WARN ... 警告、メッセージ、およびすぐに注意する必要のない情報。 // _CRTDBG_MODE_DEBUG ... デバッガーの出力ウィンドウにメッセージを書き込みます。 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); // メモリリークの情報を出力 // この関数は、呼び出された時点で解放されていないメモリを全て検知する。 // アプリケーション終了時でのリークを検出したいならここで書かないで // _CrtSetDbgFlagでプログラムが終了したときに自動的に呼び出されるように指示したほうがいい。 // _CrtDumpMemoryLeaks(); return 0; }
_CrtSetReportMode関数で_CRTDBG_MODE_DEBUGを指定しているので、デバッグウィンドウに以下の出力がされる。
ファイル名 , newした行番号 , newしたメモリサイズ がそれぞれ出力される。
癖をまとめてくれているサイト。
プログラマの友
http://www7b.biglobe.ne.jp/~robe/pf/pf008.html
このcrtdbg.hによるメモリリーク検出、プロジェクトの規模が大きくなってくるとかなり気を遣わなければいけないらしい。
閑古鳥
WordPressの管理画面の投稿一覧が、いつの間にかタイトルなどのカラムがとても狭くなってしまって困った。これを解決する方法を探した。functions.phpでadmin_headをadd_actionすればよいらしい。
column-cbはチェックボックス部分の幅。一応指定している。
title(タイトル),author(投稿者),categories(カテゴリ), ... をそれぞれ指定。
.postsは投稿一覧、.pagesは固定ページ一覧。
<?php /****************************************/ /* 投稿・固定ページ一覧のカラム幅指定 */ function column_title_width(){ echo '
<style>
.posts .column-cb { width: 40px; } .pages .column-cb { width: 40px;} .posts .column-title { width: 300px; } .pages .column-title { width: 300px; } .posts .column-author { width: 50px; } .pages .column-author { width: 50px; } .posts .column-categories { width: 100px; } .pages .column-categories { width: 100px; } .posts .column-tags { width: 150px; } .pages .column-tags { width: 150px; } </style>
'; } add_action('admin_head','column_title_width'); /****************************************/ ?>
VC++で、.cpp と .cuファイルが混在したプロジェクトのビルド方法。
プロジェクトを右クリックして、「ビルドの依存関係」→「ビルドのカスタマイズ」を選択。
使用できるビルド カスタマイズ ファイルの中から、 CUDA 12.1 をチェックして OK をクリックする。
.cuファイルを右クリック → プロパティ → プロパティページ を開き、「全般」から以下を設定
・ビルドから除外 → いいえ
・項目の種類 → CUDA C/C++
#include <windows.h> #include <memory> #include "mycuda.cuh" #pragma warning(disable:4996) void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p); static const int N = 500;//作成画像 N × N LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { static std::unique_ptr<unsigned char[]> p_cpu; switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_CREATE: p_cpu.reset(new unsigned char[N * N * 3]); call_mycuda(N, p_cpu.get()); return 0; case WM_PAINT: { // dc設定 PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); for(int x=0;x<N;x++) for (int y = 0; y < N; y++) { size_t pos = N * y + x;//二次元座標を一次元座標に SetPixel(hdc, x, y, RGB( p_cpu[pos * 3 + 0], p_cpu[pos * 3 + 1], p_cpu[pos * 3 + 2] ) ); } EndPaint(hwnd, &ps); } } 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("SZL-WINDOW"); if (!RegisterClass(&winc)) return -1; hwnd = CreateWindow( TEXT("SZL-WINDOW"), TEXT("mywindow"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, N+100, N+100, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return -1; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; } ///////////////////////////////////////////// //画像ファイル書き出し///////////////////////// //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]); k++; } fprintf(fp, "\n"); } fclose(fp); }
#include <stdio.h> #include <cstring> #include <cuda_runtime.h> void call_mycuda(const int N, unsigned char* p_cpu); /////////////////////////////////////////////// // GPU側 ////////////////////////////////////// __global__ void gradation(unsigned char* c, const int N);
#include "mycuda.cuh" #include <device_launch_parameters.h> #pragma comment(lib,"cudart.lib") #pragma comment(lib,"cuda.lib") void call_mycuda(const int N, unsigned char* p_cpu) { //作成画像 N × N × 3 //16×16の領域に分けて計算する dim3 block(16, 16); //グリッド数 dim3 grid(N / 16 + 1, N / 16 + 1); unsigned char* p_gpu;//GPU側メモリ確保 cudaMalloc((void**)&p_gpu, N * N * 3); gradation << <grid, block >> > (p_gpu, N); //GPU→CPU側へメモリコピー cudaMemcpy(p_cpu, p_gpu, N * N * 3, cudaMemcpyDeviceToHost); cudaFree(p_gpu); } __global__ void gradation(unsigned char* c, const int N) { //アクセス法 //このスレッドが担当する画素の位置を二次元座標で求める size_t xpos = blockIdx.x * blockDim.x + threadIdx.x; size_t ypos = blockIdx.y * blockDim.y + threadIdx.y; if (xpos < N && ypos < N) { size_t pos = N * ypos + xpos;//二次元座標を一次元座標に float R = ypos / (float)N; float G = (N - ypos) / (float)N; float B = xpos / (float)N; c[pos * 3 + 0] = R * 255;//0~255にして色書き込み c[pos * 3 + 1] = G * 255; c[pos * 3 + 2] = B * 255; } }
レベルとは(誤解を恐れずに)端的にいうとマップのこと。
レベルは保存しなければ操作できないので、まず作成・保存方法を確認する。
レベルを追加するには [新規レベル] → [新規レベル] から行う。
レベルを保存するには、[ファイル] → [現行レベルを保存] から行う。名前を付けると、コンテンツブラウザに登録される。
上記方法でレベルを二つ作成して保存(My-Map-1 、 My-Map-2)したら、
1.My-Map-1 をダブルクリックして開く
2.レベルブループリントを開く
3.ノードを設定
Keyboard F に Open Level を接続し、Level に先ほど作成した My-Map-2を指定する。
これでMy-Map-1を実行し、Fを押すとMy-Map-2へ切り替わる。
VTK9.3で、PLY読み込みなどを行う際に進捗状況を%表示する方法。
vtkProgressObserverを指定するSetProgressObserverメンバ関数はvtkAlgorithmで定義されているので、vtkAlgorithmを継承した処理でないとこの方法は使えない。
1.vtkProgressObserverを継承したMyProgressObserverを定義
2.UpdateProgressをオーバーライドし、amountから進捗を取得
3.MyProgressObserverのインスタンスを作成
4.vtkPLYReaderなどのインスタンスを作成
5.SetProgressObserverにオブザーバーのポインタを与える
// 進捗管理用 #include <vtkProgressObserver.h> // PLYファイル読み込み用 #include <vtkPLYReader.h> // 読み込めているか確認用 #include <vtkPointData.h>
////////////////////////////////////////////////// // 進捗管理のためにvtkProgressObserverを継承したクラスを作成 class MyProgressObserver : public vtkProgressObserver { public: // UpdateProgressをオーバーライド virtual void UpdateProgress(double amount)override { // 進捗を表示 printf("== %d\n", int(amount*100) );// amountは進捗状況を0.0~1.0で表現 } };
void myload() { const char* filename = "C:\\test\\mill.ply"; //////////////////////////////////////////////////////////// // 進捗管理用のオブザーバを作成 vtkSmartPointer<MyProgressObserver> observer = vtkSmartPointer<MyProgressObserver>(new MyProgressObserver); //////////////////////////////////////////////////////////// // PLYファイルを読み込むオブジェクト作成 vtkSmartPointer<vtkPLYReader> plyReader = vtkSmartPointer<vtkPLYReader>::New(); plyReader->SetProgressObserver(observer);// オブザーバを設定 plyReader->SetFileName(filename); plyReader->Update(); //////////////////////////////////////////////////////////// // 読み込んだデータを取得 vtkSmartPointer<vtkPolyData> polyData = plyReader->GetOutput(); vtkSmartPointer<vtkPoints> points = polyData->GetPoints(); if (points == nullptr) { return; } for (vtkIdType i = 0; i < points->GetNumberOfPoints(); i++) { double p[3]; points->GetPoint(i, p); printf("%lf %lf %lf\n", p[0], p[1], p[2]); } }
Landscapeは地形。現在のレベルに新に地形を追加する。
選択モード → ランドスケープ からランドスケープモードへ移行する
ランドスケープの設定をして「作成」をクリック。あまり大きくしすぎると編集が大変になるので、コンポーネント数を1x2にしておく。
スカルプトの要領で、マウスドラッグで地形を作れる。ただし作れるのは高低差だけなので、洞窟のようなものはできない。
実行すると、地形の上を歩くことができるようになっている。
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