Terminate activity diagram from subactivity - c

I´m trying to draw an UML activity diagram for a fnction that is (highly simplified) represented by the following code snippet. My intention is to have a subactivity for the lines that check the mode parameter (if-else).
ErrorType DoSomething(int mode) {
if(mode==MODE1) {
...
}
else {
return MODE_NOT_AVAILABLE;
}
SomethingElse...
return NO_ERROR;
}
You can see, the return-Statement in the else-Block leads to termination of function DoSomething. So if it´s executed, there is no way for SomethingElse... to be executed.
As I mentioned, this else-block should be in a subactivity.
How do I visualize that an action in a subactivity (return MODE_NOT_AVAILABLE) has the consequence that it´s parental activity diagram has to be in a final state?
In the following picture you can see my try to solve it. Is this a correct solution?

Since you are dealing with some kind of exception, I'd model it with an exception handler like you see here http://www.sparxsystems.com.au/images/screenshots/uml2_tutorial/ad11.GIF. Even though your concrete implementation uses if/else, that should be a way which makes it easy to understand what you want to achieve (prevent the subroutine from being executed in wrong mode).
You can see more details about the notation here: http://edn.embarcadero.com/article/30169
It depends on how much you want to dictate the actual implementation. UML itself is langage-unaware, and so are most stakeholders.

Related

How do you represent a function call as an if condition statement in Sequence Diagram?

I've been drawing a sequence diagram of a module recently, while reverse engineering.
I encountered a control statement, and it is like,
if (func_A() == True)
{
DoSomeThing();
}
else
{
DoSomeThingElse();
}
The problem is how to draw the condition?
As I mentioned, It is reverse engineering. The code cannot be modified now.
I drew two diagrams, and I don't know which way is right,
The first one is this, I think it's wrong because it doesn't show the function call as a message from A to B.
This is the second, It shows a message func_A.
What do you think about to do this right?
To complete the other answer there is anyway a problem in the second proposal because we do not know if in [func_A() == True] you reuse the value return by the previous call or you do a second call, to avoid that add the explicit return in your diagram :
Out of that do you know the activities ? A sequence diagram is "just" an interaction while an activity is a behavior and can be more adapted :
It depends. If func_A is an operation defined in Object2 the second representation would be correct. The first does not tell where the operation is defined. Most likely (!) one would interpret func_A as an operation local to ObjectA which your code seems to say. (Btw. you have two completely different object sets AB vs. 12 in your examples.) But that is uncertain. So the 2nd variant is more explicit (and correct).
In any case I advise to not overdo SDs with fragments as "graphical programming" doesn't make things easier to read (my practical experience). It's excellent to show message flows in various collaborations. But when it comes to conditions it's getting messy very soon. A better way is to create different sub-diagrams or even use pseudo code if there are too nested if conditions. In many cases such if clauses are a good fit for state machines.

Whats a Strong Argument against Variable Redundancy in c code

I work in safety critical application development. Recently as a code reviewer I complained against coding style shown below, but couldn't make a strong case against it. So what would be a good argument against such Variable redundancy/duplication, I am looking for cases where this might lead to problems or test cases which might fail, rather than just coding style.
//global data
// global data
int Block1Var;
int Block2Var;
...
//Block1
{
...
Block1Var = someCondition; // someCondition is an logical expression
...
}
//Block2
{
...
Block2Var = Block1Var; // Block2Var is an unconditional copy of Block1Var
...
}
I think a little more context would be helpful perhaps.
You could argue that the value of Block1Var is not guaranteed to stay the
same across concurrent access/modification. This is only valid if Block1Var
ever changes (ie is not only read). I don't know if you are concerned with
multi-threaded applications or not.
Readability is an important issue as well. Future code maintainers
don't want to have to trace around a bunch of trivial assignments.
Depends on what's done with those variables later, but one argument is that it's not future-proof. If, in the future, you change the code such that it changes the value of Block1Var, but Block2Var is used instead (without the additional change) later on, then this will result in erroneous behavior.
If the shown function context reaches a certain length (I'm assuming a lot of detail has been discarded to create the minimal reproducible example for this question), a good next step could be to create a new (sub-)function out of Block 2. This subfunction then should be started assigning Block1Var (-> actual parameter) to Block2Var (-> formal parameter). If there were no other coupling to the rest of the function, one could cut the rest of Block 2 and drop it as a function definition, and would only have to replace the assignment by the subfunction call.
My answer is fairly speculative, but I have seen many cases where this strategy helped me to mark useful points to split a complex function later during the development. Of course, this interpretation only applies to an intermediate stage of development and not to code that is stated to be "ready for release".

Infinite Loop caused by Append?

I have a bit of a question regarding why my code seems to hang when I run it. The code is for a project I have in a class, but we spent one class period going over Prolog so much of what I've learned is stuff I've searched around for and have taught myself. I do apologize if my code contains horrendous stylistic errors, but again, as we never formally learned how we 'should' use Prolog, this is based mostly on my own experimentation.
The goal of the segment of code I am writing is, more or less, to form a chain that connects one actor to another through a series of movies that they have been in.
I have a function I am calling that is meant to construct connections between a starting actor, all possible linked actors ending actor, and the list of movies that connects them. This is probably a horribly inefficient method of doing this, however implementing it this way solves two parts of the assignment with one segment of code.
The code that calls the function works, and for the sake of making this simpler to read, I will omit it unless asked to share it. In short, it asserts a globalStartingActor, and passes on two empty lists (ActorList = [] and MovieList = []) to a function doActorAssertions.
In turn, we have doActorAssertions. This is the revised version of it, which should be simplified and easier to read, but lacks the massive commenting that it had previously.
doActorAssertions(ActorsName,ActorList,MovieList) :-
isNotInList(ActorsName,ActorList) ->
(
findMoviesIn(ActorsName,MoviesIn),%finds all movies ActorsName is in
howLong(MoviesIn,LenMoviesIn),%Sees how many movies there are.
(
LenMoviesIn ==0;
(
append(ActorsName,ActorList,UpdatedActorList),%this causes errors!
globalStartingActor(GSAName),%asserted starting actor
assert(connectedActors(GSAName,ActorsName,MovieList)), %says that the GSAName is connected to ActorsName by a list of movies MovieList.
write(actorAsserted),
addAndTraverse(MoviesIn,UpdatedActorList,MovieList) %Goes to propegate all movies the actor is in, then actors in those movies, then recursively calls this function again.
)
)
),
true.
As I said previously, the append tag seemed to be the source of the error! This indeed appears to be the case when I simplify the code to what it is above. I simply comment that append out, and the code body works.
Why, then, is append preventing the code from working properly? I need to have append (or similar function) in that part of the code!
Is ActorsName a list? The variable' name suggests it is, as well as the usage in append/3, but then what isNotInList(ActorsName,ActorList) means? Partial or full disjunction? This could be the cause of the endless loop, maybe you should use the difference of those sets to increment the ActorList.
You should try to avoid assert/1, and instead pass around the state in variables. See this other answer for a schema doing something very similar to what you are attempting here.
This is useless, could be a typo, but then I don't understand the ->
...
),
true.
I think should read
...
); % note the semicolon!
true.

