ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
         12
    3456789
    10111213141516
    17181920212223
    2425262728  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
       1234
    567891011
    12131415161718
    19202122232425
    26272829   
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28      
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
    1234567
    891011121314
    15161718192021
    22232425262728
           
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • C++でnlohmann::jsonでjson形式でOpenAIと通信する

    準備

    以下からダウンロードし、single_include/nlohmann を適切な場所に展開する。ヘッダオンリーなのでincludeパスを通すだけでよい。

    https://github.com/nlohmann/json

    使用例

    使用例1 文字列を整形して標準出力

    前回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
        }
    }
    

    使用例2 データを一つだけ取り出す

    #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;
    
    
    }
    

    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")
    
    ////////////////////////////////
    // 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;
    }
    

    C/C++からlibcurlでOpenAIに接続してみる

    サンプルコード

    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;
    }
    

    返答(成功例)

    {"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}}

    ローマ字と指定しているので、Konnichiwa, sekai という返答が返ってきている。

    VC++でlibcurlを使う(https OpenSSLの場合)

    libcurlをlibssl.lib+libcrypto.libで再ビルド

    前回OpenSSLをビルドしてlibssl.libとlibcrypto.libを作ったので、それらをリンクしたlibcurl.libをビルドする。

    前々回のlibcurlのビルドで、プロジェクト設定を以下のように変更する。

    まずLIB Release - LIB OpenSSL を選択。

    libssl.lib ; libcrypto.libを依存ファイルとして設定。それらがあるライブラリディレクトリを指定。

    opensslのincludeディレクトリを指定。

    libcurlをリンクしてプログラムをビルド

    以上でlibcurlをビルドできたので、これを使ったプログラムをビルドしてみる。

    ライブラリを設定

    curlのlibファイルのディレクトリに加え、opensslのlibファイルのディレクトリも追加してビルドする。

    以下の二つを追加ライブラリディレクトリに設定

    • D:\myDevelop\oss\curl-8.0.1\build\Win64\VC14.10\LIB Release - LIB OpenSSL
    • D:\myDevelop\oss\src\openssl-install\lib

     

    pemファイルの準備

    以下から、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;
    }
    

    VC++からlibcurlでhttpsを使うためにOpenSSLをビルド

    環境を汚さないことをコンセプトに、Perl+NASM+nmakeでOpenSSLをビルドする。

    単体の記事として成立するのでlibcurlは次回。

    OpenSSL 環境準備

    NASM

    まず、nasmの公式へ行き、

    https://www.nasm.us/

    Stableから、

    Index of /pub/nasm/releasebuilds/2.16.01/win64

    まで潜り、zipをダウンロード・展開する。

    nasm-2.16.01-win64.zip

     

    Strawberry Perl

    https://strawberryperl.com/releases.html

    へ行き、Portable 64bit版をダウンロードする。

    strawberry-perl-5.32.1.1-64bit-portable.zip

     

    OpenSSL

    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\
    

    OpenSSL ビルド

    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に対応する。

    VC++でlibcurlを使う(httpの場合)

    httpsの場合はopenssl設定が必要になるのでまた後。httpプロトコルのみであればビルドもプログラムもかなり簡単。しかしhttpsのアドレス入れるとUnsupported protocolとか言われる。

    ビルド

    公式からcurl-8.0.1.zipをダウンロードし展開する。

    https://curl.se/download.html

    展開し、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を開き、ソリューションをアップデートすることにする。以下を開く。

    curl-8.0.1\projects\Windows\VC14.10\curl-all.sln

     

    ソリューション構成を 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;
    }
    

    Rustで.libや.dllを作り、C++から呼び出す

    Rust側

    プロジェクト設定

    まず、cargoで--libを指定してプロジェクトを作成する

    >cargo new myhello --lib
    Created library `myhello` package

    Cargo.tomlに以下の設定を追加:

    [lib]
    name = "myhello"
    crate-type = ["cdylib"]

    なお、これがdllではなくstatic libraryのみの場合は、以下のように設定する。

    [lib]
    name = "myhello"
    crate-type = ["staticlib"]

     

    プログラム

    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 になってしまう。今のところ修正する方法がないらしいので、気になるなら手動で修正する。

    > cargo build --release

     

    C++側

    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を試す(anaconda使用)

    mesonはCMakeの代替と言われている。VC++の環境ではPython + ninja +VC++が必要。ただしconda install mesonするとninjaも一緒に入るので実質VC++の設定だけでいい。

    環境を混ぜないために以下をして仮想環境mesonenvを作成し、その中で動作させる。必須ではない。

     

    conda create -n mesonenv python=3.11
    conda activate mesonenv

     

    環境構築

    mesonとninjaのインストール

     

    conda install meson

    VC++の設定

    VC++が動くように、以下で環境設定を行う。(VC++2022)

    "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

     

    mesonのテスト

    まず、testの中にmain.cpp とmeson.buildを作成する。

    d:\myDevelop\test
    ├─main.cpp       ... ソースファイル
    ├─meson.build     ... 設定ファイル
    

    meson.buildを以下のように記述。

    project('MyProject', 'CPP')
    executable('myout', ['main.cpp'])
    

    testをカレントれディレクトリとし、以下を実行。

    (mesonenv) D:\myDevelop\test>meson build
    (mesonenv) D:\myDevelop\test>meson compile -C build

    これで、buildの中にmyout.exeが作成される。

    d:\myDevelop\test
    │  main.cpp
    │  meson.build
    │
    └─build
        │  myout.exe
        │
        ├─meson-info
        │
        ├─meson-logs
        │
        ├─meson-private
        │
        └─myout.exe.p
    

    Windowsにzip版Pythonを導入

    zip版Pythonをインストールすると、OS側の環境を一切汚さず完全に独立した環境を作れる。

    ダウンロード・展開

    https://www.python.org/downloads/windows/

    各種設定

    パスを通した起動用バッチを作る

    もちろんwindowsの環境変数を直接書き換えてもいいが、それをやるとわざわざzip版を導入している意味が薄れるので、起動用のバッチファイルを作成してそれを叩いて環境を起動するような使い方にする。

    SET PATH=D:\myDevelop\mypython;%PATH%
    start cmd.exe
    

    import siteの設定

    このままでは、exit()で終了できないなど問題があるので、._pthファイルを変更する。ファイル名はバージョンによって違う。今回の場合はpython310._pthを編集する。

    import siteの先頭に # がついているのでそれを外す。

    python310.zip
    .
    
    # Uncomment to run site.main() automatically
    import site
    

    pipの導入

    以下から、pipをインストールするためのスクリプトをダウンロード

    https://pip.pypa.io/en/stable/installation/#get-pip-py

    スクリプトを実行

    python get-pip.py

    このスクリプトを実行すると、以下のようなWARNINGが出る。

    WARNING: The script wheel.exe is installed in 'D:\myDevelop\mypython\Scripts' which is not on PATH.
    Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
    WARNING: The scripts pip.exe, pip3.10.exe and pip3.exe are installed in 'D:\myDevelop\mypython\Scripts' which is not on PATH.
    Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.

     

    意訳:

    pipはD:\myDevelop\mypython\Scriptsにインストールされましたがパスが通ってないので自分で通してください。

     

    WARNINGに従い起動用のバッチファイルにScriptsへのパスも追加する。

    SET PATH=D:\myDevelop\mypython;%PATH%
    SET PATH=D:\myDevelop\mypython\Scripts;%PATH%
    start cmd.exe
    

    ビルドシステムのNinjaでC++のソースコードをビルドしてみる

    ninjaはビルドシステムの一つで、makeの仲間。高速なのが取り柄らしい。makeはmakefileを作成してビルドするのに対して、ninjaは.ninjaファイルを作成してビルドする。ただし、.ninjaはcmakeで作ることを想定している。

    準備(ninjaの導入)

    ninjaは以下URLから環境にあったものをダウンロード、展開する。今回はwindowsなのでninja-win.zipをダウンロードする。

    https://github.com/ninja-build/ninja/releases

    ninjaを呼び出せるように、ninja.exeへのパスをPathに追加しておく。

    作り方

    ソースコードの準備

    CMakeLists.txt

    # 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})
    

    main.cpp

    #include <cstdio>
    
    int main(int argc,char** argv){
    	printf("hello world\n");
    }
    

    VC++を使える環境でcmake-guiを起動

    ninjaは内部で有効なコンパイラを呼び出すので、それらを使用できる環境が必要になる。

    今回は、Visual Studio 2022の環境でビルドするため、Visual Studioが動く環境を用意する。

    そこでまず、x64 Native Tools Command Prompt for VS 2022 を実行する。

    これで、nmakeやcl.exeを使える環境となる。

    この状態で、cmake-guiを起動

    >C:\Software\cmake-3.25.0-rc2-windows-x86_64\bin\cmake-gui.exe

     

    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
        
    >d:
    >cd D:\myDevelop\project\test\solution
    D:\myDevelop\project\test\solution>

    カレントディレクトリにbuild.ninjaがある状態で、ninjaコマンドを実行する。

    >ninja
    [1/3] Building CXX object CMakeFiles\MyProject.dir\function.cpp.obj
    ...
    [2/3] Building CXX object CMakeFiles\MyProject.dir\main.cpp.obj
    ...
    [3/3] Linking CXX executable myout.exe

    これでmyout.exeが作成された。

    余談

    予約投稿してなかった。タヒにたい。確定申告のせいだ。

    CLaunchとVC++の相性が悪い件

    前置き

    本当は開発者に直接言うべきなんだが(またか)、腰が重すぎて十年たっても行動する未来が見えないのでここに書いておく。私は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;
    }
    

    終了時

    0x00007FFD63892D6A (ntdll.dll) で例外がスローされました (WindowsProject2.exe 内): 0xC0000008: An invalid handle was specified

    exe_common.inl内、以下のあたりで起こる。

    if (!__scrt_is_managed_app())
        exit(main_result);
    
    if (!has_cctor)
        _cexit();
    

    注意

    再現性はそこそこ高いのだが出るときはでるものの出ないときは何度試しても出ない。出ないときは何度も実行するか、VC++を再起動するなどするとそのうち発生する。

    余談

    なお今著者はゼルダの伝説ToKをやっていて記事を書く時間があまりない。