icuライブラリのU16_NEXTでコードポイントを取得できる。
#include <unicode/ubrk.h> #include <unicode/ustring.h> #ifdef _DEBUG // デバッグ時リンク #pragma comment(lib, "icuucd.lib") #pragma comment(lib, "icuind.lib") #else // リリース用リンク #pragma comment(lib, "icuuc.lib") #pragma comment(lib, "icuin.lib") #endif // Utf16文字列からUtf32文字列に変換 std::u32string u16_to_u32(const char16_t* u16str) { UErrorCode status = U_ZERO_ERROR; // イテレータ作成 UBreakIterator* bi = ubrk_open(UBRK_CHARACTER, nullptr /*文字単位の処理にロケールは不要*/, nullptr, 0, &status); if (U_FAILURE(status)) { return U"";// エラーが発生 } int32_t utf16Length = wcslen((wchar_t*)u16str); // テキストを設定 ubrk_setText(bi, (const UChar*)u16str, utf16Length, &status); if (U_FAILURE(status)) { ubrk_close(bi); return U"";// エラーが発生 } std::u32string ret32; // 文字境界を使ってUTF-16からUTF-32に変換 int32_t start = ubrk_first(bi); // 文字列(utf16)の最後までループ for (int32_t end = ubrk_next(bi); end != UBRK_DONE; start = end, end = ubrk_next(bi)) { int32_t i = start; while(i < end) { UChar32 c32; // ① u16str[i] 以降の文字を一文字コードポイントに変換してcに格納 // ② iを次の文字の位置に進める U16_NEXT(u16str, i, utf16Length, c32); // コードポイントを保存 ret32.push_back(c32); } } // 終了処理 ubrk_close(bi); return ret32; };
変換したところで結果を確認するまともな方法(エディタなど)が少ないので、先に作っておいた前回の render_by_font を使い、結果を出力する。
std::u32stringをFreeType2+HarfBuzzで描画する処理を関数化
#include"u16_to_u32.hpp" int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; // HarfBuzzのオブジェクト作成 hb_buffer_t* hbbuf; hbbuf = hb_buffer_create(); ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// // 出力先を作成 Image image(300, 150); ///////////////////////////////////////////// FaceInfo faces[3] = { FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\msgothic.ttc"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\seguiemj.ttf"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\Times.ttf"), HB_SCRIPT_LATIN, "en" ) }; ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// { std::u32string text32 = u16_to_u32(u"あ゙いう"); render_by_font( &image, faces[0].face, text32.c_str(), faces[0].script, hbbuf, faces[0].hbfont, faces[0].lang.c_str() ); pbmP1_Write("freetypetest_jp.pbm", image.imageWidth, image.imageHeight, &image.image[0]); } { std::u32string text32 = u16_to_u32(u"👨👨👧👦"); render_by_font( &image, faces[1].face, text32.c_str(), faces[1].script, hbbuf, faces[1].hbfont, faces[1].lang.c_str() ); pbmP1_Write("freetypetest_mj.pbm", image.imageWidth, image.imageHeight, &image.image[0]); } { std::u32string text32 = u16_to_u32(u"àbâáăã"); render_by_font( &image, faces[2].face, text32.c_str(), faces[2].script, hbbuf, faces[2].hbfont, faces[2].lang.c_str() ); pbmP1_Write("freetypetest_la.pbm", image.imageWidth, image.imageHeight, &image.image[0]); } for(auto& f : faces){ f.Delete(); } ///////////////////////////////////////////// ///////////////////////////////////////////// ///////////////////////////////////////////// // HarfBuzzのオブジェクト破棄 hb_buffer_destroy(hbbuf); // FreeType2の解放 FT_Done_FreeType(library); }
諸事情により、以前作成したFreeType2+HarfBuzzのコードを関数化する。
HarfBuzzを使いまわす場合、バッファのクリアが必要となり、hb_buffer_clear_contentsを使用する。
#pragma once #ifdef _DEBUG #pragma comment(lib,"freetyped.lib") #pragma comment(lib,"harfbuzz.lib") #else #pragma comment(lib,"freetype.lib") #pragma comment(lib,"harfbuzz.lib") #endif #include <ft2build.h> #include FT_FREETYPE_H #include <hb.h> #include <hb-ft.h>
struct Image { int imageWidth; int imageHeight; std::vector<unsigned char> image; Image(int w, int h) { image.resize(w * h); imageWidth = w; imageHeight = h; } //! @brief imageへの書き込み時のピクセル計算 int pixel_pos(const int x, const int y) { return y * imageWidth + x; } bool is_valid_area(int xx, int yy) { if (xx < 0)return false; if (yy < 0)return false; if (xx >= imageWidth)return false; if (yy >= imageHeight)return false; return true; } };
//! @brief imageへbmpの内容を書き込む //! @param [in] bmp 文字画像 //! @param [in] startx image画像内の書き込み開始位置 //! @param [in] starty image画像内の書き込み開始位置 void draw(Image* image, const FT_Bitmap& bmp, int startx, int starty) { int Width = bmp.width; int Height = bmp.rows; for (size_t y = 0; y < Height; y++) { for (size_t x = 0; x < Width; x++) { int xx = startx + x; int yy = starty + y; if( image->is_valid_area(xx, yy) == false)continue; if (bmp.buffer[y * Width + x]) { (image->image)[image->pixel_pos(xx, yy)] = 1; } } } }
//! @brief フォントの読込 FreeType2の処理 //! @param [in] library FT_Library //! @param [in] fontfile フォントファイルへのパス //! @return フェイスオブジェクト FT_Face* my_LoadFonts( FT_Library& library, const char* fontfile ) { FT_Face* face = new FT_Face; FT_Error error; // フォントファイル読み込み error = FT_New_Face( library, fontfile, 0, face ); //文字コード指定 error = FT_Select_Charmap( *face, FT_ENCODING_UNICODE // エンコード指定 ); if (error == FT_Err_Unknown_File_Format) return nullptr; else if (error) return nullptr; int pixel_size_y = 64; error = FT_Set_Pixel_Sizes( *face, 0, pixel_size_y); return face; }
//! @brief HarfBuzzで計算した座標を使いFreeType2で文字列を描画する //! @param [in] image 描画先 //! @param [in] hbbuf HarfBuzzオブジェクト //! @param [in] face FreeType2のフェイス void my_FaceDraw(Image* image, hb_buffer_t* hbbuf, FT_Face* face) { // 描画先をクリア std::fill(image->image.begin(), image->image.end(), 0); //文字数を格納 (書記素数ではない。例えば「あ゙」は2文字) unsigned int glyph_count; hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count); hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count); hb_position_t cursor_x = 0; hb_position_t cursor_y = 0; // 各文字ごとに描画する for (unsigned int i = 0; i < glyph_count; i++) { // codepointという変数名だが実際にはグリフインデクスが入っている hb_codepoint_t glyphid = glyph_info[i].codepoint; // 一文字分のオフセット。本来描画される位置からどれぐらいずれるか hb_position_t x_offset = glyph_pos[i].x_offset >> 6;// 結合文字の゛は本来の位置より左側に描画するので hb_position_t y_offset = glyph_pos[i].y_offset >> 6;// x_offsetにはマイナスの値が入る // 次の文字の描画開始位置までのピクセル数 hb_position_t x_advance = glyph_pos[i].x_advance >> 6; hb_position_t y_advance = glyph_pos[i].y_advance >> 6; /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// FT_Error error = FT_Load_Glyph(*face, glyphid, FT_LOAD_RENDER); if (error) { continue; } // 文字を画像化 FT_Render_Glyph((*face)->glyph, FT_RENDER_MODE_NORMAL); // 画像書き込み // オフセットを加えて座標調整する draw( image, (*face)->glyph->bitmap, cursor_x + x_offset + (*face)->glyph->bitmap_left, cursor_y + y_offset - (*face)->glyph->bitmap_top + 100 ); /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// // 次の文字の描画開始値 cursor_x += x_advance; cursor_y += y_advance; } }
void render_by_font( Image* image, FT_Face* ft_face, const std::u32string& text, hb_script_t script, hb_buffer_t* hbbuf, hb_font_t* hbfont, std::string lang ) { // バッファの内容をクリア // hb_buffer_tを再利用するときは、hb_buffer_clear_contents()でバッファをクリアする hb_buffer_clear_contents(hbbuf); // バッファにテキストを追加 hb_buffer_add_utf32(hbbuf, (const std::uint32_t*)text.data(), -1, 0, -1);// 描画したいテキストの設定 hb_buffer_set_direction(hbbuf, HB_DIRECTION_LTR); // 文字の方向を左から右として設定 hb_buffer_set_script(hbbuf, script); // Unicodeの用字(Script)として日本語を指定 hb_buffer_set_language(hbbuf, hb_language_from_string(lang.c_str(), -1));// 言語として日本語を設定 //////////////////////// hb_shape(hbfont, hbbuf, NULL, 0); //////////////////////// my_FaceDraw(image,hbbuf, ft_face);//FreeType2とHarfBuzzで文字列描画 //////////////////////// }
#include <iostream> #include <vector> #include <fstream> #include <filesystem> #include <unordered_map> #include"render_font.hpp" #pragma warning(disable:4996) #include <string> #include <array> //! @brief PBM(1byte,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details 1画素1Byteのメモリを渡すと、0,1テキストでファイル名fnameで書き込む void pbmP1_Write(const char* const fname, const int width, const int height, const unsigned char* const p);
//! @brief フォント情報を管理 struct FaceInfo { FT_Face* face; // フェイス(Freetype) hb_font_t* hbfont; // フェイス(HarfBuzz) hb_script_t script; // Unicodeの用字(=Script) std::string lang; // HarfBuzzに指定する言語 FaceInfo(FT_Face* f, hb_script_t s, const char* l) : face(f), script(s), lang(l) { // フォントオブジェクト作成 hbfont = hb_ft_font_create(*face, nullptr); } FaceInfo(FaceInfo&& fr){ face = fr.face; script = fr.script; lang = fr.lang; hbfont = fr.hbfont; fr.face = nullptr; fr.hbfont = nullptr; } // フォント破棄 void Delete() { if (hbfont) hb_font_destroy(hbfont); if (face) FT_Done_Face(*face); } };
int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; // HarfBuzzのオブジェクト作成 hb_buffer_t* hbbuf; hbbuf = hb_buffer_create(); ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// // 出力先を作成 Image image(300, 150); ///////////////////////////////////////////// // フォント情報作成 FaceInfo faces[3] = { FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\msgothic.ttc"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\seguiemj.ttf"), HB_SCRIPT_HIRAGANA, "jp" ), FaceInfo( my_LoadFonts(library, "C:\\Windows\\Fonts\\Times.ttf"), HB_SCRIPT_LATIN, "en" ) }; ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// { // フォントを使って文字列を描画 render_by_font( &image, faces[0].face, U"あ゙いう", faces[0].script, hbbuf, faces[0].hbfont, faces[0].lang.c_str() ); pbmP1_Write("freetypetest_jp.pbm", image.imageWidth, image.imageHeight, &image.image[0]);// 結果を保存 } { // フォントを使って文字列を描画 render_by_font( &image, faces[1].face, U"👨👨👧👦", faces[1].script, hbbuf, faces[1].hbfont, faces[1].lang.c_str() ); pbmP1_Write("freetypetest_mj.pbm", image.imageWidth, image.imageHeight, &image.image[0]);// 結果を保存 } { // フォントを使って文字列を描画 render_by_font( &image, faces[2].face, U"àbâáăã", faces[2].script, hbbuf, faces[2].hbfont, faces[2].lang.c_str() ); pbmP1_Write("freetypetest_la.pbm", image.imageWidth, image.imageHeight, &image.image[0]);// 結果を保存 } for(auto& f : faces){ f.Delete(); } ///////////////////////////////////////////// ///////////////////////////////////////////// ///////////////////////////////////////////// // HarfBuzzのオブジェクト破棄 hb_buffer_destroy(hbbuf); // FreeType2の解放 FT_Done_FreeType(library); } //! @brief PBM(1byte,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details 1画素1Byteのメモリを渡すと、0,1テキストでファイル名fnameで書き込む void pbmP1_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, "P1\n%d\n%d\n", width, height); 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 ", p[k] ? 0 : 1); k++; } fprintf(fp, "\n"); } fclose(fp); }
WSL2+Pythonでプログラムを書いた。これをWindows 11上でアイコンをダブルクリックして起動したい。
基本の環境を汚さないようにするため、Ubuntuをもう一つ入れてそちらで実行できるようにする。
ユーザ名、パスワードを入力した後、コンソールをもう一つ開いてwsl -l -v を実行すると、有効な環境一覧が得られる。
環境が分かったら、 wsl --set-default <環境名> で、使用する環境を切り替える。
wsl -d <環境名> で、環境に入れる
以下のコードをtest.pyとして保存。
import tkinter as tk from tkinter import messagebox # クリックイベント def on_button_click(): messagebox.showinfo("Message", "HelloWorld") # メインウィンドウの作成 root = tk.Tk() root.title("MessageBox") root.geometry("200x100") # ボタンの作成 button = tk.Button(root, text="Click Me!", command=on_button_click) button.pack(pady=20) # ウィンドウの表示 root.mainloop()
バッチファイルを作成し、以下を記述
-d オプションで使用する環境を指定。
python3 の後は自分のスクリプトが置いてあるパスを指定。
Ubuntu 22.04 LTS +Firefox 129.0.2 で動作確認。ちゃんと動いた。
Pybind11を使ってみる。
PYBIND11_MODULE(pydファイル名, 変数名 ) {
変数名 . def (
"Python側の関数名" ,
&C++側の関数名
);
}
// 呼び出し側のPythonのバージョンのライブラリを使用する
#pragma comment(lib, "python39.lib") #include <pybind11/pybind11.h> namespace py = pybind11; int my_add_func(int a, int b) { return a + b; } // PYBIND11_MODULE(モジュール名, モジュール変数名) // 生成物はmy_module_name.pydという名前にしなければならない PYBIND11_MODULE(my_module_name, m) { m.def( "call_in_python", // Python側で呼び出す関数名 &my_add_func, // C++側の関数名 py::arg("a"), // Python側での引数名(指定省略可) py::arg("b") // Python側での引数名(指定省略可) ); }
import my_module_name val = my_module_name.call_in_python(10,2) print(val)
#pragma comment(lib, "python39.lib") #include <pybind11/pybind11.h> namespace py = pybind11;
std::tuple<int,float> my_calc_func(int a, int b) { return std::make_tuple( a + b, (float)(a) / (float)(b) ); }
// PYBIND11_MODULE(モジュール名, モジュール変数名) // 生成物はmy_module_name.pydという名前にしなければならない PYBIND11_MODULE(my_module_name, m) { m.def( "call_in_python", // Python側で呼び出す関数名 &my_calc_func, // C++側の関数名 py::arg("a"), // Python側での引数名(指定省略可) py::arg("b") // Python側での引数名(指定省略可) ); }
import my_module_name val1,val2 = my_module_name.call_in_python(10,2) print(val1) print(val2)
#pragma comment(lib, "python39.lib") #include <pybind11/pybind11.h> namespace py = pybind11;
class MyClass { std::string m_str; int value; public: MyClass(std::string str,int val) : m_str(str),value(val) {} std::string get() { return m_str + " " + std::to_string(value); } void set(std::string str) { m_str = str; } };
std::string version() { return "1.0.0"; }
// PYBIND11_MODULE(モジュール名, モジュール変数名) // 生成物はmy_module_name.pydという名前にしなければならない PYBIND11_MODULE(my_module_name, m) { // クラスを追加 pybind11::class_<MyClass>(m, "MyClassInCPP") // クラス名を指定 PythonからMyClassInCPPとしてアクセス .def(pybind11::init<std::string,int>()) // コンストラクタを指定 .def("get_str", &MyClass::get) // メンバ関数を追加。Pythonからget_strとしてアクセス .def("set_str", &MyClass::set); // 関数を追加 m.def("get_version", &version, "get version function"); }
import my_module_name ss = my_module_name.MyClassInCPP("hello",15) print("get_str " , ss.get_str() ) ss.set_str("string") print("get_str " , ss.get_str() ) print("version:" , my_module_name.get_version() )
CLionでGithub Copilotを使用する。
設定から[プラグイン] → [Marketplace]で Github Copilot を検索。
サードパーティプラグインに関する通知 が出るが、Github公式のもなので安心して同意する。
IDEを再起動する。
再起動後、右下のアイコンをクリックしてLogin to Githubを選択。選択できないなら一度ログアウトする。
Copy and OpenでGithubへ入って完了。動かないようなら一度CLionを再起動する。
Luaに構造体はないがテーブルがあり構造体と同じような書き方ができる。
LuaとC++の通信はLuaのスタックを介して行われるため、Luaのスタックの挙動を知っておく必要がある。
1.lua_getglobalでスクリプト内のテーブルへの参照をスタックへPush
2.lua_getfieldでテーブルへの参照から指定したキーの値をスタックへPush
3.lua_tostringでスタックの先頭の値を読み込んで表示
4.スタックをPopして次の処理に備える
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") // Luaスクリプト static const char* script = R"(
MyLuaTable ={ val_a = "VAL_A", val_b = "VAL_B", val_c = "VAL_C", val_d = "VAL_D", }
)"; int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas); // 文字列で与えてスクリプトを実行する luaL_dostring(luas, script); // テーブルへアクセス lua_getglobal(luas, "MyLuaTable");// MyLuaTableへの参照をスタックの一番上にPush{ // lua_getfieldでテーブルから値を取得 // 第一引数 ... lua_State // 第二引数 ... テーブルのスタック上の位置を現在のスタック位置からの差で表す // 第三引数 ... テーブルのキー /* * スタックの状態 * 4 □ * 3 □ * 2 □ 現在位置==2。 現在位置-1 == 1。従ってlua_getfieldは'1'で参照されているテーブルから'val_a'を取り出し、ここへPushする * 1 ■ MyLuaTableへの参照 lua_getglobalによりPushされている */ lua_getfield(luas, -1, "val_a"); // val_aの値(現在位置からMyLuaTableまでの距離は-1)をスタックの一番上にPush const char* value = lua_tostring(luas, -1); // スタックの一番上の値を取得 printf("value: %s\n", value); lua_pop(luas, 1/*要素を一つPOP*/); // スタックの一番上の要素をPop。これで現在のスタック位置が2に戻る }
{ lua_getfield(luas, -1, "val_b"); // val_bの値(MyLuaTableまでの距離は-1)をスタックの一番上にPush const char* value = lua_tostring(luas, -1); // スタックの一番上の値を取得 printf("value: %s\n", value); lua_pop(luas, 1/*要素を一つPOP*/); // スタックの一番上の要素をPop }
// Luaの終了。全てのメモリ解放 lua_close(luas); }
lua_getglobalとlua_getfieldの挙動がわからなかったので、スタックの状態を確認する。
以下のコードはスクリプトで定義されたテーブルへアクセスするために、各値をひたすらスタックへ積んでいく。
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") // Luaスクリプト static const char* script = R"(
MyLuaTable_1 ={ val_1_a = "VAL_1_A", val_1_b = "VAL_1_B", val_1_c = "VAL_1_C", val_1_d = "VAL_1_D", } MyLuaTable_2 ={ val_2_a = "VAL_2_A", val_2_b = "VAL_2_B", val_2_c = "VAL_2_C", val_2_d = "VAL_2_D", }
)";
// Lua スタックの内容を表示 void print_stack(lua_State* luas) { // スタックのトップの位置を取得 int top = lua_gettop(luas); // 全てのスタックの内容を表示 // Luaのスタックは1から始まる for (int pos = 1; pos <= top; pos++) { int type = lua_type(luas, pos); printf("%d: ", pos); switch (type) { case LUA_TSTRING: printf("'%s'", lua_tostring(luas, pos)); break; case LUA_TBOOLEAN: printf(lua_toboolean(luas, pos) ? "true" : "false"); break; case LUA_TNUMBER: printf("%g", lua_tonumber(luas, pos)); break; case LUA_TNIL: printf("nil"); break; case LUA_TTABLE: printf("table Reference"); break; case LUA_TFUNCTION: printf("function"); break; case LUA_TUSERDATA: printf("userdata"); break; case LUA_TTHREAD: printf("thread"); break; case LUA_TLIGHTUSERDATA: printf("light userdata"); break; default: printf("unknown"); break; } printf("\n"); } }
int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas); // 文字列で与えてスクリプトを実行する luaL_dostring(luas, script); { // テーブルへアクセス lua_getglobal(luas, "MyLuaTable_1");// MyLuaTable_1への参照をスタックの一番上にPush lua_getglobal(luas, "MyLuaTable_2");// MyLuaTable_2への参照をスタックの一番上にPush lua_getfield(luas, -1, "val_2_a"); // val_2_aの値(MyLuaTable_2までの距離は-1)をスタックの一番上にPush lua_getfield(luas, -2, "val_2_b"); // val_2_bの値(MyLuaTable_2までの距離は-2)をスタックの一番上にPush lua_getfield(luas, -3, "val_2_c"); // val_2_aの値(MyLuaTable_2までの距離は-3)をスタックの一番上にPush lua_getfield(luas, -4, "val_2_d"); // val_2_bの値(MyLuaTable_2までの距離は-4)をスタックの一番上にPush // [現在位置-5] にはMyLuaTable_2への参照が積まれている lua_getfield(luas, -6, "val_1_a"); // val_1_aの値(MyLuaTable_1までの距離は-6)をスタックの一番上にPush lua_getfield(luas, -7, "val_1_b"); // val_1_bの値(MyLuaTable_1までの距離は-7)をスタックの一番上にPush lua_getfield(luas, -8, "val_1_c"); // val_1_cの値(MyLuaTable_1までの距離は-8)をスタックの一番上にPush lua_getfield(luas, -9, "val_1_d"); // val_1_dの値(MyLuaTable_1までの距離は-9)をスタックの一番上にPush } print_stack(luas); // スタックの内容を表示 // Luaの終了。全てのメモリ解放 lua_close(luas); }
lua_tostringの第二引数には、スタックの先頭からの距離を指定する。-1は先頭に積まれている値を指す。例えば-4を指定すれば、先頭から4つ目の値へアクセスする。
// テーブルへアクセス lua_getglobal(luas, "MyLuaTable_1");// MyLuaTable_1への参照をスタックの一番上にPush lua_getglobal(luas, "MyLuaTable_2");// MyLuaTable_2への参照をスタックの一番上にPush lua_getfield(luas, -1, "val_2_a"); // val_2_aの値(MyLuaTable_2までの距離は-1)をスタックの一番上にPush lua_getfield(luas, -2, "val_2_b"); // val_2_bの値(MyLuaTable_2までの距離は-2)をスタックの一番上にPush lua_getfield(luas, -3, "val_2_c"); // val_2_aの値(MyLuaTable_2までの距離は-3)をスタックの一番上にPush lua_getfield(luas, -4, "val_2_d"); // val_2_bの値(MyLuaTable_2までの距離は-4)をスタックの一番上にPush // [現在位置-5] にはMyLuaTable_2への参照が積まれている lua_getfield(luas, -6, "val_1_a"); // val_1_aの値(MyLuaTable_1までの距離は-6)をスタックの一番上にPush lua_getfield(luas, -7, "val_1_b"); // val_1_bの値(MyLuaTable_1までの距離は-7)をスタックの一番上にPush lua_getfield(luas, -8, "val_1_c"); // val_1_cの値(MyLuaTable_1までの距離は-8)をスタックの一番上にPush lua_getfield(luas, -9, "val_1_d"); // val_1_dの値(MyLuaTable_1までの距離は-9)をスタックの一番上にPush
///////////////////////////////////////// // 現在位置から -4 の位置にある値を取得 const char* value = lua_tostring(luas, -4); // val_1_a の値を取得 printf("value: %s\n", value);
LuaとC/C++のデータの受け渡しはLuaのスタックを介して行う。
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas);
// lua_pcallで呼び出す。 スタックに積まれた変数を取り出すには ... を使用 luaL_loadstring(luas, R"(
local val1,val2 = ...; print("Received value:", val1,val2);
)");
lua_pushinteger(luas, 10); // 値をスタックに積む lua_pushstring(luas, u8"Hello World"); // 値をスタックに積む。文字列はUTF-8で渡す
lua_pcall(luas, 2, 0, 0); // Luaの終了。全てのメモリ解放 lua_close(luas); }
※このコードを実行するとセキュリティソフトにウィルス認定されるんだが、ヒューリスティックスキャンが誤爆でもしているのか?
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas); // Luaスクリプト // luaL_loadstringによりコンパイルされる int load_status = luaL_loadstring(luas, R"(
function myfunc() print(my_string); print(my_int); end
)"); if (load_status == LUA_OK) { // Luaスクリプトを実行 // スクリプトを実行しているだけなので関数を実行しているわけではない // このスクリプト実行で myfunc()が定義される。 lua_pcall(luas, 0, 0, 0); } lua_pushinteger(luas, 10); // 値をPushする lua_setglobal(luas, "my_int"); // グローバル変数を定義し、値をセット lua_pushstring(luas, "Hello World"); // 値をPushする lua_setglobal(luas, "my_string"); // グローバル変数を定義し、値をセット // 一回目の lua_pcall で定義した myfunc() をスタックへPushする lua_getglobal(luas, "myfunc"); lua_pcall(luas, 0, 0, 0);// myfunc呼び出し // Luaの終了。全てのメモリ解放 lua_close(luas); }
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas); // lua_pcallでコンパイル luaL_loadstring(luas, R"(
global_value = 105
)"); // Luaスクリプトを実行 lua_pcall(luas, 0, 0, 0); lua_getglobal(luas, "global_value"); int gval = lua_tointeger(luas, -1); printf("global_value = %d\n", gval); // Luaの終了。全てのメモリ解放 lua_close(luas); }
LuaはC言語に組み込みやすい言語。
Windows版をmakeするのは面倒なので、ビルド済みバイナリをダウンロードする。
以下URLへ行き、Historyから Release 1とついているものをダウンロードする。
https://luabinaries.sourceforge.net
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas);
// Luaスクリプト const char* script = R"(
for i = 1,5 do print(i) end
)"; // 文字列で与えてスクリプトを実行する luaL_dostring(luas, script); // Luaの終了。全てのメモリ解放 lua_close(luas); }
for i = 1,5 do print(i) end
#include <iostream> #include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas); // myscript.luaを実行する // luaL_dofileでもいい。luaL_dofileはマクロ。 // luaL_dofile(luas, "myscript.lua"); luaL_loadfile(luas, "myscript.lua");// スクリプトファイル読み込み lua_pcall(luas, 0, LUA_MULTRET, 0);// スクリプト実行 // Luaの終了。全てのメモリ解放 lua_close(luas); }
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib") int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas); luaL_dostring(luas, R"(
function calc(x, y) return x + y, x - y, x * y, x / y end
)" ); // Luaの関数を呼び出す // 引数はスタックにプッシュして渡す ///////////////////////////////////////////// lua_getglobal(luas, "calc"); // add 関数をスタックにプッシュ lua_pushnumber(luas, 3); // 第一引数 x に 3 をプッシュ lua_pushnumber(luas, 7); // 第二引数 y に 7 をプッシュ ///////////////////////////////////////////// // 関数呼び出し int ret = lua_pcall( luas, 2, // 引数の数 4, // 戻り値の数 0 // エラーハンドラのインデックス ); ///////////////////////////////////////////// // エラーチェック if (ret != LUA_OK) { const char* err = lua_tostring(luas, -1); printf("error: %s\n", err); return -1; } ///////////////////////////////////////////// // 戻り値を取得 int add = (int)lua_tonumber(luas, -4);// 戻り値1 int sub = (int)lua_tonumber(luas, -3);// 戻り値2 double mul = (double)lua_tonumber(luas, -2);// 戻り値3 double div = (double)lua_tonumber(luas, -1);// 戻り値4 printf("add = %d\n", add); printf("sub = %d\n", sub); printf("mul = %f\n", mul); printf("div = %f\n", div); ///////////////////////////////////////////// // Luaの終了。全てのメモリ解放 lua_close(luas); }
#include <lua.hpp> // lua54.dllが必要 #pragma comment(lib, "lua54.lib")
int ccalc(lua_State* L) { double a = luaL_checknumber(L, 1); // 1番目の引数を取得 double b = luaL_checknumber(L, 2); // 2番目の引数を取得 // 結果をスタックにプッシュ lua_pushnumber(L, a + b); lua_pushnumber(L, a - b); lua_pushnumber(L, a * b); lua_pushnumber(L, a / b); return 4; // 戻り値の数を返す }
int main() { lua_State* luas = luaL_newstate(); // Luaの標準ライブラリを読み込む luaL_openlibs(luas); // C++の関数をLuaに登録 lua_register(luas, "ccalc", ccalc); // LuaからC++の関数を呼び出す luaL_dostring(luas, R"(
add,sub,mul,div = ccalc(3, 7) print("add",add) print("sub",sub) print("sub",mul) print("div",div)
)" ); // Luaの終了。全てのメモリ解放 lua_close(luas); }
過去のParallel::Forの記事がかなりわかりにくかったので再度書く。過去記事:
以下サンプル
int i; int c1[100]; for (i = 0; i < 100; i ++) { c1[i] = i; }
#include "pch.h" using namespace System;
// Parallel::Forでデータを操作する関数を含んだクラス ref struct LoopObject { int *_array; public: LoopObject(int* data) { _array = data; } // データを操作する関数 // Parallel::Forの場合は、intの引数一つである必要がある // 引数 i ループカウンタ for(int i = 0;i<SIZE;i++){ function(i); } のような感じ
void function(int i) { // データを操作 _array[i] = i; } };
int main(array<System::String^>^ args) { // データ const int SIZE = 100; int c1[SIZE]; // Parallel::Forでデータを操作する関数を含んだクラスのインスタンスを生成 LoopObject^ forInstance = gcnew LoopObject(c1);// コンストラクタにデータを渡す // forの範囲を指定 int BEGIN = 0; int END = SIZE; // 並列処理実行 For( 開始Index , 終了Index , 一回分の処理を行う関数オブジェクト ) System::Threading::Tasks::Parallel::For( BEGIN, END, gcnew Action<int>(forInstance, &LoopObject::function) ); // 結果を表示 for (int i = 0; i < SIZE; i++) { System::Console::WriteLine( System::String::Format("mydata[{0}] = {1}", i, c1[i])); } Console::ReadLine(); return 0; }
Action<int>のintはLoopObject::functionの引数がintなので、ループカウンタを与える関数に合わせているのだが、問題はParallel::Forがintしか受け付けていないので、実質Action<int>以外に選択肢がない(Action<size_t>などやってもParallel::Forでビルドエラーになる)。
要は「関数オブジェクトを並列で呼び出す関数」がParallel::Forで、Actionのインスタンスが関数オブジェクトになる。C++でそれっぽいコードを書くと以下になる。
std::sort(begin,end,[](...){...});とやるときと考え方は同じ。
#include <functional> struct LoopObject { int* _array; public: LoopObject(int* data) { _array = data; } // データを操作する関数 // Parallel::Forの場合は、intの引数一つである必要がある // 引数 i ループカウンタ for(int i = 0;i<SIZE;i++){ function(i); } のような感じ void function(int i) { // データを操作 _array[i] = i; } }; namespace Parallel { void For(int start, int end, LoopObject* action) { // 本来はここは並列処理で呼び出すが // 今回はシングルスレッドで実行する for (int i = start; i < end; i++) { action->function(i); } } } int main() { int c1[100]; auto action = new LoopObject(c1); Parallel::For(0, 100, action); // 結果の表示 for (int i = 0; i < 100; i++) { printf("%d\n", c1[i]); } }