UML representation for C/C++ function pointers
Asked Answered
A

7

18

What would be the best representation of a C/C++ function pointer (fp) in an UML structural diagram?

I'm thinking about using an interface element, may be even if 'degenerate' with the constraint of having at most a single operation declared.

I found some proposal in this document: C and UML Synchronization User Guide, Section 5.7.4. But this sounds quite cumbersome and not very useful in practice. Even if right from a very low level of semantic view. Here's a diagram showing their concept briefly: enter image description here

IMHO in C and C++ function pointers are used as such a narrowed view of an interface which only provides a single function and it's signature. In C fp's would be used also to implement more complex interfaces declaring a struct containing a set of function pointers.

I think I can even manage to get my particular UML tool (Enterprise Architect) to forward generate the correct code, and synchronizing with code changes without harm.

My questions are:

  1. Would declaration of fp's as part of interface elements in UML proivde a correct semantic view?
  2. What kind of stereotype should be used for single fp declaration? At least I need to provide a typedef in code so this would be my guts choice.(I found this stereotype is proprietary for Enterprise Architect) and I need to define an appropriate stereotype to get the code generation adapted. Actually I have chosen the stereotype name 'delegate', does this have any implications or semantic collisions?
  3. As for C++, would be nesting a 'delegate' sterotyped interface with in a class element enough to express a class member function pointer correctly?

Here's a sample diagram of my thoughts for C language representation: C language interface and function pointer implementation

This is the C code that should be generated from the above model:

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;

EDIT:
The whole problem boils down what would be the best way with the UML tool at hand and its specific code engineering capabilities. Thus I have added the tag.

Atlee answered 1/12, 2012 at 20:31 Comment(6)
@garzanti A primitive datatype if your question is serious, I've mentioned this (follow the linked document).Packing
I was very serious, but indeed rhetorical and not make fun of you or anyone else. I simply believe that sometimes we have to return to the basics.Truc
@Truc At least I was confused reading german here. But anyway, I see that using an uml:DataType would look similar to my proposal, unfortunately I can't nest DataTypes inside classes with my UML tool (which is propably right) and would need extra typedefs, which makes the diagram less clear of it's intend.Packing
@Truc I have to correct myself: A function pointer is not a <<primitive>> but a <<dataType>>.Packing
I think this approach is wrong in the outset. Why would you need to model function pointer objects?Wylie
@PedroLamarão Mainly to get fine grained control over code generation of the UML tool, curiosity is another aspect.Packing
S
10

EA's help file has the following to say on the subject of function pointers:

When importing C++ source code, Enterprise Architect ignores function pointer declarations. To import them into your model you could create a typedef to define a function pointer type, then declare function pointers using that type. Function pointers declared in this way are imported as attributes of the function pointer type.

Note "could." This is from the C++ section, the C section doesn't mention function pointers at all. So they're not well supported, which in turn is of course due to the gap between the modelling and programming communities: non-trivial language concepts are simply not supported in UML, so any solution will by necessity be tool-specific.

My suggestion is a bit involved and it's a little bit hacky, but I think it should work pretty well.

Because in UML operations are not first-class and cannot be used as data types, my response is to create first-class entities for them - in other words, define function pointer types as classes.

These classes will serve two purposes: the class name will reflect the function's type signature so as to make it look familiar to the programmer in the diagrams, while a set of tagged values will represent the actual parameter and return types for use in code generation.

0) You may want to set up an MDG Technology for steps 1-4.

1) Define a tagged value type "retval" with the Detail "Type=RefGUID;Values=Class;"

2) Define a further set of tagged value types with the same Detail named "par1", "par2" and so on.

3) Define a profile with a Class stereotype "funptr" containing a "retval" tagged value (but no "par" tags).

4) Modify the code generation scripts Attribute Declaration and Parameter to retrieve the "retval" (always) and "par1" - "parN" (where defined) and generate correct syntax for them. This will be the tricky bit and I haven't actually done this. I think it can be done without too much effort, but you'll have to try it. You should also make sure that no code is generated for "funptr" class definitions as they represent anonymous types, not typedefs.

