Identifier not found even with forward declarations

I have a project consisting of several files. This is their interdependency:

Header interdependencies
1
2
3
4
5
6
7
8
9
10
main.cpp -> all below
io.h ->  classes.h
classes.h -> globals.h, io.h, colmanip.h, console.h
console.h -> none
colmanip.h -> none
globals.h -> none
glossary.h -> globals.h, classes.h
locale.h -> globals.h
vector -> none
word.h -> globals.h


I know that to avoid identifiers not being recognised I need to forward declare my definitions (and sometimes variables using extern). Relevant header where I forward declare/put my prototypes is io.h (definitions reside in io.cpp).

I will try to distill all the code to the most important parts and omitt those unimportant.

io.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef IO_H
#define IO_H

#include <needed-stuff>

#include "classes.h" // for ent::lib_entry & ent::save_entry classes used in prototypes below\
// io.cpp is linked with io.h by including io.h

// ...... relevant function prototypes, defined in io.cpp
bool read_file_library(std::ifstream& stream, std::vector<ent::lib_entry>& vector_glossaries); // lib_entry defined in classes.h yet - UNDECLARED IDENTIFIER
char press_button();
bool is_number(const std::string& check_string);
bool if_secure_open(std::ifstream& stream, const std::string dir, const int message_if_fail);
size_t lines_count(std::ifstream& stream);

#endif 


Some other relevant parts of code, namely colmanip.h (which doesn't have its .cpp counterpart and changes text colour) and globals.h (where I keep const/non-const vars for many files to use):

colmanip.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*Header file to colour text and background in windows console applications
code snippet by Eklavya Sharma, cplusplus.com/articles/Eyhv0pDG
Global variables - textcol,backcol,deftextcol,defbackcol,colourprotect*/

#pragma once // use pragma, include guards or both? I guess neither actually protects the code from being included in different files (just from multiple inclusions in one file).
#ifndef _INC_EKU_IO_CONCOL
#define _INC_EKU_IO_CONCOL

#include <needed-stuff>
// no accompanying colmanip.cpp, all code is here

namespace colourmanip
{

#ifndef CONCOL
#define CONCOL
	enum concol
	{
		// ...
	};
#endif //CONCOL

	// globals defined like so
	HANDLE std_con_out;

	// inline functions supposedly protect me from multiple definition error
	inline void update_colours()
	{
		// ...
	}
	
	template<class elem, class traits>
	inline std::basic_ostream<elem, traits>& operator<<(std::basic_ostream<elem, traits>& os, concol col)
	{
		// ...
	}
}	//end of namespace eku

#endif	//_INC_EKU_IO_CONCOL  


globals.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef GLOBALS_H
#define GLOBALS_H

#include <needed-stuff>
// globals.cpp is linked with globals.h by including globals.h

// I think constants have interal-linkage by default, is it true? Should I make them "const char" and move definitions to globals.cpp?
const char [100] sth = "...........";
// ......
// non-constants have external-linkage by default if they appear in file scope, so I guess extern is not needed here?
extern int settings[SETT_OPTION_COUNT]; // defined in globals.cpp

// enum definitions within namespaces to avoid ambiguity
namespace error {
	enum save_error {
		....
	};
}
// ......

// struct definitions like so
struct glossary {
	// .....
};
// ......

#endif GLOBALS_H 


I also have some classes that have global scope and their definitions are located in classes.h. However, problem arises when I want to do either of these things:

1. Call functions inside the class definition (like if_secure_open() - see code comments)
2. Use newly defined classes in function prototypes (like read_file_library() in io.h - see code comments)

(other questions that arose during writing this code are included in the code comments, please look at them as well, thank you kindly :) )

classes.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#ifndef CLASSES_H
#define CLASSES_H

#include <needed-stuff>

#include "globals.h" // FORWARD DECLARATIONS OF struct glossary
#include "io.h" // FORWARD DECLARATIONS/PROTOTYPES OF if_secure_open() USED IN ent::lib_entry.get_data()
#include "colmanip.h" // CONSOLE COLOUR MANIPULATION USED IN ent::lib_entry.format_content();
#include "console.h" // forward declarations of simple functions like console_width() or reset_stream()
// no accompanying classes.cpp, all code is here

