質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.60%

ソケット通信のエラー解消(バージョンアップに伴い発生)

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 4,753

score 13

概要

virtual vox上のゲストOSとwindows10間でudp通信をしようと思い,visual studio 2013を用いて以下のプログラムを作成し実行,成功しました.

その後,同じプログラムをvisual studio 2017でリビルドしたところ以下のエラーが発生しており,実行ファイルが作成されません.エラーの原因は何なんでしょうか?

どなたかご教示下されば幸い.よろしくお願いいたします.

エラーメッセージ

重大度レベル    コード    説明    プロジェクト    ファイル    行    抑制状態
エラー (アクティブ)    E0413    "std::_Binder<std::_Unforced, int &, LPSOCKADDR, int>" から "int" への適切な変換関数が存在しません    streaming_receiver_win    C:\DEVEL_2018\VirtualBox_share\streaming_receiver_win\ver.3\SOURCE\main.cpp    71    

ソースコード(関係ありそうなところのみ)

//サーバー側
#include <vector>
#include <fstream>

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "ws2_32.lib")

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <iostream>

#ifdef _DEBUG
#pragma comment(lib,"C:/SOURCE/OpenCV_3.4.1/BUILD/lib/Debug/opencv_world341d.lib")
#else
#pragma comment(lib,"C:/SOURCE/OpenCV_3.4.1/BUILD/lib/Debug/opencv_world341.lib")
#endif

using namespace std;
using namespace cv;

/* 省略 */

int main() {
    /* ポート番号、ソケット */
    int port = 12345;
    int recvSocket;

    /* sockaddr_in 構造体 */
    struct sockaddr_in recvSockAddr;
    struct sockaddr_in clientSockAddr;

    int clientaddrlen = sizeof(clientSockAddr);
    /* 各種パラメータ */
    int status;
    int numrcv;
    unsigned long on = 1;

    /************************************************************/
    /* Windows 独自の設定 */
    WSADATA data;
    WSAStartup(MAKEWORD(2, 0), &data);

    /* sockaddr_in 構造体のセット */
    memset(&recvSockAddr, 0, sizeof(recvSockAddr));
    recvSockAddr.sin_port = htons(port);
    recvSockAddr.sin_family = AF_INET;
    recvSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    /* ソケット生成 */
    recvSocket = socket(AF_INET, SOCK_DGRAM, 0);

    /* バインド(エラー発生箇所) */
    status = bind(recvSocket, (LPSOCKADDR)&recvSockAddr, (int) sizeof(recvSockAddr));

    /* 省略 */

    /* Windows 独自の設定 */
    WSACleanup();
}

参考サイト

opencv + UDP/IP + Webカメラ

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • yohhoy

    2019/01/14 00:21

    「bind」を「::bind」に変えるとコンパイル通ったりしますか?

    キャンセル

  • Takumi_s

    2019/01/14 00:47

    ありがとうございます。コンパイルが通り実行できました!

    今回の原因は何なのでしょうか?大変恐縮ではございますが,後学のために参考となるサイトなどがあればお教え頂けないでしょうか.

    キャンセル

回答 2

checkベストアンサー

+2

こんにちは。

C++11でstd::bindが追加されています。VC++ 2013はC++11にはほとんど対応していませんが、VC++ 2017はほぼ対応しています。
結果、std::bindの方とマッチしたのだろうと思います。

using namespace std;を止めて、1つ1つ std:: を付けた方が良いと思います。


C++は多量にキーを押すことでコンパイラにより多くのバグを検出させる言語と割り切るのも一つかも。
(キーをケチりすぎるとろくなことになりません。)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/14 00:49

    回答ありがとうございます.

    そういう考え方もあるのですね.大変勉強になりました.

    キャンセル

+1

エラーの直接原因、および推奨事項は Chironian さん回答の通りです。

  • WinSockの bind関数 ではなく、C++標準ライブラリの std::bind関数 が意図せず呼び出されている。
  • using namespace std; は利用せずに、std:: のように名前空間を明示指定する方が好ましい。

