UML構造図でのC/C++関数ポインター(fp)の最良の表現は何でしょうか?
私はインターフェース要素を使用することを考えていますが、「縮退」したとしても、最大で1つの操作を宣言するという制約があります。
このドキュメントでいくつかの提案を見つけました: CおよびUML同期ユーザーガイド、セクション5.7.4 。しかし、これは非常に面倒に聞こえ、実際にはあまり役に立ちません。たとえ非常に低いレベルのセマンティックビューからでも。以下は、その概念を簡単に示す図です。
CおよびC++関数ポインタのIMHOは、単一の関数とそのシグネチャのみを提供するインターフェイスのそのような狭められたビューとして使用されます。 Cでは、fpは、関数ポインタのセットを含む構造体を宣言するより複雑なインターフェイスを実装するためにも使用されます。
特定のUMLツール(エンタープライズアーキテクト)に正しいコードを転送生成させ、コードの変更と無害に同期させることさえできると思います。
私の質問は次のとおりです。
これは、C言語表現についての私の考えのサンプル図です。
これは、上記のモデルから生成する必要があるCコードです。
struct Interface1;
typedef int (*CallbackFunc)(struct Interface1*);
typedef struct Interface1
{
typedef void (*func1Ptr)(struct Interface1*, int, char*);
typedef int (*func2Ptr)(struct Interface1*, char*);
typedef int (*func3Ptr)(struct Interface1*, CallbackFunc);
func1Ptr func1;
func2Ptr func2;
func3Ptr func3;
void* instance;
};
/* The following extern declarations are only dummies to satisfy code
* reverse engineering, and never should be called.
*/
extern void func1(struct Interface1* self, int p1, char* p2) = 0;
extern int func2(struct Interface1* self, char*) = 0;
extern int func3(struct Interface1* self, CallbackFunc p1) = 0;
編集:
問題全体は、手元にあるUMLツールとその特定のコードエンジニアリング機能を使用する場合の最善の方法を要約したものです。したがって、 enterprise-architect タグを追加しました。
EAのヘルプファイルには、関数ポインタに関して次のように書かれています。
C++ソースコードをインポートする場合、EnterpriseArchitectは関数ポインタ宣言を無視します。それらをモデルにインポートするには、typedefを作成して関数ポインター型を定義し、その型を使用して関数ポインターを宣言します。このように宣言された関数ポインタは、関数ポインタ型の属性としてインポートされます。
「できた」ことに注意してください。これはC++セクションからのものであり、Cセクションでは関数ポインタについてはまったく触れていません。そのため、これらは十分にサポートされていません。これはもちろん、モデリングコミュニティとプログラミングコミュニティの間のギャップが原因です。重要な言語の概念はUMLでサポートされていないため、ソリューションは必然的にツール固有になります。
私の提案は少し複雑で少しハッキーですが、かなりうまくいくはずだと思います。
UML操作はファーストクラスではなく、データ型として使用できないため、私の応答は、それらのファーストクラスエンティティを作成することです。つまり、関数ポインタ型をクラスとして定義します。
これらのクラスは2つの目的を果たします。クラス名は関数の型シグネチャを反映して、ダイアグラムでプログラマーにわかりやすくします。一方、タグ付き値のセットは、コード生成で使用する実際のパラメーターと戻り値の型を表します。
0)ステップ1〜4のMDGテクノロジーを設定することをお勧めします。
1)詳細「Type = RefGUID; Values = Class;」を使用してタグ付き値タイプ「retval」を定義します。
2)「par1」、「par2」などの名前の同じ詳細を持つタグ付き値タイプのセットをさらに定義します。
3)「retval」タグ付き値を含む(「par」タグを含まない)クラスステレオタイプ「funptr」でプロファイルを定義します。
4)コード生成スクリプトの属性宣言とパラメーターを変更して、「retval」(常に)と「par1」-「parN」(定義されている場合)を取得し、それらの正しい構文を生成します。これはトリッキーなビットになりますが、私は実際にはこれを行っていません。手間をかけずにできると思いますが、ぜひお試しください。また、「funptr」クラス定義はtypedefではなく匿名型を表すため、コードが生成されないようにする必要があります。
5)ターゲットプロジェクトで、プリミティブCタイプを表すクラスのセットを定義します。
これにより、関数ポインタ型を、charを取り、longを返す関数の「long(*)(char)」のような名前の"funptr"クラスとして定義できます。
「retval」タグで、手順4で定義した「long」クラスを選択します。
「par1」タグを手動で追加し、上記のように「char」クラスを選択します。
このクラスを属性またはパラメーターのタイプとして、またはEAがクラス参照を許可する他の場所(別の"funptr"クラスの "par1"タグなど)で使用できるようになりました。これにより、のポインタータイプを簡単に作成できます。パラメータの1つがそれ自体が関数ポインタ型である関数)。
ここで最もハッキーなビットは、番号が付けられた「par1」-「parN」タグです。 EAでは同じ名前の複数のタグを定義することは可能ですが(タグ付けされた値ウィンドウのオプションを変更して表示する必要がある場合があります)、コード生成スクリプトで異なる値を取得することはできないと思います(順序が必ずしも保持されるとは思わないかもしれません。C)ではパラメーターの順序が重要です。したがって、パラメータの最大数を事前に決定する必要があります。実際には大きな問題ではありません。たとえば、20個のパラメータを設定するだけで十分です。
EA 9ではリバースエンジニアリングプロセスをカスタマイズできないため、この方法はリバースエンジニアリングには役立ちません。ただし、今後のEA 10(現在はRC 1)でこれが可能になりますが、自分で調べたことがないため、これがどのような形式になるかはわかりません。
関数ポインタの定義はUML仕様の範囲外です。さらに、多くのUMLモデリングソフトウェアでサポートされていないのは言語固有の機能です。したがって、最初の質問に対する一般的な回答は、この機能を回避することを示唆していると思います。指定したトリックはエンタープライズアーキテクトにのみ関連し、他のUMLモデリングツールとは互換性がありません。他のUMLソフトウェアで関数ポインタがどのようにサポートされているかを次に示します。
MagicDrawUMLは、FPクラスメンバーには<< C++FunctionPtr
>>ステレオタイプを使用し、関数プロトタイプには<< C++FunctionSignature
>>を使用します。
コードのサンプル( 公式サイト -「C++コード生成のためのtypedefと関数ポインターのモデリング」ビューレットを参照):
class Pointer
{
void (f*) ( int i );
}
対応するUMLモデル:
Objecteeringは、対応するC++ TypeExprノートでFP属性を定義します。
IBMのRational Software Architectは、関数ポインターをサポートしていません。ユーザーは、コード-> UMLおよびUML->コード変換中に変更されないままのユーザー定義セクションで生成されたコードにそれらを追加する場合があります。
関数ポインタをクラスで仮想的にラップできると思います。 UMLはコードの青写真レベルである必要はないと思います。概念を文書化することがより重要です。
私の考えでは、UMLインターフェースをstruct-with-function-pointersCイディオムにマップしたいと思っています。
Interface1は、モデルの重要な要素です。関数ポインタオブジェクトタイプをあちこちで宣言すると、ダイアグラムが判読できなくなります。
Enterprise Architectを使用すると、独自のコードジェネレーターを指定できます。 コードテンプレートフレームワーク を探します。新しいステレオタイプを1つか2つ使用して、Cの既存のコードジェネレーターを変更できるはずです。
私には正しいようです。単一の関数ポインターの型と関係を記述するための低レベルの詳細に飛び込む必要があるかどうかはわかりません。私は通常、インターフェースの記述は、その内部要素を分解する必要なしに十分な詳細化であることに気付きます。
私はエンタープライズアーキテクトと何らかの形で連携することができました。それは少しハッキーな解決策ですが、それは私のニーズを満たしています。私がしたこと:
FuncPtrという名前の新しいクラスステレオタイプを作成します。ここのガイドに従いました: http://www.sparxsystems.com/enterprise_architect_user_guide/10/extending_uml_models/addingelementsandmetaclass.html これを行ったとき、プロファイルの新しいビューを作成しました。だから私はそれを私のメインプロジェクトの外に封じ込めておくことができます。
クラスコードテンプレートを変更しました。基本的にC言語を選択し、クラステンプレートから始めて、[新しいステレオタイプオーバーライドの追加]をクリックし、FuncPtrを新しいオーバーライドとして追加します。
その新しいテンプレートに次のコードを追加します。
%PI="\n"% %ClassNotes% typedef %classTag:"returnType"% (*%className%)( %list="Attribute" @separator=",\n" @indent=" "% );
属性宣言コードテンプレートを変更しました。以前と同じように、新しいステレオタイプを追加します
次のコードを新しいテンプレートに追加します。
%PI = "" %% attConst == "T"? "const": ""%
%attType%
%attContainment == "参照による"? "*": ""%
%attName%
EnterpriseArchitectで関数ポインタを配置するために私がしなければならなかったのはこれだけです。関数ポインタを定義したいときは、次のようにします。
このようにして、他のクラス(構造)および演算子の属性またはパラメーターとして含めることができる新しい型を作成します。選択可能なタイプとしてツール内で参照されなかったため、演算子自体にはしませんでした。
したがって、関数ポインタへのtypedefとして特別なステレオタイプ化されたクラスを使用するのは少しハッキーです。
最初の例のように、分類子を使用しますが、プロファイルでは非表示にします。概念の説明を明確にするために、それらが含まれていると思います。しかし実際には、ステレオタイプの全体的な考え方は、「ノイズ」の問題を回避するために詳細をプロファイルに抽象化することです。 EAはプロファイルの処理に非常に適しています。
最初の例と異なるのは、プリミティブ型ステレオタイプではなくデータ型)を分類することです。 ステレオタイプ。データ型はドメインスコープオブジェクトですが、プリミティブ型はUMLのスコープ外で定義されたセマンティクスを持つアトミック要素です。これは、特にプロファイルにメモを追加したり、functionPointerのような非常に明確なステレオタイプ名を付けたりできないということではありません。