namespace ent {
	class lib_entry {
	public:
		// couple of variables, most relevant one:
		glossary var; // structure from globals.h

		void get_data(std::ifstream& stream, lib_entry& entry) {
			std::getline(stream, entry.data.name);
			std::ifstream f_line_count;
			if (if_secure_open(f_line_count, entry.data.name, NULL)) { // if_secure_open as identifier not found - I INCLUDED io.h where it is forward declared!
				// ....
			}
			else {
				entry.data.status = error::save_error::not_found;
			}
		}
		std::ostringstream* format_content(lib_entry& entry, unsigned int cardinal, unsigned int columns) {
			// ...
			std::ostringstream oss;
			oss << colourmanip::red << "...."; // colourmanip namespace from colmanip.h
			pressbutton(); // function forward declared/prototyped in io.h
			// ...
		}
	};
	class save_entry {
	public:
		// some vars ...
		error::save_error internal_error; // again, enum error::save_error defined in globals.h

		void get_data(std::ifstream& stream, save_entry& entry) {
			// .....
			if (entry.internal_error != error::save_error::corrupted) {
				for (int u = 1; u < SAVE_DATA_COUNT; u++) {
					if (is_number(temp_str[u])) { // is_number forward declared in io.h and defined in io.cpp - yet IDENTIFIER UNKNOWN
						entry.vals[u - 1] = stoi(temp_str[u]);
					}
				}
			}
		}
	};
}

#endif CLASSES_H 


At some point I tried to move class definitions to its own .cpp file and leave only this in the header:
1
2
class lib_entry;
class save_entry;

as forward declarations, but it produced even more errors.

I would presume that including a header in another file is almost identical to physically copying the content of the header into a file, so those forward declarations should work, even if they are pasted into several files that use them (#pragma once doesn't protect against such pasting).

Those are some relevant errors I get while compiling (there are too many to show and some may stem from previous errors)

errrors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
1>word.cpp
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\classes.h(25): error C3861: 'if_secure_open': identifier not found
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\classes.h(26): error C3861: 'lines_count': identifier not found
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\classes.h(92): error C3861: 'is_number': identifier not found
1>main.cpp
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\io.h(15): error C2653: 'ent': is not a class or namespace name
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\io.h(15): error C2065: 'lib_entry': undeclared identifier
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\io.h(15): error C2923: 'std::vector': 'lib_entry' is not a valid template type argument for parameter '_Ty'
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\io.h(15): error C3203: 'allocator': unspecialized class template can't be used as a template argument for template parameter '_Alloc', expected a real type
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\main.cpp(43): error C2664: 'bool read_file_library(std::ifstream &,std::vector &)': cannot convert argument 2 from 'std::vector<ent::lib_entry,std::allocator<_Ty>>' to 'std::vector &'
1>        with
1>        [
1>            _Ty=ent::lib_entry
1>        ]
1>c:\users\pawil\documents\visual studio 2017\projects\one\one\main.cpp(84): error C2039: 'score': is not a member of 'ent::lib_entry'
Last edited on
You have a circular dependency.

io.h includes classes.h.
classes.h includes io.h.

This will not work because if io.h is included it will also include classes.h which will in turn try to include io.h but the include guard (#ifndef IO_H) prevents the content of io.h from being included a second time so you end up compiling the code for ent::lib_entry and ent::save_entry before the functions in io.h have been declared.

To fix this problem I think you should define the member functions of ent::lib_entry and ent::save_entry in classes.cpp instead of classes.h. That way you no longer need include io.h in classes.h, same with console.h and colmanip.h. Instead you include these headers from classes.cpp where they are needed.
Last edited on
For future users stumbling upon this thread and looking for solutions, I highly advise checking this article - it's very helpful (Peter87's answer was insightful too):

[url]http://www.cplusplus.com/articles/Gw6AC542/[/url]
Topic archived. No new replies allowed.