17

UML 结构图中 C/C++ 函数指针 (fp) 的最佳表示是什么?

我正在考虑使用界面元素,即使“退化”也可能会受到最多声明一个操作的约束。

我在本文档中找到了一些建议:C 和 UML 同步用户指南,第 5.7.4 节。但这听起来很麻烦,在实践中也不是很有用。即使从非常低级别的语义角度来看也是如此。下图简要展示了他们的概念: 在此处输入图像描述

恕我直言,C 和 C++ 函数指针被用作接口的狭窄视图,它只提供一个函数及其签名。在 C fp 中也将用于实现更复杂的接口,声明一个包含一组函数指针的结构。

我想我什至可以设法让我的特定 UML 工具(Enterprise Architect)转发生成正确的代码,并与代码更改同步而不会造成伤害。

我的问题是:

  1. 将 fp 声明为 UML 中接口元素的一部分是否会提供正确的语义视图?
  2. 单 fp 声明应该使用什么样的构造型?至少我需要在代码中提供一个 typedef,所以这将是我的胆量选择。(我发现这个原型是 Enterprise Architect 专有的),我需要定义一个合适的原型来适应代码生成。实际上我选择了刻板印象名称“代表”,这是否有任何含义或语义冲突?
  3. 至于 C++,是否会在类元素中嵌套一个“委托”原型接口,足以正确表达类成员函数指针?

这是我对 C 语言表示的想法的示例图: 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 工具及其特定代码工程功能的最佳方法。因此,我添加了标签。

4

7 回答 7

10

EA 的帮助文件对函数指针的主题有以下说法:

导入 C++ 源代码时,Enterprise Architect 忽略函数指针声明。要将它们导入您的模型,您可以创建一个 typedef 来定义函数指针类型,然后使用该类型声明函数指针。以这种方式声明的函数指针作为函数指针类型的属性导入。

注“能”。这是来自 C++ 部分,C 部分根本没有提到函数指针。所以它们没有得到很好的支持,这当然是由于建模和编程社区之间的差距:UML 根本不支持非平凡的语言概念,因此任何解决方案都必然是特定于工具的。

我的建议有点牵强,而且有点hacky,但我认为它应该工作得很好。

因为在 UML 中操作不是一流的,不能用作数据类型,所以我的回应是为它们创建一流的实体——换句话说,将函数指针类型定义为类。

这些类有两个用途:类名将反映函数的类型签名,以使程序员在图中看起来很熟悉,而一组标记值将表示实际参数和返回类型以用于代码生成。

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 类型。

有了这个,您可以将函数指针类型定义为一个 «funptr» 类,其名称类似于“long(*)(char)”,用于接受 char 并返回 long 的函数。

在“retval”标签中,选择您在步骤 4 中定义的“long”类。

手动添加“par1”标签,如上选择“char”类。

您现在可以将此类用作属性或参数的类型,或 EA 允许类引用的任何其他位置(例如在不同的 «funptr» 类的“par1”标签中;这使您可以轻松地为其中一个参数本身就是函数指针类型的函数)。

这里最骇人听闻的一点是编号的“par1”-“parN”标签。虽然在 EA 中可以定义多个具有相同名称的标签(您可能必须更改标签值窗口选项才能看到它们),但我认为您无法在代码生成脚本中检索不同的值(即使你可以我认为不一定会保留顺序,并且参数顺序在C中很重要)。所以你需要事先决定参数的最大数量。在实践中不是一个大问题;设置说 20 个参数应该足够了。

这种方法对逆向工程没有帮助,因为 EA 9 不允许您自定义逆向工程过程。然而,即将到来的 EA 10(目前在 RC 1 中)将允许这样做,虽然我自己没有看过它,所以我不知道这将采取什么形式。

在此处输入图像描述

于 2012-12-08T11:52:37.063 回答
2

函数指针的定义超出了 UML 规范的范围。更重要的是,它是许多 UML 建模软件不支持的特定于语言的特性。因此,我认为您对第一个问题的一般回答建议避免使用此功能。您提供的技巧仅与 Enterprise Architect 相关,并且与其他 UML 建模工具不兼容。以下是一些其他 UML 软件支持函数指针的方式:

