How to tag code with side effects?

I am working on a project on 8051 where every byte is counted. Thus, I use some global variables, where I usually did not. The usual way of passing pointers to a function adds too much overhead.

I have a number of functions that use single bit variables (a special extension for the compiler to C ) to signal the result of the function in addition to the normal return value.

bit global_error_flag = 0; bit global_data_ready_flag = 0; unsigned char A_Function (void) { // Do some stuff if ( badness ) { global_error_flag = 0; global_data_ready_flag = 1; return 0; } if ( data_is_ready_use ) { global_data_ready_flag = 1; } return a_value; } void Other_Function (void) { unsigned char c; c = A_Function(); if( global_error_flag) { // Do error stuff here. } else if( global_data_ready_flag ) { // Do data processing here. } global_error_flag = 0; global_data_ready_flag = 0; } 

Given that the technique is evil, is there a way to make the code more understandable?

What is the best way to specify which function calls will have a side effect of setting these flags? Are there enough comments? Should I name functions that indicate their API (quasi-Hungarian style)? Should I use a macro to mark such calls:

 #define FUNCTION_SETS_FLAGS(code) (code) FUNCTION_SETS_FLAGS( c = A_Function() ); 

Any other ideas?

+4
source share
9 answers

Your global tags are tagged for clarity, which is a good start.

Ideally, you want something that will not compile if you make a mistake. This means that macros and comments will not work.

I would stick to a naming convention for functions - not necessarily Hungarian, but something like A_Function_Returns_Flags or less verbose if you can think about it.

+3
source

Using an agreement, whether you want to call it โ€œHungarianโ€ or not, is the best way I can think of to mark this. Stylistically, some kind of naming prefix would be preferable to an empty #define, at least for me.

This is actually quite common, I think. I know that the S60 programming environment uses many common tags for functions to indicate that they throw exceptions, for example.

+6
source

I made my candidacy. on a similar issue in Java. I can tell you one thing that you should not do: do not rely on the documentation, because then you depend on who really reads it. You need to add a tooltip in the method name to indicate that the user should read the documents in order to learn about side effects. If you choose something and agree with it, you are likely to be more likely.

+3
source

If you just want to mention that the function affects the global variable (s), then a simple (Hungarian) prefix can help.

But if you want to mention every single flag (s) that it affects, then perhaps using a function header is probably the way to go. For instance,

  /************************************************************************* * FUNCTION : <function_name> * DESCRIPTION : <function description> * PARAMETERS : * Param1 - <Parameter-1 explanation> * Param2 - <Parameter-2 explanation> * Param3 - <Parameter-3 explanation> * RETURN : <Return value and type> * GLOBAL VARIABLES USED: * Global1 - <Global-1 explanation> * Global2 - <Global-2 explanation> * Global3 - <Global-3 explanation> *************************************************************************/ 
+3
source

This will not help you, but GCC has a way to do the opposite of what you want: mark functions that do not have side effects. See const and pure attributes. This is more for optimization than documentation, I thought: if the compiler knows that this function does not consider any data other than its arguments, it can perform more intelligent optimizations, such as loop-invariant code movement .

+2
source

You can use a macro to simulate a function to have more parameters:

 unsigned char _a_function(void); #define A_Function(ret_val) (*(ret_val) = _a_function(), !global_error_flag) ... unsigned char var; /* call the function */ if (!A_Function(&var)) { /* error! */ } else { /* use var */ var++; } 

I did not try to compile it, so I canโ€™t say that it will work, but I think it should be.

+2
source

First, I would try to encode it so that for each of these flags there is only one producer and only one consumer. Then I cleared / set the flag only when it was needed. As for specifying a side effect, there should be a fairly standard header on top of the function, doxygen style:

  // Function func // Does something // Consumes ready_flag and sets error_flag on error. int func() { if (ready_flag) { //do something then clear the flag if (some_error) error_flag = x; ready_flag = 0; } //don't mess with the flags outside of their 'scope' return 0; } 

On the other hand, if the error and readiness flags are mutually exclusive, you can use the byte (or bits inside the byte / register) to indicate the readiness or status of the error.

0 for error, 1 for unprepared / error-free and 2 for finished / error-free (or -1, 0, 1, independently)

IIRC, the standard 8051 instruction set does not work with single bits, so using an integer byte for (different) flags should not produce huge results.

0
source

If you have not already done so, you can also check the sdcc project on sourceforge , it is a C compiler specially designed for use in embedded development, which is also oriented to 8051, in addition, the compiler supports a number of custom, target and non-standard built-in built-in compilers for various cases use, also I personally found that the development team is very open to and respond to ideas for new improvements and other related feature requests.

0
source

If you really need to stick to these global variables, you can make it obvious that a function can modify them, waiting for a reference to them as arguments to the function:

 unsigned char A_Function (bit *p_error_flag, bit *p_data_ready_flag) { ... } 
-2
source

All Articles