貧しいアレクサンドレスについて、一言

高価な時間帯!

本アレクサンドレスク 最近、C ++でのテンプレートメタプログラミングに関する記事を 1つ読みました。 そして、そのようなコメントがありました :「インターフェイス、実装、ファクトリー、定義、構成、およびあらゆるものに対して、まったく同じレベルのカスタマイズで同じことができます。」 一般的に、記事と議論のモラル-アレクサンドレスクからのこれらのテンプレートは、人生ではあまり必要ありません。

私は自分の仕事を思い出しました。直交設計のアイデアが彼(Alexandrescu)に大いに役立ちました。 あなたと共有したいです。





問題



私はコンピューターとマイクロコントローラーデバイス用のプログラムを書いています。 そして、私はしばしばそれらの間の接続を整理する必要があります。 そして、いつものように、プログラムではエラーをキャッチする必要があります。 また、PCプログラムのデバッグがそれほど難しくない場合、マイクロコントローラーでは事態はさらに悪化します(ある意味、あらゆる種類のインサーキットデバッガーがありますが、リアルタイムタスクには適していません)。 まあ、それはポイントでもありません-接続自体をデバッグする必要がある場合があります! つまり、情報は送られるように見えますが、どういうわけか間違っています...これは、プログラムの開発の開始時に重要です-接続自体、プロトコルなどがデバッグされるとき。どういうわけか見つけることができませんでした。 何かが私の目を引きましたが、それはすべてではありませんでした(またはそれは、しかし非常に支払われました)。



そして突然、私は考えました-なぜ私は自分でそのようなプログラムを書かないのですか? 実際、すべてのビジネス-読み取り/書き込み、オープン/クローズ機能では、外部ファイルへの書き込み機能を追加します。 そこで、#defineを使用して、これらの機能が不要な場合は無効にします。



これと並行して、ローカルネットワークとグローバルネットワーク上のプロセス間でメモリ内のデータを交換するための便利なクラスをいくつか作成しました。 また、この交換をデバッグする必要がありました。



そことそこの両方で、私は共通の論理プロトコルを使用しました-メッセージには禁止された文字があり、これはパケットの開始/終了のサインです。



そして今、アレクサンドレスクのこの本は私の目を引きます。 何かがここで何かをかき立てることができるという私の頭の中で鐘が鳴りました...



実際、この同じ論理プロトコルを実装する単一の基本クラスを作成したかったのです。 このクラスは、交換の物理的性質(USB、プロセス間交換(Windowsで、メモリマッピングが使用された)、ローカルエリアネットワーク交換(名前付きパイプ)、グローバル交換(ソケット))も指定する必要があります。 デバッグ機能も作りたかった。 私もしたかった...



問題の声明



そのため、次のプロパティ(または「ポリシー」)で設定される基本クラスを作成する必要があります。





そして最も重要なことは、最初の2つのポリシーのみが重要です-通信の性質と通信イベントの処理方法です。 その他はすべて無視できます。 したがって、「空の」ポリシーがあります-何もしません。



はい、もう1つ-このクラスは普遍的なソリューションであると主張していません。 異なるメソッドやクラスを動的に接続することについては話していません。 すべてのグローバル設定は、プログラムの1か所に実装する必要があります-そして、作業中ずっと変わらないようにします。



どうした



その結果、InterConnectテンプレートクラスが作成されました。

template < typename Phys, // physical layer of connection - shared memory, USB, RS-232, LAN... typename Call, // call mode - function style, thread with events typename Prot = NoStrategy, // protection layer - protection based on critical sections, multiprocess protection etc. typename Log = NoStrategy, // logging mode - no logging (default), to file, to external process typename Timer = NoStrategy // timer mode - no timer (default), based on GetTickCount, high frequency timer, RDTSC > class InterConnect { protected: Phys _phys; Call _call; Prot _prot; Log _log; Timer _timer; // ... };
      
      







クラスの本文では、このポリシーが使用されているかどうかを常に確認しています。 たとえば、バイト書き込み機能の実装方法は次のとおりです。

