以下からダウンロードし、single_include/nlohmann を適切な場所に展開する。ヘッダオンリーなのでincludeパスを通すだけでよい。
https://github.com/nlohmann/json
前回OpenAIから取得したjsonを整形して表示してみる。nlohmann:json::parseでパースし、dumpでstd::stringに変換できる。
#include <iostream> #include <nlohmann/json.hpp> int main() { // json形式の文字列 std::string res = R"({"id":"cmpl-7NcCYUs1IuINlSSc8Km6ulUTjrakG","object":"text_completion","created":1685862746,"model":"text-davinci-003","choices":[{"text":"\n\nKonnichiwa, sekai.","index":0,"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":16,"completion_tokens":11,"total_tokens":27}} )"; // パース nlohmann::json j = nlohmann::json::parse(res); // 4はインデント(スペース)の個数 std::cout << j.dump(4); }
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"text": "\n\nKonnichiwa, sekai."
}
],
"created": 1685862746,
"id": "cmpl-7NcCYUs1IuINlSSc8Km6ulUTjrakG",
"model": "text-davinci-003",
"object": "text_completion",
"usage": {
"completion_tokens": 11,
"prompt_tokens": 16,
"total_tokens": 27
}
}
#include <iostream> #include <nlohmann/json.hpp> int main() { // json形式の文字列 std::string res = R"({"id":"cmpl-7NcCYUs1IuINlSSc8Km6ulUTjrakG","object":"text_completion","created":1685862746,"model":"text-davinci-003","choices":[{"text":"\n\nKonnichiwa, sekai.","index":0,"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":16,"completion_tokens":11,"total_tokens":27}} )"; // パース nlohmann::json j = nlohmann::json::parse(res); // std::cout << j["choices"].type_name(); // 「array」なのでこれは配列 // std::cout << j["choices"].size(); // 配列の要素数。この例では「1」となる。 assert(j["choices"].type_name()=="array"); // j["choices"]は要素数1の配列。"text"というキーのアイテムは0番に入っている std::string text = j["choices"][0]["text"]; // 4はインデント(スペース)の個数 std::cout << text << std::endl; }
#include <iostream> #include <fstream> #include <string> // libcurlを使うために必要 #define CURL_STATICLIB #include <curl/curl.h> // ssl対応したlibcurlに必要 #pragma comment(lib, "CRYPT32.LIB") #pragma comment(lib, "wldap32.lib" ) #pragma comment(lib, "Ws2_32.lib") // ssl対応したlibcurlに必要 #pragma comment(lib,"libcurl.lib") #pragma comment(lib,"libssl.lib") #pragma comment(lib,"libcrypto.lib") //////////////////////////////// // jsonを扱うために必要 #include <nlohmann/json.hpp> std::size_t WriteCallback( const char* in, std::size_t size, std::size_t num, std::string* out) { const std::size_t totalBytes(size * num); out->append(in, totalBytes); return totalBytes; } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// int main() { //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // まず、OpenAI APIへアクセスするURLと、送信するデータを定義する const std::string url = "https://api.openai.com/v1/completions"; // 送信するデータをjsonで記述。 nlohmann::json json_data; json_data["model"] = u8"text-davinci-003"; // モデル名 json_data["prompt"] = u8"'Hello, world'を日本語に訳してください。"; json_data["max_tokens"] = 60; const std::string data = json_data.dump(); //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // ここからlibcurlの処理開始 CURL* curl = curl_easy_init(); if (!curl) { std::cerr << "Error: Failed to initialize libcurl." << std::endl; return false; } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // httpsの場合は、以下の設定が必要 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1); curl_easy_setopt(curl, CURLOPT_CAINFO, R"(.\cacert.pem)"); curl_easy_setopt(curl, CURLOPT_CAPATH, R"(.\cacert.pem)"); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // サーバー側からの返答を受け取る関数と受け取ったデータを保存する変数を渡す std::string response; curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // OpenAI APIへのアクセスをするURLを設定 CURLcode res = curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); if (res != CURLE_OK) { fprintf(stderr, "curl_easy_setopt() failed: %s\n", curl_easy_strerror(res)); return 1; } //sk-ABCDEFGHIJK1234567890 はAPIキー。これを自分のものに変える。 curl_slist* hs = NULL; hs = curl_slist_append(hs, "Content-Type: application/json"); hs = curl_slist_append(hs, "Authorization: Bearer sk-ABCDEFGHIJK1234567890"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // 通信の実行 res = curl_easy_perform(curl); if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } else { // エラーの場合は、response_codeが200以外になる long response_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); if (response_code != 200) { std::wcout << "ERROR" << std::endl; } else { std::wcout << "SUCCESS" << std::endl; } // 成功の場合、responseには結果が入る。 // エラーの場合、responseにはエラーの原因などが入る。 std::cout << "HTTP Response code: " << response_code << std::endl; std::cout << "Response: " << response << std::endl; // OpenAIの返答を取得して、答えの部分だけを抽出する nlohmann::json jout = nlohmann::json::parse(response); std::string out = jout["choices"][0]["text"]; // utf8で受け取ったデータをファイルへ保存 std::ofstream ofs("response.txt"); ofs << out; ofs.close(); } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// curl_slist_free_all(hs); curl_easy_cleanup(curl); return 0; }
SSLに対応したlibcurlで、OpenAIへリクエストを送る。
#include <iostream> #include <fstream> #include <string> // libcurlを使うために必要 #define CURL_STATICLIB #include <curl/curl.h> // ssl対応したlibcurlに必要 #pragma comment(lib, "CRYPT32.LIB") #pragma comment(lib, "wldap32.lib" ) #pragma comment(lib, "Ws2_32.lib") // ssl対応したlibcurlに必要 #pragma comment(lib,"libcurl.lib") #pragma comment(lib,"libssl.lib") #pragma comment(lib,"libcrypto.lib") std::size_t WriteCallback( const char* in, std::size_t size, std::size_t num, std::string* out) { const std::size_t totalBytes(size * num); out->append(in, totalBytes); return totalBytes; } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// int main() { //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // まず、OpenAI APIへアクセスするURLと、送信するデータを定義する // text-davinci-003 がモデル名。 const std::string url = "https://api.openai.com/v1/engines/text-davinci-003/completions"; // 送信するデータをjsonで記述。promptに指示を入れる。 const std::string data = R"( { "prompt":"Translate the following English text to Japanese Romaji: 'Hello, world'", "max_tokens":60 } )"; //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // ここからlibcurlの処理開始 CURL* curl = curl_easy_init(); if (!curl) { std::cerr << "Error: Failed to initialize libcurl." << std::endl; return false; } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // httpsの場合は、以下の設定が必要 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1); curl_easy_setopt(curl, CURLOPT_CAINFO, R"(.\cacert.pem)"); curl_easy_setopt(curl, CURLOPT_CAPATH, R"(.\cacert.pem)"); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L); //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // サーバー側からの返答を受け取る関数と受け取ったデータを保存する変数を渡す std::string response; curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // OpenAI APIへのアクセスをするURLを設定 CURLcode res = curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); if (res != CURLE_OK) { fprintf(stderr, "curl_easy_setopt() failed: %s\n", curl_easy_strerror(res)); return 1; } // sk-ABCDEFGHIJK1234567890 はAPIキー。これを自分のものに変える。 curl_slist* hs = NULL; hs = curl_slist_append(hs, "Content-Type: application/json"); hs = curl_slist_append(hs, "Authorization: Bearer sk-ABCDEFGHIJK1234567890"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // 通信の実行 res = curl_easy_perform(curl); if (res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } else { // エラーの場合は、response_codeが200以外になる long response_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); if (response_code != 200) { std::wcout << "ERROR" << std::endl; } else { std::wcout << "SUCCESS" << std::endl; } // 成功の場合、responseには結果が入る。 // エラーの場合、responseにはエラーの原因などが入る。 std::cout << "HTTP Response code: " << response_code << std::endl; std::cout << "Response: " << response << std::endl; // utf8で受け取ったデータを、Shift-JISに変換して保存する std::ofstream ofs("response.txt"); ofs << response; ofs.close(); } //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// curl_slist_free_all(hs); curl_easy_cleanup(curl); return 0; }
ローマ字と指定しているので、Konnichiwa, sekai という返答が返ってきている。
前回OpenSSLをビルドしてlibssl.libとlibcrypto.libを作ったので、それらをリンクしたlibcurl.libをビルドする。
前々回のlibcurlのビルドで、プロジェクト設定を以下のように変更する。
まずLIB Release - LIB OpenSSL を選択。
libssl.lib ; libcrypto.libを依存ファイルとして設定。それらがあるライブラリディレクトリを指定。
opensslのincludeディレクトリを指定。
以上でlibcurlをビルドできたので、これを使ったプログラムをビルドしてみる。
curlのlibファイルのディレクトリに加え、opensslのlibファイルのディレクトリも追加してビルドする。
以下の二つを追加ライブラリディレクトリに設定
以下から、cacert.pemをダウンロードして、カレントディレクトリに配置する。
https://curl.se/docs/caextract.html
#include <iostream> #define CURL_STATICLIB #include<curl/curl.h> // OS提供 #pragma comment(lib, "CRYPT32.LIB") #pragma comment(lib, "wldap32.lib" ) #pragma comment(lib, "Ws2_32.lib") // 作成したlibcurl #pragma comment(lib,"libcurl.lib") // 作成したopenssl #pragma comment(lib,"libssl.lib") #pragma comment(lib,"libcrypto.lib") size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); return size * nmemb; } bool PerformHttpsRequest(const std::string& url, std::string& response) { CURL* curl; CURLcode res; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (!curl) { std::cerr << "Error: Failed to initialize libcurl." << std::endl; return false; } curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); /// ///////// // 相手がhttpsの場合、以下五行を入れないと SSL peer certificate or SSH remote key was not OK というエラーが出る。
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1); curl_easy_setopt(curl, CURLOPT_CAINFO, R"(.\cacert.pem)"); curl_easy_setopt(curl, CURLOPT_CAPATH, R"(.\cacert.pem)"); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "Error: curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
curl_easy_cleanup(curl);
curl_global_cleanup();
return false;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
return true;
}
int main() {
std::string url = "https://suzulang.com/";
std::string response;
if (PerformHttpsRequest(url, response)) {
std::cout << "Response from " << url << ":" << std::endl;
std::cout << response << std::endl;
}
return 0;
}
環境を汚さないことをコンセプトに、Perl+NASM+nmakeでOpenSSLをビルドする。
単体の記事として成立するのでlibcurlは次回。
まず、nasmの公式へ行き、
Stableから、
Index of /pub/nasm/releasebuilds/2.16.01/win64
まで潜り、zipをダウンロード・展開する。
nasm-2.16.01-win64.zip
https://strawberryperl.com/releases.html
へ行き、Portable 64bit版をダウンロードする。
strawberry-perl-5.32.1.1-64bit-portable.zip
https://github.com/openssl/openssl
へ行き、以下をダウンロード
openssl-master.zip
今回は、上記を展開し、以下のように配置する。
D:\myDevelop\oss\
├─src\
│ ├─openssl-install\ ... ビルドした結果を格納するためのディレクトリ
│ └─openssl-master\ ... GitHubからダウンロードしたソースコードのディレクトリ
└─tool\
├─nasm-2.16.01\
└─strawberry-perl-5.32.1.1-64bit-portable\
1.x64 Native Tools Command Prompt for VS 2019を起動する
今回、x64 Native ToolsのプロンプトがC:\で、ビルド作業をD:\で行い、コマンドプロンプトを使用するので、先にD:\へ移動しておく
>d:\
2.perlのコンソールを起動
プロンプトから、portableshell.batを実行
>cd D:\myDevelop\oss\tool\strawberry-perl-5.32.1.1-64bit-portable
>portableshell.bat
これで、x64 NativeToolsが使えるコンソール上でperlの設定済が適用される。
2.nasmをパスへ追加
このコンソールからnasmを使えるようにパスを追加する。
>set PATH=D:\myDevelop\oss\tool\nasm-2.16.01;%PATH%
3.カレントディレクトリをopensslのトップへ移動
>cd D:\myDevelop\oss\src\openssl-master
4.ビルド開始
>perl Configure VC-WIN64A --prefix=%CD%\..\openssl-install --openssldir=%CD%\ssl no-shared
>nmake
>nmake install
libcurlにリンクしてhttpsに対応する。
httpsの場合はopenssl設定が必要になるのでまた後。httpプロトコルのみであればビルドもプログラムもかなり簡単。しかしhttpsのアドレス入れるとUnsupported protocolとか言われる。
公式からcurl-8.0.1.zipをダウンロードし展開する。
展開し、VC++のソリューションが入っているディレクトリを探す。
curl-8.0.1\projects\README.md を読むと、以下のバージョン対応表があってわかりやすい。
- VC10 (Visual Studio 2010 Version 10.0)
- VC11 (Visual Studio 2012 Version 11.0)
- VC12 (Visual Studio 2013 Version 12.0)
- VC14 (Visual Studio 2015 Version 14.0)
- VC14.10 (Visual Studio 2017 Version 15.0)
- VC14.30 (Visual Studio 2022 Version 17.0)
今回使ったのはVC++2019なので、VC14.10を開き、ソリューションをアップデートすることにする。以下を開く。
ソリューション構成を LIB Releaseに設定。
libcurlをビルド。
curl-8.0.1\build\Win64\VC14.10\LIB Release\libcurl.lib が出来上がる。
curl-8.0.1 ├─build\ │ └─Win64\ │ └─VC14.10\ │ └─LIB Release\ │ └─lib\ ... libcurl.lib の出力先 ├─CMake\ ├─docs\ ├─include\ │ └─curl\ ├─lib\ ├─m4\ ├─packages\ ├─plan9\ ├─projects\ │ └─Windows\ │ ├─VC10\ │ ├─VC11\ │ ├─VC12\ │ ├─VC14\ │ ├─VC14.10\ ... ここに.slnが入っている │ └─VC14.30\ ├─scripts\ ├─src\ ├─tests\ └─winbuild\
#include <iostream> #define CURL_STATICLIB #include<curl/curl.h> // OS標準 #pragma comment(lib, "CRYPT32.LIB") #pragma comment(lib, "wldap32.lib" ) #pragma comment(lib, "Ws2_32.lib") // 作成したlibcurl #pragma comment(lib,"libcurl.lib") // 参考 // https://gist.github.com/alghanmi/c5d7b761b2c9ab199157
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); return size * nmemb; }
int main(void) { CURL* curl; CURLcode res; std::string readBuffer; // CURL の初期化 curl = curl_easy_init(); if (curl) { // 読みたいURLを指定 curl_easy_setopt(curl, CURLOPT_URL, "http://www.google.com/"); // データを受け取るためのコールバック関数を指定 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); // データを受け取るためのコールバック関数「WriteCallback」に渡す、データの格納先 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); // 実際にHTTPリクエストを送信。 // 実際にWriteCallbackが呼ばれるのはここ。 res = curl_easy_perform(curl); if (res != CURLE_OK) { std::cerr << "curl_easy_perform failed: " << curl_easy_strerror(res) << std::endl; curl_easy_cleanup(curl); return 1; } // CURL の終了処理 curl_easy_cleanup(curl); // 結果の表示 std::cout << readBuffer << std::endl; } return 0; }
まず、cargoで--libを指定してプロジェクトを作成する
Cargo.tomlに以下の設定を追加:
なお、これがdllではなくstatic libraryのみの場合は、以下のように設定する。
Rust側では以下のような関数を用意する。
#[no_mangle] は、関数のオーバーロードなどのためにコンパイラが勝手に関数名を変更するのを防ぐために指定。
extern "C" は、関数の呼び出し規約をC言語にするための設定。ほかにも"stdcall"など色々用意されている。
#[no_mangle] pub extern "C" fn add(a: i32, b: i32) -> i32 { a + b }
cargo build --releaseでビルドする。
この時、myhello.dllができるのだが、libのほうがmyhello.dll.lib になってしまう。今のところ修正する方法がないらしいので、気になるなら手動で修正する。
C++から呼び出し。extern "C"でC++のマングル回避。
// 関数宣言。この関数はRust側にある。 // 本当は.hppファイルなどに定義するべきだが // 今はここに書いておく。 extern "C" { int add(int a, int b); } #pragma comment(lib,"myhello.dll.lib") #include <iostream> int main() { // 関数呼び出し std::cout << add(3,5) << std::endl; }
mesonはCMakeの代替と言われている。VC++の環境ではPython + ninja +VC++が必要。ただしconda install mesonするとninjaも一緒に入るので実質VC++の設定だけでいい。
環境を混ぜないために以下をして仮想環境mesonenvを作成し、その中で動作させる。必須ではない。
conda create -n mesonenv python=3.11
conda activate mesonenv
conda install meson
VC++が動くように、以下で環境設定を行う。(VC++2022)
まず、testの中にmain.cpp とmeson.buildを作成する。
d:\myDevelop\test ├─main.cpp ... ソースファイル ├─meson.build ... 設定ファイル
meson.buildを以下のように記述。
project('MyProject', 'CPP')
executable('myout', ['main.cpp'])
testをカレントれディレクトリとし、以下を実行。
これで、buildの中にmyout.exeが作成される。
d:\myDevelop\test
│ main.cpp
│ meson.build
│
└─build
│ myout.exe
│
├─meson-info
│
├─meson-logs
│
├─meson-private
│
└─myout.exe.p
zip版Pythonをインストールすると、OS側の環境を一切汚さず完全に独立した環境を作れる。
https://www.python.org/downloads/windows/
もちろんwindowsの環境変数を直接書き換えてもいいが、それをやるとわざわざzip版を導入している意味が薄れるので、起動用のバッチファイルを作成してそれを叩いて環境を起動するような使い方にする。
SET PATH=D:\myDevelop\mypython;%PATH% start cmd.exe
このままでは、exit()で終了できないなど問題があるので、._pthファイルを変更する。ファイル名はバージョンによって違う。今回の場合はpython310._pthを編集する。
import siteの先頭に # がついているのでそれを外す。
python310.zip . # Uncomment to run site.main() automatically import site
以下から、pipをインストールするためのスクリプトをダウンロード
https://pip.pypa.io/en/stable/installation/#get-pip-py
スクリプトを実行
このスクリプトを実行すると、以下のようなWARNINGが出る。
意訳:
pipはD:\myDevelop\mypython\Scriptsにインストールされましたがパスが通ってないので自分で通してください。
WARNINGに従い起動用のバッチファイルにScriptsへのパスも追加する。
SET PATH=D:\myDevelop\mypython;%PATH% SET PATH=D:\myDevelop\mypython\Scripts;%PATH% start cmd.exe
ninjaはビルドシステムの一つで、makeの仲間。高速なのが取り柄らしい。makeはmakefileを作成してビルドするのに対して、ninjaは.ninjaファイルを作成してビルドする。ただし、.ninjaはcmakeで作ることを想定している。
ninjaは以下URLから環境にあったものをダウンロード、展開する。今回はwindowsなのでninja-win.zipをダウンロードする。
https://github.com/ninja-build/ninja/releases
ninjaを呼び出せるように、ninja.exeへのパスをPathに追加しておく。
# CMakeのバージョンを設定 今自分が使っているものを指定している # 必須要件なので、数字は小さい方がいい。 cmake_minimum_required(VERSION 3.25) ############################################# ############################################# # ソリューション名を設定 set(MY_SOLUTION_NAME "MySolution") #プロジェクト名を設定 set(MY_PROJECT_NAME "MyProject") # 出力ファイル名を設定 set(MY_OUTPUT_NAME "myout") ############################################# ############################################# # ソリューションを作成 # プロジェクト名と使用する言語を設定 project(${MY_SOLUTION_NAME} CXX) ############################################# ############################################# # プロジェクトを作成 # out.exeという実行ファイルをmain.cppから作成 add_executable(${MY_PROJECT_NAME} main.cpp ) ############################################# ############################################# ############################################# ############################################# set_target_properties(${MY_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${MY_OUTPUT_NAME})
#include <cstdio> int main(int argc,char** argv){ printf("hello world\n"); }
ninjaは内部で有効なコンパイラを呼び出すので、それらを使用できる環境が必要になる。
今回は、Visual Studio 2022の環境でビルドするため、Visual Studioが動く環境を用意する。
そこでまず、x64 Native Tools Command Prompt for VS 2022 を実行する。
これで、nmakeやcl.exeを使える環境となる。
この状態で、cmake-guiを起動
Generateすると、Where to build the binariesにbuild.ninjaが出来上がる。
D:\myDevelop\project\test\ │ ├─solution │ │ build.ninja ... ninjaファイル │ │ CMakeCache.txt │ │ cmake_install.cmake │ │ │ └─CMakeFiles │ └─src CMakeLists.txt ... CMakeLists.txt function.cpp ... ソースコード main.cpp source.hpp
カレントディレクトリにbuild.ninjaがある状態で、ninjaコマンドを実行する。
これでmyout.exeが作成された。
予約投稿してなかった。タヒにたい。確定申告のせいだ。
本当は開発者に直接言うべきなんだが(またか)、腰が重すぎて十年たっても行動する未来が見えないのでここに書いておく。私はCLaunchのヘビーユーザーなのだがVC++の挙動がおかしいのがまさかこれが原因と思わず数か月悩み続けた。
CLaunchは ぴょんきち 氏の開発したランチャーで、以下のサイトからダウンロードできる。
http://hp.vector.co.jp/authors/VA018351/index.html
・Windows 10 または Windows 11
・Visual Studio 2019 または 2022
・CLaunch 4.04 を起動した状態
上記の条件で、C++プロジェクトを作成し、Win32APIを使用するプログラムを書きデバッグモードまたはリリースモードで実行すると、プログラム終了時、exe_common.inlの中のhas_cctor周辺で高確率で例外が発生する。なおver3系では起こらないかもしれない。
#include<windows.h> int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { MessageBox(NULL, TEXT("my test"), TEXT("title"), MB_OK); return 0; }
exe_common.inl内、以下のあたりで起こる。
if (!__scrt_is_managed_app())
exit(main_result);
if (!has_cctor)
_cexit();
再現性はそこそこ高いのだが出るときはでるものの出ないときは何度試しても出ない。出ないときは何度も実行するか、VC++を再起動するなどするとそのうち発生する。
なお今著者はゼルダの伝説ToKをやっていて記事を書く時間があまりない。