2009-04-09 10 views
2

低レベル(つまり、フリップレジスタビットなど)のハードウェアにアクセスするコードライブラリを作成する予定です。低レベルのC/C++コードを混在させる

これまではすべてをC関数として記述し、extern "C"を使用してCとC++コードの両方でライブラリをコンパイルしました。したがって、CとC++の両方のユーザーは、単にヘッダーファイルをインクルードして、関数をそのまま呼び出すだけでした。

今、クラスとして物事を整理することを考えています。たとえば、すべての関数を組み込んで、クラス内のUARTを初期化、構成、送信、および受信することができます。これはC++でうまく動作しますが、Cについてはどうですか?私はクラス全体をextern "C"することはできません。

私が考えていたこと:extern "C"でエスケープされた標準C関数のすべてを書いてください。次に、これらの 'C'関数を呼び出す一連のインラインメソッドを持つC++のラッパークラスを提供します。

int foo_bar (int *address, int data) {...} // extern C stuff 
int foo::bar (int *address, int data) { return foo_bar(address, data); } // inline method 

大丈夫ですか?他のアイデア?ベストプラクティス?

答えて

5

あなたが提案しているものにはいくつかの先例があります.MicrosoftのMFCクラスはC互換のWindows APIに関するC++ラッパーです。

しかし、始める前に、あなた自身のために忙しい作業をすること以外にもいくつかの目標があります。 C++はCよりも扱いが容易であるか、何も得られていないはずです。

2

あなたはそうすることができますが、何が得られますか?クラスがいくつかの機能を追加しない限り、私は自由関数のアプローチに固執します。

一方、クラスのアプローチでは、クラスのクライアントのバッファなどの管理を行うことで、C関数をもっと使いやすくすることができます。クラスはC APIを使用して実際の作業。

+0

何もありません。しかし、UART_send(* port、data)の代わりにUART.send(data)を行う方が簡単かもしれません。ポインタは、例えば、プライベートであってもよい。 – sybreon

+1

Cインターフェイスは引き続き利用可能で、実際にはそのインターフェイスを使用するためには、ポートへのポインタが必要です。だからそれは本当に私的ではありません。 –

1

私は、C++の抽象化機能の恩恵を受けたいと思っていますが、コードをCで書かれたコードにアクセスできるようにしています。これを達成する方法の1つは、C++で大量のコードを書いて、 Cの世界にあなたのコードをインターフェースする薄いextern "C"ラッパー関数。

あなたが提案したアプローチも機能しますが、別の回答者が指摘したように、それはあなたに追加の力を買わないことになります。

どちらのアプローチも、プレーンなC関数アプローチに比べてわずかなパフォーマンスの低下を招くことに注意してください。あなたの提案では、C++コードが価格を支払っていますが、Cコードは価格を支払っています。これは、関数をインラインとして定義することで最小化できます。

+0

はい、私は可能な限りインラインで作ります。 – sybreon

3

これを実行する強固な理由の1つは、Cインターフェイスがリソースを表すために典型的な「ハンドル」イディオムを使用している場合です。 Cでは

OpaqueThingHandle t = CreateOpaqueThing(); 

DoStuffWithOpaqueThing(t); 

DestroyOpaqueThing(t); 

クライアントは、それが実装されている方法のない可視性を持っていないように、情報隠蔽を達成するために、OpaqueThingHandleはしばしばvoid *のtypedefです。

C++ラッパーは単にRAAIを適用することにより、真に有益な何かを追加することができます - AQUIRE機能や自由ハンドルタイプで識別されるリソースへのマッピングの建設と破壊を:

class OpaqueThing : boost::noncopyable 
{ 
    OpaqueThingHandle handle; 

public: 
    OpaqueThing() 
     : handle(CreateOpaqueThing()) {} 

    ~OpaqueThing() 
     { DestroyOpaqueThing(handle); } 

    void DoStuff() 
     { DoStuffWithOpaqueThing(handle); } 
}; 
1

率直に言って、私はドン本質的にCとC++の関数があなたを買うのを見ているのか分かりません。

私はC関数をできるだけ基本的なものにして、すべてのプリミティブが公開されていて空想がないAPIを定義することをお勧めします。その後、はるかに高いレベルでC++クラスを定義してください。そのため、UARTにはコンストラクタ、デストラクタ、意味のあるメソッドがあります。すべての必要なデータストレージを備えています。 C++クラスはすべての可能なプリミティブを公開しません。別の何かをするために、C APIを呼び出すことで、新しいメンバー関数やクラスなどを書くことになります。

これは、OCCI(Oracle C++ Call Interface)やMFCなどのシステムでのアプローチであり、うまく機能します。

+0

うん、それは私がやろうとしていることだ。たぶんそれは明らかに問題ではなかったでしょう。 – sybreon

2

CメソッドベースのインターフェイスとC++クラスのインターフェイスの両方をエクスポートする必要があります。 C関数のまわりの薄いC++ラッパーか、C++インスタンスの周りのC関数のどちらにでも行くことができます。

は、後者の場合は、典型的なパターンはこれです:

void * c_open_thing(id) { return new CThing(id); } 
void c_close_thing(void * handle) { delete (CThing) handle; } 
int c_transmit(void * handle, transmitbuf) 
    { return ((CThing *)handle)->Transmit(transmitbuf); } 

このような単純なラッパーは、あなたがそれを行うどの方法でno matzterが、しかし無意味です。 C++ラッパーは、次の方法で値を追加することができます。

  • 強制構築/破壊要件
    あなたが 割り当てを提供し、建設をコピーすることができない限り、cosntructor /デストラクタを使用すると、ここで通常は十分ではありません。私は通常reference counted handleを使用します。あなたは元のライブラリをコントロールしている場合

  • エラー処理
    これはにエラーを変換し、クリーンアップを必要とするかもしれない(意味のある!)例外など

  • スレッドの安全性 はもちろん、あなたがそれを追加することができます。バイナリで得

  • ...