「関数電卓」のコマンド版(Windows のデスクトップアプリケーション版)を作成しました。
「関数電卓コマンド版(1) - 非専門的シンギュラリティー研究所」で書いたように計算の部分は「関数電卓」を C++ に変換したものを使っています。
GUI の部分は以下のようになります。計算の部分(「関数電卓」と同様)は長いので省略します。
「stringとwstringの変換 #C++ - Qiita」を参考にしています。
#include <windows.h> #include <string> #include <tchar.h> #include <map> #include "ProgInpStr.h" #include "ProgExp.h" // 使い方 // exit で終了します。 // list で変数の一覧を表示します。 // 変数の定義は、 // 変数名 = 式 // で行います。 // 変数名は、英字またはアンダースコアで始まる必要があります。 // 変数の値を表示するには、 // 変数名 // と入力します。 // 式の評価は、 // 式 // と入力します。 // 式は、四則演算や関数呼び出しなどが可能です。 #define ID_INPUT 1001 #define ID_OUTPUT 1002 // https://qiita.com/tsuchinokoman/items/869a30e02e6ddb5786b3 からコードを引用 std::wstring StringToWstring(const std::string& str); std::string WstringToString(const std::wstring& wstr); class ConOutput { // コンソール風出力クラス private: HWND hOutput; // 出力先のウィンドウハンドル void print(const std::string& str) { // 出力先のウィンドウに文字列を追加 std::wstring wstr = StringToWstring(str); int len = GetWindowTextLength(hOutput); SendMessage(hOutput, EM_SETSEL, len, len); SendMessage(hOutput, EM_REPLACESEL, 0, (LPARAM)wstr.c_str()); } public: ConOutput(HWND hOutput) { // コンストラクタで出力先のウィンドウハンドルを設定 this->hOutput = hOutput; } ConOutput& operator<<(const std::string& str) { // 出力演算子オーバーロード print(str); return *this; } }; HWND hInput, hOutput; WNDPROC oldEditProc = nullptr; using Env = std::map<std::string, ProgExp>; Env variables; bool process_line(const std::wstring& wline) { ConOutput co_cout(hOutput); // 出力オブジェクト ConOutput co_cerr(hOutput); // エラー出力オブジェクト std::string s_endl = "\r\n"; // 改行文字列 std::string line = WstringToString(wline); if (line.find("exit") == 0) { return false; } if (line.find("list") == 0) { // 変数リストの表示 if (variables.empty()) { co_cout << "No variables defined." << s_endl; } else { co_cout << "Defined variables:" << s_endl; for (const auto& var : variables) { co_cout << var.first << " = " << var.second.mprint(0).GetString() << s_endl; } } return true; } ProgInpStr inputStr(line); ProgExp exp = inputStr.get_asn(); if (exp.is_error()) { co_cerr << "Error: " << exp.get_error_message().GetString() << s_endl; return true; } if (exp.getType() == "var") { // 変数の定義を表示 std::string varName = exp.getName().GetString(); if (isalpha(varName[0]) || varName[0] == '_') { if (variables.find(varName) == variables.end()) { co_cerr << "Error: Variable '" << varName << "' is not defined." << s_endl; return true; } // 変数の値を表示 ProgExp pointExp = variables[varName]; co_cout << varName << " = " << pointExp.mprint(0).GetString() << s_endl; return true; } } else if (exp.getType() == "=") { // 変数を定義 std::string varName = exp.get_left().getName().GetString(); ProgExp right_exp = exp.get_right(); variables[varName] = right_exp; return true; } // 式の評価 ProgDefMap vardefs; for (const auto& var : variables) { vardefs.Add(var.first, var.second); } ExpNum result = exp.value(vardefs); if (result.is_error()) { co_cerr << "Error: " << result.get_error_message().GetString() << s_endl; } else { co_cout << "Result: " << result.Print().GetString() << s_endl; } return true; } const int MAX_INPUT_LENGTH = 1000; std::wstring prompt = L"> "; // サブクラス用プロシージャ LRESULT CALLBACK EditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg == WM_KEYDOWN && wParam == VK_RETURN) { wchar_t buffer[MAX_INPUT_LENGTH]; DWORD start, end; SendMessage(hInput, EM_GETSEL, (WPARAM)&start, (LPARAM)&end); // カーソル位置取得 int currentLine = (int)SendMessage(hInput, EM_LINEFROMCHAR, (WPARAM)start, 0); // 最初の WORD に、バッファのサイズを入れる(重要!) *((WORD*)buffer) = sizeof(buffer) / sizeof(TCHAR); // EM_GETLINE を使って指定行を取得 int copied = (int)SendMessage(hInput, EM_GETLINE, (WPARAM)currentLine, (LPARAM)buffer); // buffer に copied 文字分のテキストが格納される(終端の \0 は自動で付かないので注意) buffer[copied] = _T('\0'); // 明示的に終端すること int lineCount = (int)SendMessage(hInput, EM_GETLINECOUNT, 0, 0); if (currentLine < lineCount - 1) { // 次の行がある場合は、最後に移動する int len = GetWindowTextLength(hInput); SendMessage(hInput, EM_SETSEL, len, len); } else { // 最後の行の場合は、プロンプトを表示する int len = GetWindowTextLength(hInput); SendMessage(hInput, EM_SETSEL, len, len); SendMessage(hInput, EM_REPLACESEL, 0, (LPARAM)(L"\r\n" + prompt).c_str()); } // 行の処理 std::wstring line = buffer; line = line.substr(prompt.size()); // プロンプト部分を除去 if(!process_line(line)) { // "exit" コマンドが入力された場合、アプリケーションを終了 PostQuitMessage(0); } return 0; // エンターキーの既定動作(ビープ音など)を抑制 } if (msg == WM_CHAR && wParam == VK_RETURN) { return 0; // エンターキーの既定動作(ビープ音など)を抑制 } return CallWindowProc(oldEditProc, hwnd, msg, wParam, lParam); } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_CREATE: // 出力用(読み取り専用、複数行) hOutput = CreateWindow(L"EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_READONLY | WS_VSCROLL, 10, 10, 460, 300, hwnd, (HMENU)ID_OUTPUT, NULL, NULL); // 入力用 hInput = CreateWindow(L"EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | WS_VSCROLL, 10, 320, 460, 300, hwnd, (HMENU)ID_INPUT, NULL, NULL); SetWindowText(hInput, prompt.c_str()); { int len = GetWindowTextLength(hInput); SendMessage(hInput, EM_SETSEL, len, len); } SetFocus(hInput); // サブクラス化 oldEditProc = (WNDPROC)SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR)EditProc); break; case WM_COMMAND: if (LOWORD(wParam) == ID_INPUT && HIWORD(wParam) == EN_CHANGE) { // Enterキーを検出するためにEN_CHANGEではなくWM_KEYDOWNを使う } break; case WM_KEYDOWN: break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) { const wchar_t CLASS_NAME[] = L"MyConsoleWindow"; WNDCLASS wc = {}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); RegisterClass(&wc); HWND hwnd = CreateWindowEx( 0, CLASS_NAME, L"GUI Console", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 700, NULL, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); MSG msg = {}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } // https://qiita.com/tsuchinokoman/items/869a30e02e6ddb5786b3 からコードを引用 std::wstring StringToWstring(const std::string& str) { std::wstring ret; //一度目の呼び出しは文字列数を知るため auto result = MultiByteToWideChar(CP_UTF8, 0, str.c_str(),//入力文字列 str.length(), nullptr, 0); ret.resize(result);//確保する //二度目の呼び出しは変換 result = MultiByteToWideChar(CP_UTF8, 0, str.c_str(),//入力文字列 str.length(), ret.data(), ret.size()); return ret; } // https://qiita.com/tsuchinokoman/items/869a30e02e6ddb5786b3 からコードを引用 std::string WstringToString(const std::wstring& wstr) { std::string ret; //一度目の呼び出しは文字列数を知るため auto result = WideCharToMultiByte( CP_ACP, 0, wstr.c_str(),//入力文字列 wstr.length(), nullptr, 0, nullptr, nullptr); ret.resize(result);//確保する //二度目の呼び出しは変換 result = WideCharToMultiByte( CP_ACP, 0, wstr.c_str(),//入力文字列 wstr.length(), ret.data(), ret.size(), nullptr, nullptr); return ret; }


