0

特定のハードウェアとのすべての通信を処理するクラスがあります。ハードウェアは、1 つのソケットを介してデータを更新する必要があり、既知の (そしてハードコードされた) マルチキャスト アドレスでハンドシェイクの目的でその IP をブロードキャストします。

その結果、クラスは 2 つのスレッドを生成します。1 つのスレッドは、ハードウェアとの直接通信を処理し、クラスのメンバー構造に基づいてデータを更新します。もう一方のスレッドは、マルチキャスト アドレスを継続的に監視して、IP が変更されたかどうかを確認します。

両方のスレッドは、クラス内の静的メンバー関数として実装されます。ただし、pthreads を使用するには、extern "C" として宣言する必要があると理解しています。とはいえ、私の調査によると、クラスのメンバー関数を extern "C" として宣言することはできません。以前このプロジェクトに携わったエンジニアがこの問題を回避するコードを見たのは、スレッド化された関数の実装が extern "C" で囲まれていることです。つまり、extern "C" は関数の宣言または定義に含まれていませんが、関数定義全体が extern "C" {} にカプセル化されています。

このような:

extern "C" {
  // function goes here
}

コードの理解を深め、マルチスレッド プログラミングのスキルを向上させたいと考えているため、この実装についていくつか質問があります。

  1. extern "C" は、実装されている方法で実際に何かを行いますか? ここで修飾子が実際に行うことについて読みました: In C++ source, what is the effect of extern "C"? しかし、この場合、クラスの静的メンバーである関数を扱っているという事実を考えると、実際に適用できますか?

  2. スレッド化されたメンバー関数は、 reinterpret_cast を実行してクラスへのポインターを作成することにより、クラスのメンバー変数を変更するという事実を回避しているようです。そのキャストを見たときに危険信号が発生したため、これは標準的な慣行ですか?

  3. コンストラクターは pthread_create() を呼び出してスレッドを起動します。ある時点で 1 つのインスタンスのみがハードウェアと通信できるようにする静的メンバー変数を追加するようなことを計画していました。つまり、呼び出し元がクラスの複数のインスタンスを持つことを許可することを考えていましたが、一度に「接続」できるのは 1 つだけです。これは標準的な慣行ですか、それとも私の考え方に「ハッキー」なものがありますか? 特定の時点で接続されるハードウェアは 1 つだけであるため、特定の時点で動作するクラス インスタンスが 1 つだけである必要があります。

ご助力いただきありがとうございます。また、「おい、Boost スレッドを使用してください、FTW」のようなことを提案する場合は、息を止めてください。最終的にはスレッド ライブラリに移行しますが、Boost は POSIX システムで pthread をラップするだけであると理解しています。その結果、ライブラリで自分を台無しにする前に、まず生のスレッドの使用について学びたいと思っています。私の主な目標は時間通りに成果を出すことですが、途中で何も学べないとは誰も言いませんでした。;)

追加するために編集:

明確にするために、私が見ている問題を説明するソースコードを次に示します。この場合、extern「C」が何かをしているとはまだ100%確信が持てません...

私の.hで:

class MyClass
{
 private:
  static void* ThreadFunc(void* args);  // extern "C" not found in declaration
  static bool instance_;
};

私の.cppで:

