Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Help with the design of a code with object containment !

Fahim_F_
Novice
393 Views

Hello,

I would like to make a class "A" which contains other classes "A_contained_1" and "A_contained_2"  (they may also contain other classes, e.g., like "A_contained_1" contains "A_contained_1_contained"). Lets say each of the contained classes  "A_contained_1" and "A_contained_2" have several methods, which for every method you may have two or more formula (F1 and F2) which you want to implement and they do exactly the same thing.  Once you implemented all the classes with their methods, you want to instantiate an object "A" which its "A_contained_1" runs with F1 implementation and another object "A" which its "A_contained_1" runs with F2 implementation and so on. You clearly see that the number of possibilities can be very large.

An example of such a system but a very simple one is given below:

Problem: Consider I have a class "Calculator" which contains class "Engine". Engine has a function to generate two numbers "a" and "b". We want the possibility to generate number with two different formula, "method_1" and "method_2". "Calculator" has a method called "Multiply" which multiplies the numbers "a" and "b" generated by "Engine". Now by the choice of the user I want to make a calculator which the numbers are generated by  "method_1" or "method_2" of the engine, How do I design the code?? I really want to avoid passing flags !

Solution: I define a class "engine" and extend it to two classes "engine_1" with "method_1" implememtation and and "engine_2" with "method_2" implementation. Then I define a class calculator which has polymmorphic pointer to "class (engine)". Based on the choice of user, I define an engine of "engine_1" or "engine_2" and point the pointer to that. Therefore, my calcuator uses the apprpriate method based on the user selection! Please see the simple attached code!

pros: With this approach I do not need to pass some sort of flag every time I call the function of the calculator, so I may save some run time.

cons: What If I have another class "student" which contains calculator and becuase of some variety of the formula in calculator I need to also make several calculators? then with a similar approach I need to define the student with a pointer to calculator which makes things crazy in my opinion. Also, if I have 10 different formula in engine and 10 other in calculator, which there are just slightly different in one part, I need to extend engine and calcuator 10 times each which is kind of tedious!! 

Although I tried to resolve this issue myself I think I am not being very sucessfull. So any Idea how to design a code like this example in a more elegant way?

Thank you very much for your inputs

0 Kudos
7 Replies
Fahim_F_
Novice
393 Views

Ooops, I forgot to upload the fortran code. Here it is,

Thanks

0 Kudos
IanH
Honored Contributor II
393 Views

I don't quite follow what it is that you ultimately want to do, or perhaps more importantly, why you want to do things this way. What do the types represent?  What are their roles?

Some points:

- Relating to your comment "I do not need to pass some sort of flag ... so I may save some run time" - you may find that your approach is actually slower.  You would need to measure to make sure.  Regardless, I wouldn't be worrying about this sort of micro optimization at this stage - code clarity and an effective implementation (which needs to consider the reliability of compiler support) is far more important. 

- There are pitfalls associated with pointers, in particular around the validity of pointers to dummy arguments when the actual argument doesn't have the target attribute.  As a result, when calculator_construct completes, the cal_engine pointer component becomes undefined.  Is there a reason that you have made the cal_engine component a pointer?

- I know it is just an example, but don't forget implicit none.

0 Kudos
Fahim_F_
Novice
393 Views

I didnt quite get what you meant by "..., in particular around the validity of pointers to dummy arguments when the actual argument doesn't have the target attribute.  As a result, when calculator_construct completes, the cal_engine pointer component becomes undefined." I assign the pointer target in the constructor subroutine and in there, the object "eng" has a target attribute. Isnt this enough? Do you mean that I should also define my_engine_1 and my_engine_2 in the main program with a target attribute??

Regarding your question that why I did this, my answer is that simply becuase I couldnt do it otherwise. I would really like to see another way that you could make type "calculator" which contains type "engine" in it and "engine" has a method with two different formula. Most importantly that user can select to have calculator with an "engine" with formula_1 or 2. How would you design that code?

