Unresolved externals and link errors

Can anybody help me to solve these problems?
1> aokts.cpp

1>functions.obj : error LNK2005: "char const * const askSaveChanges" (?askSaveChanges@@3PBDB) already defined in filefunctions.obj
1>functions.obj : error LNK2005: "char const * const szTitle" (?szTitle@@3PBDB) already defined in filefunctions.obj
1>aokts.obj : error LNK2005: "char const * const askSaveChanges" (?askSaveChanges@@3PBDB) already defined in filefunctions.obj
1>aokts.obj : error LNK2005: "char const * const szTitle" (?szTitle@@3PBDB) already defined in filefunctions.obj
1>filefunctions.obj : error LNK2019: unresolved external symbol "void __cdecl SetSaveState(struct HWND__ *,unsigned int)" (?SetSaveState@@YAXPAUHWND__@@I@Z) referenced in function "void __cdecl FileOpen(struct HWND__ *,bool,int)" (?FileOpen@@YAXPAUHWND__@@_NH@Z)
1>aokts.obj : error LNK2001: unresolved external symbol "void __cdecl SetSaveState(struct HWND__ *,unsigned int)" (?SetSaveState@@YAXPAUHWND__@@I@Z)
1>.\aokts.exe : fatal error LNK1120: 1 unresolved externals

I include files like this:
aokts.cpp:
 
#include "aokts.h" 


aokts.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
#ifndef AOKTS_H
#define AOKTS_H

#include "../util/utilio.h"
#include "../util/zlibfile.h"
#include "../util/settings.h"
#include "../model/scen.h"
#include "../model/TrigXmlVisitor.h"
#include "../model/TrigXmlReader.h"
#include "editors.h"
#include "editorsDlgProc.h"
#include "mapview.h"
#include "../util/winugly.h"
#include "utilui.h"

#include <commdlg.h>
#include "LCombo.h"
#include "../resource.h" // must be included after Windows stuff
#include <ctype.h>
#include <fstream>

#include "strings.h"
#include "functions.h"
#include "filefunctions.h"

#endif	// AOKTS_H 


strings.h
1
2
3
4
#ifndef AOKTS_STRINGS_H
#define AOKTS_STRINGS_H
// HERE I INCLUDE ALL STRINGS const char * or const char array
#endif //AOKTS_STRINGS_H 


notice that askSaveChanges is defined in strings.h

notice: I added some comments because I got messages here that some classes had been already defined
functions.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
#ifndef AOKTS_FUNCTIONS_H
#define AOKTS_FUNCTIONS_H
/*
#include "../util/utilio.h"
#include "../util/zlibfile.h"
*/
#include "../util/settings.h"

#include "../model/scen.h"
/*
#include "../model/TrigXmlVisitor.h"
#include "../model/TrigXmlReader.h"
*/
#include "editors.h"
#include "editorsDlgProc.h"
// #include "mapview.h"
#include "../util/winugly.h"
#include "utilui.h"

#include <commdlg.h>
#include "LCombo.h"
#include "../resource.h" // must be included after Windows stuff
#include <ctype.h>
#include <fstream>

#include "strings.h"
inline void SetSaveState(HWND window, UINT state);

file menu items.
void UpdateRecentMenu(HMENU menu);

#endif	//AOKTS_FUNCTIONS_H 


filefunctions.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
#ifndef AOKTS_FILEFUNCTIONS_H
#define AOKTS_FILEFUNCTIONS_H

#include "../util/utilio.h"
#include "../util/zlibfile.h"
#include "../util/settings.h"
#include "../model/scen.h"
#include "../model/TrigXmlVisitor.h"
#include "../model/TrigXmlReader.h"
#include "editors.h"
#include "editorsDlgProc.h"
#include "mapview.h"
#include "../util/winugly.h"
#include "utilui.h"

#include <commdlg.h>
#include "LCombo.h"
#include "../resource.h" // must be included after Windows stuff
#include <ctype.h>
#include <fstream>

#include "strings.h"
#include "functions.h"
const char *getFilenameFromPath(const char *path);
void FileSave(HWND sheet, bool as, bool write);
void FileOpen(HWND sheet, bool ask, int recent);
void FileClose(HWND sheet, HWND control);
#endif 
You should not have any variables in header files. As headers are simply pasted in place of inclusion it will lead to multiple definitions if header is used more than once.
I have no variables in header files but const chars in strings.h ... so you mean I have to move it? But where? For example
const char extSave[] = ... defined in strings. I s needed by filefunctions.cpp:
ofn.lpstrFilter = extSave;
and more const strings is there.
Either make them static (with internal linkage) or move them to cpp file and declare them extern in header http://en.cppreference.com/w/cpp/keyword/extern
http://en.cppreference.com/w/cpp/language/storage_duration
Last edited on
There is written:
The extern specifier is only allowed in the declarations of objects and functions

do they count them as objects?
Yes, they are objects.
You should not have any variables in header files.

I do put const variables (aka consts) in headers, tho'. (Of smaller types.)

If askSaveChanges, etc are fixed strings, you should make them actually const. As consts have static storage by default in C++ it will eliminate the linker errors.

char const * const askSaveChanges = "Save changes?";

(a const pointer to a const string).

Given the error message I expect they're defined as

const char* askSaveChanges = "Save changes?";

(a non-const pointer to a const string)

Andy
Last edited on
Exactly:
1
2
const char * askSaveChanges =
"Do you want to save your changes?";


MiiNiPaa:
Do you mean to add in header:

 
extern const char * askSaveChanges;


and in cpp:
1
2
const char * askSaveChanges =
"Do you want to save your changes?";

??


The complete file is here:
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
#ifndef AOKTS_STRINGS_H
#define AOKTS_STRINGS_H