5) In your target project, define a set of classes to represent the primitive C types.

With this, you can define a function pointer type as a «funptr» class with a name like "long(*)(char)" for a function that takes a char and returns a long.

In the "retval" tag, select the "long" class you defined in step 4.

Add the "par1" tag manually, and select the "char" class as above.

You can now use this class as the type of an attribute or parameter, or anywhere else where EA allows a class reference (such as in the "par1" tag of a different «funptr» class; this allows you to easily create pointer types for functions where one of the parameters is itself of a function pointer type).

The hackiest bit here is the numbered "par1" - "parN" tags. While it is possible in EA to define several tags with the same name (you may have to change the tagged value window options to see them), I don't think you could retrieve the different values in the code generation script (and even if you could I don't think the order would necessarily be preserved, and parameter order is important in C). So you'd need to decide the maximum number of parameters beforehand. Not a huge problem in practice; setting up say 20 parameters should be plenty.

This method is of no help for reverse engineering, as EA 9 does not allow you to customize the reverse-engineering process. However, the upcoming EA 10 (currently in RC 1) will allow this, although I haven't looked at it myself so I don't know what form this will take.

enter image description here

Shaker answered 8/12, 2012 at 11:52 Comment(4)
Thanks @Uffe, that sounds very feasible to get the code generation right and shows clearly what type of thing this is in diagrams. I think getting the par1 to parN tags needs to have a certain limitation in number that will have to apply in the code generation template.Packing
Yes, each parX would require its own snippet of template code, and deleting for instance "par2" in a three-parameter funptr class will cause problems, which is why I say it's a bit hacky. If you want to go the whole hog, you could of course write an Add-In to help keep the structures intact.Shaker
When I'm going to generate code for attributes or parameters that refer to a <<funptr>> can I (need I) access anything else than the class name? For now I'm just using class name and replace the (*) with (*<param/attr-name>), also a bit hacky ...Packing
Hello, I like the way you describe implementing it. However, I only did steps 2, 3, & 5, while stuck with the rest. May I ask you please to refine the description e.g. step-by-step with snapshots. Thanks a lot !Guddle
O
2

Defining of function pointers is out of scope of UML specification. What is more, it is language-specific feature that is not supported by many UML modeling software. So I think that the general answer to your first question suggests avoiding of this feature. Tricks you provided are relevant to Enterprise Architect only and are not compatible with other UML modeling tools. Here is how function pointers is supported in some other UML software:

MagicDraw UML uses <<C++FunctionPtr>> stereotypes for FP class members and <<C++FunctionSignature>> for function prototype.

Sample of code (taken from official site -- see "Modeling typedef and function pointer for C++ code generation" viewlet):

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

Corresponding UML model:

Function pointers in MagicDraw UML

Objecteering defines FP attributes with corresponding C++ TypeExpr note.

Rational Software Architect from IBM doesn't support function pointers. User might add them to generated code in user-defined sections that are leaved untouched during code->UML and UML->code transformations.

Obstinacy answered 5/12, 2012 at 17:31 Comment(1)
Yes, I agree insofar that representation of this language specific feature depends on what's best supported for your specific tool. But IMHO digging down, there is a reasonable UML representation for function pointers, delegates and alike. The DataType approach from the linked document (well, also tool specific), describes it quite well I think.Packing
O
1

Seems correct to me. I'm not sure you should dive into the low-level details of descripting the type and relation of your single function pointer. I usually find that description an interface is enough detalization without the need to decompose the internal elements of it.

Outcurve answered 1/12, 2012 at 21:0 Comment(0)
M
1

I think you could virtually wrap the function pointer with a class. I think UML has not to be blueprint level to the code, documenting the concept is more important.

Meaghanmeagher answered 8/12, 2012 at 11:57 Comment(0)
A
1

My feeling is that you desire to map UML interfaces to the struct-with-function-pointers C idiom.

Interface1 is the important element in your model. Declaring function pointer object types all over the place will make your diagrams illegible.

Enterprise Architect allows you to specify your own code generators. Look for the Code Template Framework. You should be able to modify the preexisting code generator for C with the aid of a new stereotype or two.

