4

GCC を使用して組み込み環境用に C99 で開発しています。循環バッファとキューを処理する小さなライブラリを作成しました。バッファと必要なメタデータを含む基本的な構造体のインスタンスを実装して動作します。

struct circbuf_u8_st {
    const uint8_t buffer_num_elems; 
    uint8_t head;
    uint8_t tail;
    uint8_t * buffer;   
};

これは、データを生成する割り込みルーチンとデータを消費するメイン ループとの間の通信に使用されるため、グローバルstructとして使用されることがあります。volatile

ただし、メイン ループの一部が後で同じメイン ループで消費されるデータを生成する場合など、structが非ローカルとして使用されることがあります。volatile

structある場合とない場合があるということは、これをvolatile扱う関数には、パラメータと非パラメータstructの 2 つのバージョンが必要であることを意味します。これは保守の問題です。いずれかのバージョンで行われた変更は、他のバージョンでも繰り返さなければなりません。例えば、volatilevolatile

void circbufu8_reset(struct circbuf_u8_st *cb);
void circbufu8_v_reset(struct circbuf_u8_st volatile *cbv);

volatileそれは常に正しいので、すべてのバージョンをそのまま使用できます。volatileしかし、それはまた、私が避けたい非ケースに対する悲観化を意味します。

したがって、考えられる解決策は、 /nonメンバーでaunionを宣言し、メンバーが that の型を持つように宣言することです。volatilevolatilestructunion

union un_dual_volatile_u8 {
    uint8_t volatile v;
    uint8_t nv;
};

これは、各関数の 2 つのバージョンの問題を解決するのに役立ちます。しかし、それは本当に役に立ちますか?そのような関数にはどのようなセマンティクスがありますか? 私の理解では、コンパイラは に必要な最も厳密なセマンティクスを使用する必要があるため、実際には、これは、同じ悲観化を伴う、unioneverything- オプションの不必要に複雑なバージョンになります。volatile

したがって、質問は次のとおりです。

  • 組合が助けてくれないというのは正しいですか?
  • 関数の重複ペシミゼーションを回避する方法はありますか?
  • このような状況で C++ は役に立ちますか? (関数のオーバーロードはセマンティクスvolatileと非volatileセマンティクスを尊重しますか?またはジェネリックを使用しますか?)

(コンパイラの出力を見ることは有効な答えではありません。信頼できる標準ベースの正当な理由を探しています)

編集:質問のC++部分は好奇心からだったので、C++タグを削除しました。そしてもちろん、C11 のジェネリックは問題の解決を容易にしますが、目標は C99 でこれを解決することです。

4

4 に答える 4

3

C++ では、テンプレートを使用してこれを行うことができます。

template <class T>
void circbufu8_reset(T &cb)
{
  // code here
}

もちろん、この関数テンプレートは任意の型で呼び出すことができるため (ただし、インスタンス化に失敗する可能性があります)、次のように使用を制限することをお勧めします。

class Helper
{
  friend void circbufu8_reset(circbuf_u8_st &);
  friend void circbufu8_reset(volatile circbuf_u8_st &);
private:
  template <class T>
  static void reset(T &cb)
  {
    // use cb here, guaranteed to be circbuf_u8_st & or volatile circbuf_u8_st &
  }
};

inline void circbufu8_reset(circbuf_u8_st &cb)
{
  Helper::reset(cb);
}

inline void circbufu8_reset(volatile circbuf_u8_st &cb)
{
  Helper::reset(cb);
}

編集

さらに C++ の方法は次のようになります。

struct circbuf_u8_st {
    const uint8_t buffer_num_elems; 
    uint8_t head;
    uint8_t tail;
    uint8_t * buffer;   

    void reset() { resetImpl(*this); }
    void reset() volatile { resetImpl(*this); }
private:
    template <class T>
    static void resetImpl(T &cb) {
      //code with cb
    }
};
于 2013-09-13T14:05:13.357 に答える
0

C でこれを解決するには、マクロを使用できます。さらに良いことに、C11 を使用できる場合は、ジェネリックを使用できます。

マクロの使用:

#define RESET_BUFF_IMPL(x){\
    /* do something with x as if it were circbuf_u8_st [volatile]* */ \
}

void reset_buff_v(struct circbuf_u8_st volatile *ptr) RESET_BUFF_IMPL(ptr)
void reset_buff(struct circbuf_u8_st *ptr) RESET_BUFF_IMPL(ptr)

次に、必要なものを使用します。これは、話していたコードをすべて複製する必要がないことを意味します。

そして、C11 を使用する場合は、前述のように、これをさらに甘くすることができます。

typedef struct circbuf_u8_st volatile *buff_vptr;
typedef struct circbuf_u8_st *buff_ptr;
#define RESET(x) _Generic(x, buff_vptr: reset_buff_v, buff_ptr: reset_buff)(x)

これにより、構文糖衣が少し追加されます。


std::enable_ifこれまでのすべての C++ の回答は、ある種のオブジェクト指向性を使用していたので、2 セントの価値があるstd::is_sameと思いましたstd::remove_cv

C++14 を使用できる場合は、タイピングの手間を省く便利なstd::enable_if_tandもあります。std::remove_cv_t

テンプレート機能:

template<typename T, typename = typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, circbuf_u8_st>::value>::type>
void circbufu8_reset(T volatile *ptr){
    // ...
}

これは次のように簡略化できます。

template<typename T, typename = std::enable_if_t<std::is_same<std::remove_cv_t<T>, circbuf_u8_st>::value>>
void circbufu8_reset(T /* volatile */ *ptr){
    // ...
}

あなたのニーズに合うでしょう。

于 2015-07-12T01:54:30.213 に答える