// Strings
const char * askSaveChanges =
"Do you want to save your changes?";

#if (GAME == 2)

const char *szTitle = "SWGB Trigger Studio";
const char welcome[] =
"Welcome to SWGBTS! Please open a scenario or make a new one.";
const char warnNoVersionChange[] =
"Warning: SWGBTS cannot yet convert between versions. Saving an sc1"
" as an scx will only change the extension!";
const char extOpen[] =
"All Scenarios (*.scx, *.sc1)\0*.scx;*.sc1\0All files (*.*)\0*.*\0";
const char extSave[] =
"Scenarios (*.scx)\0*.scx\0Expansion Scenarios (*.sc1)\0*.sc1\0All files\0*.*\0";
const char datapath[] = "data_swgb.xml";
const char warnNoAOEII[] =
"You don't seem to have Star Wars: Galactic Battlegrounds installed. I don't know how useful this will be.";

#elif (GAME == 1)

const char *szTitle = "AOK Trigger Studio";
const char welcome[] =
"Welcome to AOKTS! Please open a scenario or make a new one.";
const char warnNoVersionChange[] =
"Warning: AOKTS cannot yet convert between versions. Saving an scx"
" as an scn will only change the extension!";
const char extOpen[] =
"All Scenarios (*.scn, *.scx)\0*.scn;*.scx\0All files (*.*)\0*.*\0";
const char extSave[] =
"Scenarios (*.scn)\0*.scn\0Expansion Scenarios (*.scx)\0*.scx\0All files\0*.*\0";
const char datapath[] = "data_aok.xml";
const char warnNoAOEII[] =
"You don't seem to have Age of Empires II installed. I don't know how useful this will be.";

#else

#error unsupported game

#endif

#endif //AOKTS_STRINGS_H 



And and how to do it with this one:
1
2
const char welcome[] =
"Welcome to SWGBTS! Please open a scenario or make a new one.";


should it be like that?
1
2
char const const welcome[] =
"Welcome to SWGBTS! Please open a scenario or make a new one.";


or just
1
2
char const welcome[] =
"Welcome to SWGBTS! Please open a scenario or make a new one.";

or
1
2
const char welcome[] =
"Welcome to SWGBTS! Please open a scenario or make a new one.";


I don't see the difference
Last edited on
Putting

char const * const askSaveChanges = "Do you want to save your changes?";

in your header will handle a fixed string.

Modern compilers will even eliminate the duplicates (if string pooling is enabled, which is done by default for optimized builds with MSVC, and I would expect GCC and Clang do it, too.)

I did use

1
2
// header
extern const char * askSaveChanges;


1
2
3
// one C++ file
const char * askSaveChanges =
"Do you want to save your changes?";


in "ye olden days" (i.e. pre string pooling and whole program optimization.)

Andy


Andy OK OK but there are not just pointers to const but const char arrays:

1
2
const char welcome[] =
"Welcome message...";


There is not pointer at all

Edit:
Ah, I got it. So the only change needed was to pointer to const char
Last edited on
Do you have how to solve the last message?

filefunctions.obj : error LNK2019: unresolved external symbol "void __cdecl SetSaveState(struct HWND__ *,unsigned int)" (?SetSaveState@@YAXPAUHWND__@@I@Z) referenced in function "void __cdecl FileOpen(struct HWND__ *,bool,int)" (?FileOpen@@YAXPAUHWND__@@_NH@Z)
1>aokts.obj : error LNK2001: unresolved external symbol "void __cdecl SetSaveState(struct HWND__ *,unsigned int)" (?SetSaveState@@YAXPAUHWND__@@I@Z)
1>.\aokts.exe : fatal error LNK1120: 1 unresolved externals

inline void SetSaveState(HWND window, UINT state) is defined in functions.cpp and is used in filefunctions.cpp and aokts.cpp
Last edited on
Edit

Just registered the inline

Remove it!

1
2
3
void SetSaveState(HWND window, UINT state)
{
    // etc 


should work.

How is it declared in header?

And defined in the .cpp file? (Just need to function header.)


Andy
Last edited on
Andy,
I have removed the inline from header and cpp and it works now, thanks!

But why is it? Why I cannot have the inline there?
One more question.

if in aokts.cpp is defined:
 
Scenario scen;


and in functions.cpp I need scen:
 
scen.accept(TrigXmlVisitor(textout));


should I define
extern Scenario scen;
in functions.h?
Why I cannot have the inline there?

Inline is an instruction to the compiler to do exactly that: put the function implementation inline (effectively copy and paste its code wherever it's used) rather than use a call to a function elsewhere. The compiler can only do this when it knows the implementation of the function.

inline functions are usually defined in header files. If you do put them in a .cpp file then only the functions in that .cpp, and which are defined after it, can use it.

should I define
extern Scenario scen;
in functions.h?

If scen is a global variable (or rather, is declared at file scope) in aokts.cpp then adding the declaration to functions.h will allow your function in functions.cpp to use it.

But it would be better to the eliminate the global and pass it as a parameter to the function which need to use it.

Andy

What is C++ inline functions
http://www.cplusplus.com/articles/2LywvCM9/
Last edited on
The program is already designed to work with scen as global. But now I found in one file this:
 
extern class Scenario scen;

so I should add the keyword class there too.

I have moved the definition of:
1
2
3
4
5
6
7
inline void SetSaveState(HWND window, UINT state)
{
	HMENU menu = GetMenu(window);
	EnableMenuItem(menu, ID_FILE_SAVE, state);
	EnableMenuItem(menu, ID_FILE_SAVE_AS, state);
	EnableMenuItem(menu, ID_FILE_SAVE_AS2, state);
}

to the header of function.h and now it works nice.

Thanks.
Last edited on
Topic archived. No new replies allowed.