今回のエラーは、C言語向けAPI(BSD socket互換)を提供する WinSockのレガシーな関数設計 と、C++言語による 関数オーバーロード解決規則 が組み合わさって生じたと考えられます。

WinSockのbind関数は次のプロトタイプ宣言になっています。

int bind(
  SOCKET                 s,
  const struct sockaddr *addr,
  int                    namelen
);

一方、C++標準ライブラリのstd::bind関数テンプレートは(およそ)下記のようなプロトタイプ宣言になっています。可変長テンプレートやForwarding Referenceが使われており少々難解ですが、ここでは “どんな実引数も受け付ける関数テンプレート” になっていると解釈ください。この “どんな実引数でも受付ける” 性質が、今回のトラブル要因の一端となります。

template <class F, class... BoundArgs>
ResultType bind(F&& f, BoundArgs&&... bound_args);

説明のため、bind 関数呼出し元コードのエッセンスだけ抽出します。ここでは、実引数に指定する第1引数型(int)および第2引数型(struct sockaddr *)が、WinSock bind 関数プロトタイプ宣言の第1引数型(SOCKET==unsigned __int64)および第2引数型(const struct sockaddr *)とは 厳密一致していない ことに着目してください。

// Windows/WinSock標準ヘッダによる型名定義
typedef unsigned __int64 UINT_PTR;
typedef UINT_PTR SOCKET;
typedef struct sockaddr *LPSOCKADDR;

// 呼出しコード
int recvSocket;
struct sockaddr_in recvSockAddr;

status = bind(recvSocket, (LPSOCKADDR)&recvSockAddr, (int)sizeof(recvSockAddr));
  // 第1引数の型=int
  // 第2引数の型=struct sockaddr *
  // 第3引数の型=int

つまり、この関数呼出しには int → unsigned __int64 型変換(整数拡張)と、struct sockaddr * → const struct sockaddr * 型変換(const付与)が必要となります。通常であればこれらの型変換は暗黙に行われるため、プログラマが意識する必要はありません。WinSockはC言語時代に設計された古風なAPI仕様ですから、このコードのような型キャストは自然に行われるものと思います。

一方、C++標準ライブラリ std::bind 関数テンプレートでは実引数から全てのパラメータ型を推論するため、第1引数型F==int, 第2引数型以降BoundArgs=={struct sockaddr *int}と推論されます[※]。つまり、全ての実引数型に関数パラメータ型が完全一致する関数が(意図せず)生み出されます。

ソースコード中で using namesapce std; を行った場合、この std::bind 関数テンプレートが、元々グローバル名前空間にあるWinSockの bind 関数と同列に並びます。C++言語の関数オーバーロード解決規則は非常に複雑ですが、ここでは「型変換が必要となる候補:WinSock bind関数」と「型変換が不要な候補:C++標準ライブラリstd::bind 型推論結果」が天秤にかけられた結果、プログラマの意図しない後者が選択されています。

※注:厳密にはForwarding Referenceのため、テンプレートパラメータF==int&、第1引数型はint&に型推論されます。また可変テンプレートパラメータBoundArgs=={struct sockaddr *int}となり、第2,3引数型はそれぞれstruct sockaddr *&&int&&に型推論されます。


おまけ: 実は質問のケースでは、下記コードのようにプロトタイプ宣言型と実引数の型を厳密一致させると、期待通りWinSockの bind が呼び出されるようになります。しかし、この解決方法はあまりにC++言語の難解なルールに依存しているため、素直に名前空間を分けて扱うことをおススメします。

// OK: 明示的なキャストにより全ての引数型を厳密一致させる
status = bind((SOCKET) recvSocket,
              (const struct sockaddr *) &recvSockAddr,
              (int) sizeof(recvSockAddr));


Demo: https://www.godbolt.ms/z/NiUAlN

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/15 18:36

    非常に丁寧な解説感謝いたします.
    今回この事態が発生した要因に,私の不勉強による雑なコーディングがあったと思います.

    今すぐに言語に精通できるわけでは決してありませんが,出来る限り意識してコーディングしていきたいと思います.

    重ね重ね,ご丁寧な解説ありがとうございました.

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.60%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る