Is it a bad idea to mix bool and ret codes

I have some programs which make heavy use of libraries with enumerations of error codes.
The kind where 0(first value of enum) is success and 1 is failure. In some cases I have my own helper functions that return bool indicating error, in other cases I bubble up the error enumeration. Unfortunately sometimes I mistake one for the other and things fail.
What would you recommend? Am I missing some warnings on gcc which would warn in these cases?
P.S. it feels weird to return an error code which is totally unrelated to my code, although I guess I could return -1 or some other invalid value.
Is it a bad idea? No, you should do what makes sense rather than following some abstract rule (the likes of which almost never cater for all situations you're going to encounter anyway).
One way I avoid troubles is to ensure that all boolean-returning function read like proper English, examples being isEmpty(), userFlaggedExit() or hasContent(). This is distinct from my normal verb-noun constructs like updateTables(), deleteAccount() or crashProgram().
For a function which returns a boolean indicating success or failure of a function which would normally follow that verb-noun construct, I tend to use something like deleteAccountWorked() or successfulTableUpdate().
In all those boolean-returning cases, I can construct an easily readable if statement:
if (isEmpty (list)) ...
if (deleteAccountWorked (user)) ...
And so on.
For non-boolean-returning functions, I still follow the convention that 0 is okay and all other values are errors of some sort. The use of intelligent function names usually means it's obvious as to which is which.
But keep in mind, that's my solution. It may or may not work for other people.
In the parts of the application that you control, and the parts that make up your external API I would say, choose one type of error handling and stick to it. Which type is less important, but be consistent. Otherwise people working on your code will not know what to expect and even you yourself will scratch you head when you get back to the code in a year or so ;)
If standardizing on a zero == error scheme, you can mix and match both enum and bool if you construct your tests like this:
err = some_func();
if !err...
Since the first enum evaluates to zero and also the success case it matches perfectly with bool error returns.
However, in general it is better to return an int (or enum) since this allows for the expansion of the error codes returned without modification of calling code.
I wouldn't say, that it's a bad practice.
There's no need to create tons of enum-s, if you just need to return true/false, and you don't have other options (and true and false are explanatory enough ).
Also, if your functions are named OK, you will have less "mistakes"
For example - IsBlaBla - expects to return true. If you have [Do|On]Reload, a reload could fail for many reasons, so enum would be expected. The same for IsConnected and Connect, etc.
IMHO function naming helps here.
E.g. for functions that return a boolean value, is_foo_bar(...), or for functions that return success or an error code, do_foo_bar(...).

When to use assert() and when to use try catch?

In which situations do you use them?
Try... catch - for exceptional conditions, i.e. conditions which aren't caused by malformed code, but which may just alter the normal control flow by external unpredictable events.
Assertions for catching invalid code, i.e. checking if an invariant is held in the function, checking if an internal method is called with right arguments (for public API you might still want an exception for that), etc.
Those are my basic guidelines, but the conventions vary from situation to situation and from language to language.
When you're in doubt, you can ask yourself: is that specific safety check supposed to still be there in the release code, after we test and finish everything? If you answer "yes, it's still neccessary then", you probably want an exception. Otherwise, you probably want an assertion.
Normally assert() does not work in release code, so it can never replace a try-catch strategy. Nevertheless I like to use assert() in places where exceptions are thrown. For me (as a developer!), it is often more convenient to get by an assert() message to the line of failure than through the exception stack.
They are created for different purposes. Assert is more for finding bugs, try-catch is for handling exceptional situations.
The situations of try-catch and assert are totally different.
Assert is used to check if the value you have received, as parameter for example, is expected. I would not recommend to use assert in production code, it is used in unit-test mostly and rarely to check the parameters.
To check the passed values better to use something like:
public void test(int i) {
if (i < 0) {
throw new IllegalArgumentException("i cannot be less than 0");
}
...
}
Try-catch block is used when you know something inside the block can go wrong. For example, you write to an sdcard and there is no space for writing. Or, it happened that you try to read the array out of it bounds. Then, you put your critical code in try-catch block and check for the excpetions:
try {
InputStream is = new FileInputStream("filename.txt");
...
} catch FileNotFoundExcpetion {
System.out.println("file not found");
} finally {
...
}
More about exceptions and try-catch blocks.

Resources