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]); } }
Bold、Italicなどが別々のファイルに分かれている場合、各ファイルのfamily_nameがフォントの種類を特定する指標となり、同じフォントのfamily_nameは共通しているので、family_nameごとに纏めた辞書を作れば、特定のfamillyに対してスタイルを選択することができる。
#include <iostream> #include <vector> #include <fstream> #include <filesystem> #include <unordered_map> #include <ft2build.h> #include FT_FREETYPE_H #pragma warning(disable:4996) #ifdef _DEBUG #pragma comment(lib,"freetyped.lib") #else #pragma comment(lib,"freetype.lib") #endif //! @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); }
bool render(FT_Face face, FT_ULong character) { //文字コード指定 FT_Error error = FT_Select_Charmap( face, // target face object FT_ENCODING_UNICODE // エンコード指定 ); if (error == FT_Err_Unknown_File_Format) return false; else if (error) return false; //この二つの値でフォントサイズ調整 FT_F26Dot6 fontsize = 16 * 64*2; FT_UInt CHAR_RESOLUTION = 300; error = FT_Set_Char_Size( face, // handle to face object 0, // char_width in 1/64th of points fontsize, // char_height in 1/64th of points CHAR_RESOLUTION, // horizontal device resolution CHAR_RESOLUTION); // vertical device resolution FT_UInt char_index = FT_Get_Char_Index(face, character); // グリフ(字の形状)読込 error = FT_Load_Glyph(face, char_index, FT_LOAD_RENDER); if (error) return -1; // ignore errors // 文字を画像化 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); int Width = face->glyph->bitmap.width; int Height = face->glyph->bitmap.rows; char filename[1024]; sprintf(filename, "C:\\test\\freetypetest_%s.pbm", face->style_name); pbmP1_Write(// ファイル保存 filename, Width, Height, face->glyph->bitmap.buffer ); }
// filesystemでフォントファイル名一覧を取得する std::vector<std::string> getFontFileList(const std::string& fontdir) { namespace fs = std::filesystem; std::vector<std::string> fontList; if (!fs::exists(fontdir) || !fs::is_directory(fontdir)) { std::cerr << "Directory does not exist: " << fontdir << std::endl; return fontList; } // Iterate through the directory for (const auto& entry : fs::directory_iterator(fontdir)) { if (entry.is_regular_file()) { auto path = entry.path(); fontList.push_back(path.string()); } } return fontList; }
// フォントファイルからフォントを取得し、フォントファミリでグループ化する std::unordered_map<std::string, std::vector<std::pair<std::string, FT_Face>> > getFonts(FT_Library library,const std::vector<std::string>& flist) { std::unordered_map< std::string, std::vector< std::pair<std::string,FT_Face> > > fontmap; for (const auto& file : flist) { FT_Face face; // handle to face object // フォントファイル読み込み FT_Error error = FT_New_Face( library, file.c_str(), 0, &face ); if(error) continue; fontmap[face->family_name].push_back({ file,face });// ファミリ名をキーとしてフェイスを保存 } return fontmap; }
int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; std::vector<std::string> flist = getFontFileList("C:\\Windows\\Fonts\\"); // フォントファミリでグループ化したフォントリストを取得 auto famillies = getFonts(library, flist); // arialフォントのファミリ名で取得 const auto& arial = famillies["Arial"]; for( const auto& [file,face] : arial){ std::cout << face->style_name << std::endl; render(face, wchar_t(L'A')); } // フォント解放 for (auto& familly : famillies) { for (std::pair<std::string,FT_Face>& tf : familly.second) { FT_Face face = tf.second; FT_Done_Face(face); } } FT_Done_FreeType(library); }

同じフォントの太字、斜体でも、別のファイルに分かれていることがかなりある。フォントフォルダから別フォルダに移すとファイル名がわかる。

#include <iostream> #include <vector> #include <fstream> #include <ft2build.h> #include FT_FREETYPE_H #pragma warning(disable:4996) #ifdef _DEBUG #pragma comment(lib,"freetyped.lib") #else #pragma comment(lib,"freetype.lib") #endif //! @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); }
bool render(FT_Library library,const std::string fname, FT_ULong character,int face_index) { FT_Face face; // handle to face object // フォントファイル読み込み FT_Error error = FT_New_Face( library, fname.c_str(), 0, &face ); //文字コード指定 error = FT_Select_Charmap( face, // target face object FT_ENCODING_UNICODE // エンコード指定 ); if (error == FT_Err_Unknown_File_Format) return false; else if (error) return false; //この二つの値でフォントサイズ調整 FT_F26Dot6 fontsize = 16 * 64; FT_UInt CHAR_RESOLUTION = 300; error = FT_Set_Char_Size( face, // handle to face object 0, // char_width in 1/64th of points fontsize, // char_height in 1/64th of points CHAR_RESOLUTION, // horizontal device resolution CHAR_RESOLUTION); // vertical device resolution FT_UInt char_index = FT_Get_Char_Index(face, character); // グリフ(字の形状)読込 error = FT_Load_Glyph(face, char_index, FT_LOAD_RENDER); if (error) return -1; // ignore errors // 文字を画像化 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); int Width = face->glyph->bitmap.width; int Height = face->glyph->bitmap.rows; char filename[1024]; sprintf(filename, "C:\\test\\freetypetest_%d.pbm", face_index); pbmP1_Write(// ファイル保存 filename, Width, Height, face->glyph->bitmap.buffer ); // FreeType2の解放 FT_Done_Face(face); }
int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; // 文字の取得 FT_ULong character = wchar_t(L'A'); render(library, "C:\\Windows\\Fonts\\arial.ttf", character, 0); // Regular render(library, "C:\\Windows\\Fonts\\ariali.ttf", character,1); // Italic render(library, "C:\\Windows\\Fonts\\arialbd.ttf",character,2); // Bold render(library, "C:\\Windows\\Fonts\\arialnbi.ttf", character, 3); // Bold Italic FT_Done_FreeType(library); }

最近のVC++(2022とか)で、文字列を選択した状態で、>を入力すると、選択した内容が <選択範囲> の形で囲まれてしまう。"でも同様のことが起こる。HTMLタグとかtemplate引数とかには便利かもしれないが、選択中に文字を入力したら置換してほしい。特に、時々やるのが、メンバ関数を間違ったときに書き換えようとして状況が悪化するため、この挙動に年単位で悩まされていた。

「自動サラウンドモード」を「なし」に設定する。

メンバの修正は、VC++では、ctrl+spaceでメンバの候補リストを出すことができる。
単語だけ選択→Backspace→ctrl+spaceをするのが正当な修正方法。
前回C++で実装したクイックソートで、二つの配列を同時にクイックソートする。
#include <iostream> #include <vector>
// 配列の分割を行う関数(ピボットの位置を変更しない) template<typename Container, class QElem> size_t partition(Container& arr, size_t low, size_t high) { size_t mid = low + (high - low) / 2; // 中央の要素をピボットとして選択 QElem pivot(arr, mid); // ピボットの値を取得 size_t i = low; size_t j = high; while (true) { // ピボットより小さい要素を左側に移動 while (QElem::Lesser(arr,i,pivot) ){ i++; } // ピボットより大きい要素を右側に移動 while (QElem::Greater(arr,j,pivot) ) { j--; } // 左右の走査が交差した場合に分割終了 if (i >= j) { return j; } // 交差していない場合、要素を交換 QElem::Swap(arr, i, j); i++; j--; } }
// クイックソートを行う関数 template<typename Container, class QElem> void quick_sort(Container& arr, size_t low, size_t high) { if (low < high) { size_t pi = partition<Container,QElem>(arr, low, high); if (pi > low) quick_sort<Container, QElem>(arr, low, pi);// ピボットの左側をソート quick_sort<Container, QElem>(arr, pi + 1, high);// ピボットの右側をソート } }
// 二つの配列を一つにまとめて渡すための構造体
struct Vector2Reference { std::vector<float>& value; std::vector<std::string>& label; Vector2Reference(std::vector<float>& v, std::vector<std::string>& l) : value(v), label(l) {} size_t size() const { return value.size(); } };
struct QsortElement { float value; // Pivotの値を保存するためのオブジェクト用のコンストラクタ QsortElement(const Vector2Reference& arr, size_t index) { value = arr.value[index]; } // 要素の交換関数 static void Swap(Vector2Reference& arr, size_t i, size_t j) { std::swap(arr.value[i], arr.value[j]); std::swap(arr.label[i], arr.label[j]); } // 比較関数 static bool Lesser(Vector2Reference& arr, size_t i, const QsortElement& qe) { return arr.value[i] < qe.value; } // 比較関数 static bool Greater(Vector2Reference& arr, size_t i, const QsortElement& qe) { return arr.value[i] > qe.value; } };
int main() { std::vector<float> value; std::vector<std::string> label; value.push_back(5.0f); label.push_back("e"); value.push_back(1.0f); label.push_back("a"); value.push_back(3.0f); label.push_back("c"); value.push_back(4.0f); label.push_back("d"); value.push_back(2.0f); label.push_back("b"); Vector2Reference arr2(value, label); std::cout << "ソート前の配列: " << std::endl; for (size_t i = 0; i < arr2.size(); i++) { std::cout << arr2.value[i] << " " << arr2.label[i] << std::endl; } quick_sort< Vector2Reference, QsortElement>(arr2, (size_t)0, arr2.size()-1); std::cout << "ソート後の配列: " << std::endl; for (size_t i = 0; i < arr2.size(); i++) { std::cout << arr2.value[i] << " " << arr2.label[i] << std::endl; } return 0; }
C++でクイックソートを実装する。ただし、諸事情によりピボット保存、比較演算、スワップ関数を外部で実装し、テンプレートで渡す形式にする(QElem)。
#include <iostream> #include <vector>
// 配列の分割を行う関数 template<typename Container, class QElem> size_t partition(Container& arr, size_t low, size_t high) { size_t mid = low + (high - low) / 2; // 中央の要素をピボットとして選択 QElem pivot(arr, mid); // ピボットの値を取得 size_t i = low; size_t j = high; while (true) { // ピボットより小さい要素を左側に移動 while (QElem::Lesser(arr,i,pivot) ){ i++; } // ピボットより大きい要素を右側に移動 while (QElem::Greater(arr,j,pivot) ) { j--; } // 左右の走査が交差した場合に分割終了 if (i >= j) { return j; } // 交差していない場合、要素を交換 QElem::Swap(arr, i, j); i++; j--; } }
// クイックソートを行う関数 template<typename Container, class QElem> void quick_sort(Container& arr, size_t low, size_t high) { if (low < high) { size_t pi = partition<Container,QElem>(arr, low, high); if (pi > low) quick_sort<Container, QElem>(arr, low, pi);// ピボットの左側をソート quick_sort<Container, QElem>(arr, pi + 1, high);// ピボットの右側をソート } }
struct QsortElement { float value; // Pivotの値を保存するためのオブジェクト用のコンストラクタ QsortElement(const std::vector<float>& arr, size_t index) { value = arr[index]; } // 要素の交換関数 static void Swap(std::vector<float>& arr, size_t i, size_t j) { std::swap(arr[i], arr[j]); } // 比較関数 static bool Lesser(std::vector<float>& arr, size_t i, const QsortElement& qe) { return arr[i] < qe.value; } // 比較関数 static bool Greater(std::vector<float>& arr, size_t i, const QsortElement& qe) { return arr[i] > qe.value; } };
int main() { std::vector<float> arr = {5.2f, 1.8f, 3.2f, 4.3f, 2.7f}; std::cout << "ソート前の配列: " << std::endl; for (size_t i = 0; i < arr.size(); i++) { std::cout << arr[i] << " " << std::endl; } quick_sort< std::vector<float>, QsortElement>(arr, (size_t)0, arr.size()-1); std::cout << "ソート後の配列: " << std::endl; for (size_t i = 0; i < arr.size(); i++) { std::cout << arr[i] << " " << std::endl; } return 0; }