MagicDraw UMLC++FunctionPtr对 FP 类成员使用 << >>构造型,C++FunctionSignature对函数原型使用 << >>。

代码示例(取自官方网站——参见“Modeling typedef and function pointer for C++ code generation”viewlet):

class Pointer
{
    void (f*) ( int i );
}

对应的UML模型:

MagicDraw UML 中的函数指针

Objecteering使用相应的 C++ TypeExpr 注释定义 FP 属性。

IBM 的Rational Software Architect不支持函数指针。用户可以将它们添加到用户定义部分中生成的代码中,这些部分在代码->UML 和 UML->代码转换期间保持不变。

于 2012-12-05T17:31:36.843 回答
1

对我来说似乎是正确的。我不确定您是否应该深入了解描述单个函数指针的类型和关系的低级细节。我通常发现描述一个接口就足够了,不需要分解它的内部元素。

于 2012-12-01T21:00:47.000 回答
1

我认为你可以用一个类虚拟地包装函数指针。我认为 UML 不必是代码的蓝图级别,记录概念更重要。

于 2012-12-08T11:57:43.553 回答
1

我已经能够与 Enterprise Architect 合作。它有点笨拙的解决方案,但它满足了我的需求。我做了什么:

  1. 创建一个名为 FuncPtr 的新类原型。我在这里遵循了指南: http ://www.sparxsystems.com/enterprise_architect_user_guide/10/extending_uml_models/addingelementsandmetaclass.html 当我这样做时,我为配置文件创建了一个新视图。所以我可以将它包含在我的主要项目之外。

  2. 修改了类代码模板。基本上选择 C ​​语言并从 Class Template 开始,然后点击“Add New Stereotype Override”并添加 FuncPtr 作为新的覆盖。

  3. 将以下代码添加到该新模板中:

%PI="\n"%
%ClassNotes%
typedef %classTag:"returnType"% (*%className%)(
%list="Attribute" @separator=",\n" @indent="    "%
);
  1. 修改了属性声明代码模板。和以前一样,添加一个新的 Stereotype

  2. 将以下代码添加到新模板中:

%PI=""% %attConst=="T" ? “常量”:“”%

%attType%

%attContainment=="通过引用" ? "*" : ""%

%attName%

这就是我在 Enterprise Architect 中获得函数指针所需要做的一切。当我想定义一个函数指针时,我只是:

  1. 创建普通班级
  2. 使用我想要的返回类型添加标签“returnType”
  3. 为参数添加属性。

这样,它将创建一个新类型,该类型可以作为属性或参数包含在其他类(结构)和运算符中。我没有将它本身设为运算符,因为那样它就不会在工具内部被引用为您可以选择的类型。

所以它有点hacky,使用特殊的原型类作为函数指针的typedef。

于 2014-11-04T20:06:42.573 回答
1

我的感觉是您希望将 UML 接口映射到 struct-with-function-pointers C 习惯用法。

Interface1 是模型中的重要元素。到处声明函数指针对象类型会使您的图表难以辨认。

Enterprise Architect 允许您指定自己的代码生成器。寻找代码模板框架。您应该能够借助一两个新的构造型来修改预先存在的 C 代码生成器。

于 2012-12-11T12:51:05.000 回答
0

像您的第一个示例一样,我将使用分类器,但将其隐藏在配置文件中。我认为他们已经将它包括在内以清楚地解释这个概念;但在实践中,刻板印象的整个想法是将细节抽象到配置文件中,以避免“噪音”问题。EA 非常适合处理配置文件。

我与您的第一个示例的不同之处在于我将分类原始类型刻板印象而不是数据类型刻板印象。数据类型是一个域范围对象,而原始类型是一个原子元素,其语义定义在 UML 范围之外。这并不是说你不能添加注释,尤其是在配置文件中,或者给它一个非常清晰的原型名称,比如 functionPointer。

于 2012-12-06T17:40:01.170 回答