0

Let's say I have a stacked 2 layer app (High Layer (HL) and Low Layer (LL)) that is implemented in C.
HL defines a few #defines.
HL calls a LL function with a parameter that takes values of the #define.
LL wants to check these values and proceed accordingly.
Now my question is what is the best way to share these #defines.

Option 1: Duplicate them in the LL.
Cons: Introduces duplicity.

Option 2: Include the header of the HL layer.
Cons: LL layer shouldn't have to know about the HL

Option 3: Break the shared #defines of HL and LL into a new header file shared_defs.h which both of them include.
Cons: Not sure, does this also break encapsulation? I feel like header will be a bit standalone.

Which is the best option?

HL.h  
#define INIT_STATE (0)
#define RUN_STATE (1)

void HL_Init(void);

HL.c
#include "HL.h"
#include "LL.h"
void HL_Init(void) {
    LL_Init(INIT_STATE);
    LL_Init(RUN_STATE;
}

LL.h
void LL_Init(int state);

LL.c
void LL_Init(int state) {
    if (state == INIT_STATE) {
        do sth
    } else {
        do sth else
    }
}
1
  • 2
    it really depends on the specifics. If you can give us some information about HL and LL, about the interfaces, and about the data defined with the #define, we might provide you with a better answer. I would say: normally, data is defined in the lower layers, and #included in upper layers, but in your case it is just a blind guess. For generic numbers, your option 3 is better. Commented May 20, 2019 at 12:33

1 Answer 1

0

Usually, implementation is done bottom-up. This means that each layer only depends on the lower layers, and lower layers do not depend on higher level layers.

From this point of view, you need to #define your data in the lower layer.


There is an exception to the rule above: if you need to define some generic numbers, which are nor specific to any layer. In that case, you define all data in an external header, and access it from any layer needed.


After you edited the question I can assume that the state-machine with the states INIT_STATE and RUN_STATE is implemented in the LL, so my answer still holds.


However, it is usually a much better approach to use an enum instead of #defines:

typedef enum tag_states
{
    INIT_STATE,
    RUN_STATE
} tStates;
4
  • Cool thanks. I added an example which agrees with what you say. If I have to include the bottom layer anyway to call the LL function the definitions might as well be in the lower layer. Commented May 20, 2019 at 12:47
  • I missed that argument, but it is very good observation. If the functions are defined in LL, the data should be defined in LL also. Commented May 20, 2019 at 12:49
  • I know was just lazy to write it properly. The problem with enums is that you are not sure of the width of the int (embedded systems background). Commented May 20, 2019 at 13:11
  • Especially in embedded systems, you must code as safe as possible. With #defines, nothing is safe. First, and the most important, you do not have automatic type checking when using #define's, but you have it when using typedef enum's. Commented May 20, 2019 at 13:17

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.