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

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

ただいまの
回答率

89.23%

C++で受信クラスと受信内容に対するアクションを分ける実装方法?

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,431

sin_250

score 104

以下のコードの通り、一応の解決案は持っているのですが、
よりシンプルな案はないかと思ったので質問させていただきます。

以下のような条件をお考えください。
・ある機械がある
・その機械は何らかの通信をし、通信データを受信する受信部を持っている
・機械は、受信内容に合わせて何かしらのアクションを取る

そこで以下のようにクラスを実装したいです。
・機械のためのMachineクラスを作る
・受信部のためのReceiverクラスを作る
・MachineクラスはReceiverクラスをメンバとして所有する
・Receiverクラスはstart()メソッドを持ち、その中で延々と受信ループを回す
・Receiverは受信だけに専念し、Machineが受信内容に対してどういうアクションを取るかは関知しない

このとき、Receiverが受信ループの中で受信した内容を、Machineクラスにどうやって伝え、
そしてMachineクラスの受信内容に対するアクションの実装は、どのようにしたらよいでしょうか。
Receiverクラスは受信だけに専念するクラスなので、そもそもMachineクラスの存在など知りません。

私の一つの解決案?は以下ですが、もっと少し簡単な方法があれば知りたいです。

#include <iostream>
#include <unistd.h>
#include <string>

class ReceiveHandler {  // 受信ハンドラインターフェース
public:
    virtual void onReceive(std::string msg) = 0;
};

class Receiver {
private:
    ReceiveHandler* handler_;
public:
    void setReceiveHandler(ReceiveHandler* handler) {
        handler_ = handler;
    }
    void start() {
        while(true) {  // 受信ループ
            std::string msg = "hoge";  // 本当は msg = wait_receive() みたいな感じ
            handler_->onReceive(msg);
            sleep(1);  // 本当は要らない
        }
    }
};

class Machine : ReceiveHandler {  // Machine自信が受信ハンドラインターフェースになる
private:
    Receiver receiver_;
public:
    Machine() {
        receiver_.setReceiveHandler(this);  // 自分自身を受信ハンドラとしてセット
    }
    void onReceive(std::string msg) {
        std::cout << "Message Received: " << msg << std::endl;
    }
    void startReceive() {
        receiver_.start();
    }
};

int main(void) {
    Machine machine;
    machine.startReceive();

    return 0;
}

これを実行すると、1秒おきに
Message Received: hoge
が表示されます。

これが普通のやり方なのか、もっと簡単な方法があるのか、気になっております。
なお受信メッセージをReceiver.start()内でキューイングして、Machine側でキューをポーリングして取り出す、というのは
あまり考えておりません。リアルタイム性が怪しくなるためです。
あくまで、受信したら受信内容に対するアクションを即時に起こさせたいです。

アドバイスいただきたく、よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

こんにちは。

もし、受信→機械動作→受信→機械動作 というように完全にシリーズに実行されるのであれば、その実装でも悪くはないような気がします。
ただ、MachineがReceiverのインスタンスを保持し、かつ、ReceiverがMachineへのポインタを保持する点が、相互参照になるため心配です。相互参照は必要でない場合は避けた方が安全です。

なお、一般に通信と機械動作は平行に行うケースも多いと思います。
その場合は、それぞれのメイン・ループを別スレッドにすると制御ロジックを単純化できます。その時はReceiverを外に出して相互参照を回避しておかないと、デッドロックしやすくなると思います。

また、外部からの要求をReceiverが受け付けると言う構成であれば、Receiverは理論的にMachineに対するマスターになります。その場合はReceiverはMachineに依存させ、逆にMachineはReceiverに依存しない方が、経験的には機能拡張に耐えやすくなりそうな印象も受けます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/13 23:06

    早速のコメントありがとうございます。理解力が浅く恐縮ですが、もう少し詳細をお尋ねしてよいでしょうか。

    >相互参照は必要でない場合は避けた方が安全です。
    おっしゃるとおりだと思います。私もその点が気になっていました。これが気持ち悪いので、もう少しシンプルなコードはないかなと思っています。

    >その場合はReceiverはMachineに依存させ、逆にMachineはReceiverに依存しない方が
    おっしゃるように今はReceiverクラスが直接、外部との通信をする、例えばソケットを張ることを考えています。
    これは、ReceiverがMachineのインスタンスを所有するという意味でしょうか?例えば以下のような感じです。
    class Receiver {
    private:
    Machine machine_;
    public:
    void start() {
    msg = wait_receive();
    machine_.doAction(msg);
    }
    };


    >それぞれのメイン・ループを別スレッドにすると制御ロジックを単純化
    この場合、Receiverクラスで新たなメッセージを受信した時、即時にMachineクラス側にメッセージを通知する方法はあるのでしょうか?(浅学で恐縮ですが、マルチスレッドプログラミングの経験が少なく、イメージを持てていません)

    質問多く恐縮です。何卒よろしくお願いいたします。

    キャンセル

  • 2017/05/14 00:09

    > これは、ReceiverがMachineのインスタンスを所有するという意味でしょうか?

    ReceiverがMachineを所有しても良いとは思いますが、通信回線が機械を所有するイメージはちょっと違うかなと感じます。
    この辺は、現実のモデルに近い実装を採用しておいた方が、変更に耐えやすいです。(現実世界の変更は物理法則の制約を受けますから、現実に近いモデルの方が現実の変更を受け入れやすい筈)

    リアルでも通信回線の部品と機械部分は独立していて、同じ筐体内に収めている場合が多いと思います。
    それと同じように、ReceiverとMachineを統括する例えばHousingのようなクラスがあると良いかも知れません。
    HousingがReceiverとMachineのインスタンスを保持し、その間の接続を担うイメージです。
    例えば、Machineをコンストラクトしてその参照をReceiverのコンストラクタへ渡す等ですね。

    もし、Machineが多種類ある場合は、インタフェース用の基底クラス(ReceiveHandlerみたいなもの)を定義して独立化することも考えられますが、インタフェースを追加する時の手間がかかる割に、実際に複数のマシン・クラスを別途定義するよりは、単に変数を追加して動作を切り替える方がありがちな気がします。ハードウェアはソフトウェアに比べると遥かに変更がたいへんなので、別クラスが必要になるほどの大変更はなかなかないものです。

    マルチ・スレッドをどうするかはたいへん悩ましいです。
    まず、スレッド間でどのような通信が必要になるのかを決める必要があります。
    恐らく、ReceiverからMachineに対して要求があり、逆方向に応答があると思います。
    これらがどのように発生するのかにより、大きく実装は変わります。
    要求A→応答A→要求B→応答Bのように単純なケースなら要求イベントと応答イベントを使う程度で済みます。
    機械が動作している間、時々動作状況の通知があり、その合間に要求と応答が発生する場合もあるでしょう。更に、要求が頻発するためキューにいれないと行けないかも知れません。もしくは、頻発しすぎても処理できないので、ダブルバッファにするかも知れません。

    次に、マルチ・スレッドをどのように構成するべきかについては、私も確定した方針を持っていません。臨機応変と言うとかっこいいですが、どちらかと言うと行き当たりばったりかも知れません。

    キャンセル

  • 2017/05/16 22:24

    詳細なご回答ありがとうございます。おかげさまで、それなりにわかってきました。
    よい設計というのは、難しいですね。がんばります。

    ご回答有り難うございました。

    キャンセル

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

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