A possible real situation is for a simulation code. Assume that you want to write a simulation code and the model has thousands if not millions of grids. For each grid you want to compute four different groups of properties (lets say to form the Jacobian, etc), which are props_A, props_B, props_C and props_D, so, lets say I define four calcuators with an engine, one for each property. Then for the comuptation of each property in each property group, I have different correlations or formula, etc. For exmple for props_A_1, I have three formula and so on. Then the engine for each calculator may have several definition of one method (procedure) which corresponds to computing props_A_1. Now, one user may want to use the calculator of props_A which has an engine with formula_1 for props_A_1 for some gridblocks and another calculator of props_A with an engine with formula_2 for props_A_1 for other gridblocks. Another user wants some other combination of those options. So what would be a good design?

The problem with passing flags is that, for each gridblock at each iteration of each time step I need to compute all properties, etc. So a calculator will be called billions of times and it is just better if we could avoid performing  if-statements which come along with flags. And by the way, obviously, the reason for breaking up the code to many pieces is that so I can have all combinations with much less code repetition becuase I can combine objects. However, the problem is that this will require a lot of containments of superclasses and based on the above approach, it requires polymorphysm to pick the prper subclass.

Thanks for the hint about using implicit none, I always use it but I left it out here as I wanted to prepare sth fast.

 

0 Kudos
IanH
Honored Contributor II
393 Views

Yes - the variables in the main program need the target attribute.  Independent of that you should consider whether the cal_engine needs to be a pointer.  (Are the engines every likely to have data that is specific to a particular calculator object?).

You mention concerns about if statements and billions of calls.  Be mindful that some low level equivalent of if statements will still be going on behind the scenes with the approach being discussed here.  Fundamentally the code needs to choose at runtime a different execution path based on [a flag|the dynamic type of an object] - that requires some sort of [conditional branch|indirect call] in the generated assembly.  It used to be the case (probably ancient history now) that the indirect call was slower.

Apologies, I still don't understand your problem domain.  All I can say is to try an break your problem down into the "things" that exist, and then describe he characteristics of those things and how they interact with other things - that can then assist you define the components and bindings of your types and any inheritance hierarchy.

More comments on your original code - it looks like you could make the engine type abstract, with the num binding being deferred.  The engine type then defines what "engines" look like - they will be objects that have a num binding that has a particular interface.  If you have ten different ways that engines could implement num then you end up with ten different extensions.  You could also - eventually  - also dispense with the calculator_constructor procedure and just use the compiler provided structure constructor.  I say "eventually", because at the moment current (and beta :( ) ifort gets a bit confused.  I'm not clear what the role of the calculator type is from your code and the description above.

0 Kudos
Fahim_F_
Novice
393 Views

May I ask why the variables in the main program should also have target attribute?

I guess engine would not have data which are specific to a particular calculator object ? why? however, particular calculator object are being made specificly with specific engine(s).

I got your point about if statements. Also I recently got familair with abstract objects and ofcourse I should have used it as you modified the code. Thank you

I also think that I can make the code so that I pass the object engine as a parameter for the procedures of calcuator. Please see the attached file (and sorry that I modifed the old code as I did it before I read your previous comments). Any comments on this approach? is it better than the previous approach of using pointers? 

Thank you very much,

0 Kudos
IanH
Honored Contributor II
393 Views

When you have an actual argument that does not have the target attribute that is associated with a dummy argument that does, any pointers that are associated with the dummy argument during the procedure become formally undefined when the procedure finishes.

Whether passing as an argument is better is up to your judgement.  If it is something that may vary from call that would make sense.  If its more a characteristic of the calculator object itself, then leave it as a component.  If that characteristic is in the form of a reference - leave it as a pointer, if its more in the nature of being part of the value of the calculator itself, make it allocatable.  Up to you!

0 Kudos
Fahim_F_
Novice
393 Views

Thank you very much IanH!

0 Kudos
Reply