Two way header inclusion problem

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Evening all,

 

It's been a long day, and week - and I know I'm simply being stupid beyond belief right now... Got a problem with 2 way header inclusion, which started as an issue in a large project - but I've broken the example down into it's absolute simplest form, and can still reproduce the problem.

 

We have an arbitrary file with an array definition in it (file1.c), the constant NUMBER comes from file2.h, which is in turn included in file1.h.

 

/*
 * file1.c
 */ 

#include "file1.h"

char array[NUMBER][10] = {

	"test",

};
/*
 * file1.h
 */ 

#ifndef FILE1_H_
#define FILE1_H_

#include "file2.h"

char array[NUMBER][10];

#define ANOTHER_NUMBER	20

#endif /* FILE1_H_ */
/*
 * file2.c
 */ 

#include "file2.h"

char another_array[ANOTHER_NUMBER][10] = {

	"test",

};
/*
 * file2.h
 */ 

#ifndef FILE2_H_
#define FILE2_H_

#include "file1.h"

#define NUMBER 10

char another_array[ANOTHER_NUMBER][10];

#endif /* FILE2_H_ */

 

But, it won't build - with the following errors 

'NUMBER' undeclared here (not in a function)
'ANOTHER_NUMBER' undeclared here (not in a function)

What am I doing!? We have a complex nested project folder structure, and I initially thought we had an issue with -I directories, but having flattened this all out... clearly not!

 

Any help greatly appreciated.

 

This topic has a solution.
Last Edited: Fri. Nov 27, 2020 - 11:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As to why you would want arrange things this way is beyond me! Do yourself a favour and avoid circular references. Also avoid having data visible outside of its compilation unit/global data - write acessor functions.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Apart from anything else you appear to have forgotten to use 'extern' in the two places where it's needed.

 

Also consider putting things like your array dimensions in another "config.h" or similar then you can include that in each .h that needs to know one or more of the numbers.

Last Edited: Fri. Nov 27, 2020 - 12:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So where, exactly, do those errors occur ?

 

I concur with Kartman - it seems like bad organisation to have the array declaration in one header depend on the number definition in another and vice versa.

 

Probably time to review the partitioning of stuff...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Things like this should have been resolved at the design stage before you started coding anyway.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks all,

 

Firstly; I concur on all points:

 

  • In the project where this example was extracted from, there are many hundreds of variables - almost all of which are local to their TUs and accessible only via get/write functions
  • The two example arrays in this example are indeed extern in the main project, poor re-typing of the example - however, adding an extern declaration does not change the compilation errors
  • Generally agree of course with low coupling, high cohesion - there are very few circular links in the project, and a thoroughly designed layer system with full hardware abstraction, platform independent application layer etc

 

It just so happens we have by necessity, this one circular reference - that until today when the array dimensions were implicit integers has functioned as designed for months, only now replacing the integers with constants has this issue come about.

 

So - architecture concerns aside, and extern added back in - I'm stumped?

Edit - maybe I need to re-phrase the question; architecture best-practice aside, is it not possible to compile a circular reference between headers, despite those headers being protected from recursive inclusion via header guards, or #pragma once?

Last Edited: Fri. Nov 27, 2020 - 02:04 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

jtw_11 wrote:
architecture best-practice aside, is it not possible to compile a circular reference between headers, d

It can be done but is usually much more trouble than it's worth and then of course, becomes a nightmare to support.

 

For your trivial example I might write a new header called ArraySizes.h.

 

/*
 * ArraySizes.h
 */ 

#ifndef ARRAYSIZES_H_
#define ARRAYSIZES_H_

#define NUMBER	        10
#define ANOTHER_NUMBER	20

#endif /* ARRAYSIZES_H_ */

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jtw_11 wrote:
What am I doing!?

When you're stumped with a preprocessor issue like this, it's time to inspect the preprocessor output.

 

http://www.8052mcu.com/forum/rea...

 

You haven't said what compiler you're using, so you'll have to look-up the necessary options yourself.

 

For GCC, it's -save-temps

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

clawson wrote:
consider putting things like your array dimensions in another "config.h" or similar then you can include that in each .h
N.Winterbottom wrote:
For your trivial example I might write a new header called ArraySizes.h.
Seems we agree. As far as I can see this is the solution isn't it?

 

A "kludgey" workaround would be to re-order your .h so:

#include "file2.h"

char array[NUMBER][10];

#define ANOTHER_NUMBER	20

becomes:

#define ANOTHER_NUMBER	20

#include "file2.h"

char array[NUMBER][10];

and

#include "file1.h"

#define NUMBER 10

char another_array[ANOTHER_NUMBER][10];

becomes:

#define NUMBER 10

#include "file1.h"

char another_array[ANOTHER_NUMBER][10];

but surely:

// config.h
#define NUMBER 10
#define ANOTHER_NUMBER 20

then:

// file1.h

#include "config.h"
#include "file2.h"

extern char array[NUMBER][10];

and

// file2.h

#include "config.h"
#include "file1.h"

extern char another_array[ANOTHER_NUMBER][10];

is "better" all round ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
consider putting things like your array dimensions in another "config.h" or similar then you can include that in each .h

N.Winterbottom wrote:
For your trivial example I might write a new header called ArraySizes.h.

What they said!

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
For GCC, it's -save-temps

 

GCC indeed, thanks.

 

Thanks all, will concede to a re-design - an oddly ordered header doesn't feel like much joy to me, and whilst we have shared headers in the project, I can't justify adding a standalone header for two includes given the above discussion.