MyClass::MyClass() {
  if (!instance_) {
    instance_ = true;
    // spawn threads here
  } else {
    // don't spawn threads and warn user
  }
}
extern "C" {
  void *MyClass::ThreadFunc(void* args) {  //definintion wrapped in extern C
    MyClass* myclass_ptr = static_cast<MyClass*>(args);
    // ... more code
    return static_cast<void*> 0;
  }
4

2 に答える 2

3

質問1

クラスの静的メンバーである関数を扱っているという事実を考えると、実際に適用できますか?

適用できますが、必須でありません。準拠する実装 (コンパイラ) は、関連する型情報を認識して、関数ポインタをそのまま使用するか、暗黙的な変換を実行するか、エラーで失敗します。

ただし、言語リンケージを指定するときに追加のプロパティを適用できることに注意してください。これには、特定の呼び出し規約が含まれる場合がありますが、これに限定されません。ただし、標準ではこれを実装に任せています。

$7.5/1 から

言語リンケージを持つエンティティーに関連付けられたプロパティーの一部は、各実装に固有のものであり、ここでは説明されていません。たとえば、特定の言語リンケージは、外部リンケージを持つオブジェクトや関数の名前を表す特定の形式、または特定の呼び出し規約などに関連付けることができます。

言語リンケージを指定することにより、実装でこれらのプロパティの詳細を処理できるようになります。それ以外の場合は、言語拡張機能を使用して、呼び出し規約などを指定する必要があります。それを行ったとしても、不明な、または実装固有の拡張機能を介して適用できない他のプロパティが存在する可能性があります。

言語リンケージは、エンティティーのタイプ(この場合は関数) にも影響します。

$7.5/1 から

言語リンケージが異なる 2 つの関数型は、それ以外は同一であっても、別個の型です。

あなたの例では、静的メンバー関数へのポインターは、スレッド開始関数の関数ポインター型に変換できます。これが、言語リンケージを指定せず、明示的なキャストを行わなくても、コードが適切に機能する理由です。

ただし、これは、言語リンケージを指定してはならないという意味ではありません。実装間の移植性が気になる場合は、これを使用することをお勧めします。たとえば、一部の実装fastcallではデフォルトの C 呼び出し規則を使用cdeclし、他の実装ではデフォルトとして使用します。言語リンケージ指定子がない場合、少なくとも適合する実装では、コードはコンパイルされません。

以下の例は、異なる呼び出し規則が使用された場合に変換が失敗する方法を示しています。例で表現するのが最も簡単であるため、呼び出し規約を使用することにしました。

#include <iostream>

#if defined(__GNUC__)
#define FASTCALL __attribute__((fastcall))
#elif defined(_MSC_VER)
#define FASTCALL __fastcall
#endif

extern "C" 
{
    typedef void (*FUNC1)(); // Default C calling convention
    typedef void (FASTCALL *FUNC2)();  // fastcall calling convention
    void do1(FUNC1 f) { f(); }
    void do2(FUNC2 f) { f(); }
}

struct X
{
    static void test() {}
};

int main()
{
    do1(X::test);   // Ok. Implicit conversion available
    do2(X::test);   // Fail! Incompatible types. No conversion available
}

質問2

... a を実行してクラスのメンバー変数を変更し、クラスreinterpret_castへのポインターを作成します。そのキャストを見たときに危険信号が発生したため、これは標準的な慣行ですか?

キャストを使用することがこれを達成する唯一の方法ですが、使用するreinterpret_cast必要はなく、IMO は絶対に避ける必要があります。に渡されるスレッド開始関数pthread_createは、voidポインターを唯一の引数として受け取ります。static_cast十分であり、動作がはるかに狭いです。

質問#3

静的メンバー変数を追加するようなことを計画していました...

質問は主観的なものであり、これにどのようにアプローチするかは完全にあなた次第です。コードがなければ、実装がハックであるかどうかを判断するのは困難です。

最後の注意として...DUDE!ブーストを使おうよ!:)

于 2013-06-15T03:31:35.990 に答える
2

静的メンバー関数を宣言することはできませんがextern C、別の関数から静的メンバー関数を呼び出すことはできますextern C。スレッド関数が定義されている cpp ファイルで:

namespace {
extern "C" {
void* some_thread_func(void* arg) {
  return MyClass::static_thread_func(arg);
}
}}

// static member
// (You can omit this "middle-man" if "do_stuff()" is public.)
void* MyClass::static_thread_func(void* arg) {
  return static_cast<MyClass*>(arg)->do_stuff();
}

void MyClass::start_thread_func() {
  pthread_t thread;
  int error = pthread_create(&thread, NULL, some_thread_func, this);
  // handle errors, store "thread" somewhere appropriate.
  ...
}

複数のインスタンス/1つの接続された質問は設計上の問題であり、どの設計がプログラムの残りの部分に実際に適合するかはわかりませんが、Singletonは管理する単一のデバイスがあるという設計要件を反映しているようです.

boost::thread最後に、使用しないでくださいstd::thread;)

于 2013-06-15T04:13:12.883 に答える