UPD-例外があり、削除されました



 template <typename Phys, typename Call, typename Prot, typename Log, typename Timer> bool InterConnectStorage<Phys,Call,Prot,Log,Timer>::send_byte (const unsigned char Val) { byte val; bool res = true; Prot::ProtectByte pr (_prot); // ... res &= _call.send (_phys, Val); // ... if (Log::to_use ()) { if (Timer::to_use ()) { _timer.stamp (); _log.send (Val, _timer.dprev (), _timer.dstart (), res); } else _log.send (Val, 0, 0, res); return res; } }
      
      







ここではすべてが明らかなようです。 「保護」ポリシーから、バイト保護機能を呼び出します(他の場所では、パケット全体が保護されます)。 「交換組織」ポリシーから呼び出し関数を呼び出します(オブジェクト「物理的性質」、実際にはバイトを渡します)。 次に、「レポートレコード」ポリシーからチェックを呼び出します。このポリシーはありますか? その場合、情報をレポートに書き込みます。 「カウントダウン」ポリシーが使用される場合、時間に基づいて書き込みます(「イベント」マークをスタンプ機能で配置し、レポートに書き込みます)、いいえ-単にレポートに書き込みます。



どこにでもチェックがあります-このポリシーは使用されていますか? 静的関数bool to_use(void)はこの質問に答えます。 静的で非常にシンプルなため、コンパイラはコードを最適化できます-まったく作成しないか(ポリシーを使用しない場合)、チェックせずにすぐに呼び出します。



その結果、非常に面倒なC ++コードがアセンブラーで非常にシンプルで最適なものに変わります。



使用する



その結果、非常に少ないコードで構成された非常に便利なツールが手に入りました。



小さなUSB共有プログラムの例を次に示します。



 typedef InterConnectStrat::FTDIAccess Phys; //   typedef InterConnectStrat::ThreadCall <Phys> Call; //   typedef InterConnectStrat::CritSectProtectPack Prot; //   typedef InterConnectStrat::NoStrategy Timer; //    #if defined (MakeLogFile) typedef InterConnectStrat::TabulatedASCIILogFile Log; //    File _log; //   #else typedef InterConnectStrat::NoStrategy Log; //    #endif InterConnectStorage <Phys, Call, Prot, Log, Timer> _con; //   
      
      







MakeLogFile定数を使用して、プログラムのロジックを完全に変更します。 もちろん、再コンパイルには時間がかかりますが、結果のコードは非常に効果的です-速度とリソースの両方、および機能の両方の面で。



このコードでは、パッケージ間の間隔は重要ではありませんが、このプログラムの開発の開始時には必要でした。 typedef / * ... * /タイマーのみを変更しました-そして、すべてが設定されました。



途中で、メッセージに対するプログラムの反応を調べました。 これを行うために、交換の性質をUSBからプロセス間に変更しました。 必要なパッケージを送信する2つ目のコンソールシンプルなアプリケーションを作成しました。 そして、メインプログラムの反応を正しいものに調整しました。 それから、再びtypedef Physを変更し、プログラムの1か所(最大5行)で通信パラメーターを設定しました-そしてすべてがUSBで動作し始めました。 非常にシンプルで便利です!



また、パケットの開始/終了(必要に応じて、マルチタスク環境で保護モードを設定します)、バイトの送信など、主な操作をオーバーロードしました。その結果、パケットは次のように記述されます。



 // .   _con++; _con <<= SetProgramDate; _con <<= _HEX_date.get_Unix_time (); --_con; // .     _con++; _con <<= GotoProgram; --_con;
      
      







コードはそれ自体を物語っています。



ソリューションの美しさをお楽しみください! 初期化ポイントを除いて、プログラム全体で、私はプログラム全体が何であれ、プログラムがマルチタスクであるかどうか、レポートにエントリがあるかどうか、どのような交換プロトコルかには無関心です。 リストは非常に簡単です!



ただし、すべての種類のクラスファクトリなどとは異なり、結果のコードは最適です。不要なチェックや遷移はありません。



他の方法の比較



そして今、楽しみのために、C ++ 9xを使用して同じ問題を解決する他の方法を検討してください。 私が見逃したことをコメントで教えてください:







一般に、このツールは非常に便利で高品質であることが判明しました。 完全に異なるプレーンで機能を開発できます。



PS Alexandrescu-よくやった、何と言っても!



All Articles