2016-06-27 9 views
6

私は、所有していない/保守していないドライバにC++からのioctlコールを作成しています。きれいな "safe-ish"メカニズムがあるかどうかを調べています必要な醜い構造体の割り当てのいくつかに対処する。 IOCTL醜いC構造体割り当て用の最新のC++パターン

// IOCTL expects an instance of this structure "first" 
typedef struct { 
    int param1; 
    int param2; 
} s_ioctl_request; 

//... followed by an instance of this. If attr_length 
// is > sizeof(s_attr_header), more data is allowed to follow. 
typedef struct { 
    uint32_t attr_length; 
    uint32_t attr_type; 
} s_attr_header; 

// Example that uses more data than just the header. 
typedef struct { 
    s_attr_header hdr; 
    uint32_t attr_param; 
} s_attr_type1; 

// Another example. 
typedef struct { 
    s_attr_header hdr; 
    uint32_t attr_param1; 
    uint32_t attr_param2; 
} s_attr_type2; 

を関与いくつかの構造のバージョンをスリム

s_ioctl_requestが直ちにattr_lengthをバイト単位外側構造体の大きさに設定されているs_attr_header、またはそれを含む他の構造体、が続くことを必要とします。それはこれらの線に沿って何かを介して行われますioctlのラッパー書くためC

、:私はと思ってい

int do_ugly_ioctl(int fd, int p1, int p2, s_attr_header * attr) 
{ 
    int res;  
    // Allocate enough memory for both structures. 
    s_ioctl_request *req = malloc(sizeof(*req) + attr->hdr.attr_length); 

    // Copy the 2nd, (variable length) structure after the first. 
    memcpy(((char*)req) + sizeof(*req), attr, attr->hdr.attr_length); 

    // Modify params as necessary 
    req->param1 = p1; 
    req->param2 = p2; 

    // Make the driver call, free mem, and return result. 
    res = ioctl(fd, SOME_IOCTL_ID, req); 
    free(req); 
    return res; 
} 

// Example invocation. 
s_attr_type1 a1; 
a1.hdr.attr_length = sizeof(a1); 
a1.hdr.attr_type = 1; 
do_ugly_ioctl(fd, 10, 20, &a1); 

カップルのオプションは、以下のとおりです。

  1. 投現代のC++ - ismsはウィンドウの外にあり、私が上に示したものとまったく同じです。少なくとも私はnew[]/delete[]またはmalloc/freeをやっていないよので

  2. がのstd ::ベクトルのストレージを割り当て、結果として得られるのstd ::ベクトル::データ()ポインタで醜いキャストを行います。

  3. s_attr_type*ごとに独自の「特別な」構造体を使用する独自のラッパーメソッドを作成します。これは、「最も安全」と思われる、すなわち、ラッパー方法のユーザーがそれをねじ込む可能性が最も低いと思われる。そしてボーナスポイントは、パスバイリファレンスを許可します。

方法#3例:

  • は、それはC++に "それだけの価値" です - この問題に対する解決策をIZE:

    int do_ugly_ioctl(fd, int param1, int param2, s_attr_type2& attr){ 
        struct RequestData { 
         s_ioctl_request ioreq; 
         s_attr_type2 attr; 
        }; 
        RequestData r; 
        r.ioreq.param1 = param1; 
        r.ioreq.param2 = param2; 
        r.attr   = attr; 
        r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here. 
        ioctl(fd, SOME_IOCTL_ID, (void*) &r); 
    } 
    

    だから私はここにいくつかの質問があると思いますか? (エラーが発生しやすいC implに頼るのとは対照的に)。

  • 私が方法#3またはそれに類似している場合は、<type_traits>でこの機能のテンプレートを作成し、最初のメンバーとしてs_attr_headerの構造体のみを受け入れることができますか?

  • 他にも素晴らしいアイデアはありますか?

+0

Cラッパーには8行のコードが必要です。 C++ラッパーには10が必要です。私は多くの改善が見られません。 –

+0

オプション2はstd :: vector内の割り当てを隠すだけなので、全く改善はありません。実際、オプション2はひどい解決策です。 –

+0

@CareyGregoryすべてのC++プログラマーがベクトルの仕組みを知っているので、私はそれが隠されているとは言いません。それはカプセル化されたと呼ばれます。 – Jens

答えて

3

完全に価値があり、あなたのソリューションはかなりいいです。 複数の構造体を組み合わせるときに余分なパディングが発生しないように、構造体をpacked(これを実現するためのコンパイラ拡張があります)として宣言したい場合があります。

コンストラクタ内の構造体のサイズを設定することもできます。

あなたの2番目の質問について
struct RequestData 
{ 
     RequestData() : ioreq{}, attr{} 
     { 
     attr.hdr.attr_length = sizeof(attr); 
     } 
     s_ioctl_request ioreq; 
     s_attr_type2 attr; 
}; 

、次の2つに割り当てを分割することができ、それはあまりにも素敵ではないのですが、それは簡単です、あなたが正しいヘッダのない何かを渡すと、それはコンパイルエラーにつながる:

template<typename Attr> 
int do_ugly_ioctl(fd, int param1, int param2, Attr& attr){ 
    struct RequestData { 
     s_ioctl_request ioreq; 
     Attr attr; 
    }; 
    RequestData r; 
    r.ioreq.param1 = param1; 
    r.ioreq.param2 = param2; 
    s_attr_header hdr = Attr.hdr; //this will lead to compilation error if the type is not what we expect 
    (void) hdr; 
    r.attr  = attr; 
    r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here. 
    ioctl(fd, SOME_IOCTL_ID, (void*) &r); 
} 
+0

私のメソッド#3に精神的な "missing link"がない場合、ヘッダメンバーにアクセスするとエラーが発生するという事実。 –

関連する問題