Apiculture answered 11/12, 2012 at 12:51 Comment(5)
Thanks for the hints. I'm already deeply in tweaking EA's CTF. The question wasn't so much about the interface to struct-with-function pointers idiom, this is easy to handle with code generation. It was more about how to model single fp's that are used as callbacks for instance.Packing
I see. Since UML has no primitive for delegates you would have to produce a new class for each callback type.Wylie
@Pedro_Lamarão 'Since UML has no primitive for delegates ...' Yup, that's the point. UML has no caption of pointer types in general, though they could be usually modeled using a UML:dataType, which is finally the 'right way' IMHO. But I have difficulties to get this view handled right with EA's code generation and reverse engineering facilities. That's why at least I decided to go with Uffe's proposal, because it provides the 'easiest' way to use for EA C/C++ class models and gives a proper visual hint in viewing fp's used as parameters or attributes.Packing
I don't think a dataType is adequate in light of the behaviour you are modelling. You are not modelling pointers, but callbacks, whose form is the same as the Command pattern. You will want to describe that with the generalization relationship between an abstract callback type and it's various specializations. The fact these particular types must be represented as pointer types in C is a detail of the "implementation". Again, you can cause your code generator do Do The Right Thing if callback type hierarchies are annotated with some clever stereotype.Wylie
I must admit, though, we have now entered terrain unknown to me in practice.Wylie
A
1

I have been able to get something sort of working with Enterprise Architect. Its a bit of a hacky solution, but it meets my needs. What I did:

  1. Create a new class stereotype named FuncPtr. I followed the guide here: http://www.sparxsystems.com/enterprise_architect_user_guide/10/extending_uml_models/addingelementsandmetaclass.html When I did this I made a new view for the profile. So I can keep it contained outside of my main project.

  2. Modified the Class code templates. Basically selecting the C language and start with the Class Template and hit the 'Add New Stereotype Override' and add in FuncPtr as a new override.

  3. Add in the following code to that new template:

%PI="\n"%
%ClassNotes%
typedef %classTag:"returnType"% (*%className%)(
%list="Attribute" @separator=",\n" @indent="    "%
);
  1. Modified the Attribute Declaration code template. Same way as before, adding in a new Stereotype

  2. Add in the following code to the new template:

%PI=""% %attConst=="T" ? "const" : ""%

%attType%

%attContainment=="By Reference" ? "*" : ""%

%attName%

That's all that I had to do to get function pointers in place in Enterprise Architect. When I want to define a function pointer I just:

  1. Create a regular class
  2. Add in the tag 'returnType' with the type of return I want
  3. Add in attributes for the parameters.

This way it'll create a new type that can be included as attributes or parameters in other classes (structures), and operators. I didn't make it an operator itself because then it wouldn't have been referenced inside the tool as a type you can select.

So its a bit hacky, using special stereotyped classes as typedefs to function pointers.

Aide answered 4/11, 2014 at 20:6 Comment(2)
Can you give any improvement, this actually generates the code in question, and will be reverse engineered properly?Packing
You should note, this question was already answered well (I finally protected it right know from further answers). If you really want to contribute, please edit your answer showing diagram samples, generated code, etc.Packing
T
0

Like your first example I would use a Classifier but hide it away in a profile. I think they've included it for clarity of the explaining the concept; but in practice the whole idea of stereotypes is abstract away details into profiles to avoid the 'noise' problem. EA is pretty good for handling Profiles.

Where I differ from your first example is that I would Classify the Primitive Type Stereotype not the Data Type stereotype. Data Type is a Domain scope object, while Primitive Type is an atomic element with semantics defined out side the scope of UML. That is not to say you cannot add notes, especially in the profile or give it a very clear stereotype name like functionPointer.

Towelling answered 6/12, 2012 at 17:40 Comment(1)
Primitive types cannot have operations, so I wouldn't be able to specify the necessary operation signature, that differentiates various function pointers. I already started a profile for the stereotypes I introduce in my 2nd example, I cannot really handle the dataType stereotype correctly with code generation with EA.Packing

© 2022 - 2024 — McMap. All rights reserved.