Every answer (from what I've seen, which is a lot) to this question on this site has been addressed in my case, and I'm still at an impasse. I'm working with legacy code, having to hack my way through setting up a properly connected development environment. Using VS 2012, I have a solution with 22 projects in it with a spiderweb of dependencies between them. 1 project, phtranscript, depends on code from another project hunspell, and phtranscript's code is in turn needed by a 3rd project speechRecognizer. Here are the associated files and the compiler/linker output:
In project phtranscript:
phTranscript.h:
#ifndef _phTranscript_h__
#define _phTranscript_h__
#include <vector>
#include <string>
#include <map>
#include "config.h"
#include "character.h"
...
class hunspellMorph{
public:
static hunspellMorph *instance();
protected:
hunspellMorph();
private:
hunspellMorph(const hunspellMorph &);
hunspellMorph& operator=(const hunspellMorph &);
public:
~hunspellMorph();
void Morph(const std::string &in,std::vector<std::string> &out);
private:
class impl;
impl *pImpl_;
};
#endif
hunspellMorph.cpp:
#include "phTranscript.h"
#include "hunspell.hxx"
#include <treeNode.h>
#include <string.h>
class hunspellMorph::impl{
private:
Hunspell hs;
public:
impl();
void Morph(const std::string &in,std::vector<std::string> &out);
};
void hunspellMorph::impl::Morph(const std::string &in,std::vector<std::string> &out){
char **slst;
int re = hs.analyze(&slst,in.c_str());
...
freelist(&slst,re);
}
hunspellMorph::hunspellMorph(){
pImpl_ = new impl();
}
hunspellMorph::~hunspellMorph(){
delete pImpl_;
}
....
In project hunspell:
hunspell.hxx:
#include "affixmgr.hxx"
#include "suggestmgr.hxx"
#include "csutil.hxx"
#include "langnum.hxx"
#define SPELL_COMPOUND (1 << 0)
#define SPELL_FORBIDDEN (1 << 1)
#define SPELL_ALLCAP (1 << 2)
#define SPELL_NOCAP (1 << 3)
#define SPELL_INITCAP (1 << 4)
#define MAXDIC 20
#define MAXSUGGESTION 15
#define MAXSHARPS 5
#ifndef _HUNSPELL_HXX_
#define _HUNSPELL_HXX_
class Hunspell
{
...
public:
Hunspell(const char * affpath, const char * dpath, const char * key = NULL);
~Hunspell();
int analyze(char ***slst,const char *word,int d=0);
...
};
#endif
csutil.hxx:
#ifndef __CSUTILHXX__
#define __CSUTILHXX__
// First some base level utility routines
#define NOCAP 0
#define INITCAP 1
#define ALLCAP 2
#define HUHCAP 3
#define HUHINITCAP 4
#define MORPH_STEM "st:"
#define MORPH_ALLOMORPH "al:"
#define MORPH_POS "po:"
#define MORPH_DERI_PFX "dp:"
#define MORPH_INFL_PFX "ip:"
#define MORPH_TERM_PFX "tp:"
#define MORPH_DERI_SFX "ds:"
#define MORPH_INFL_SFX "is:"
#define MORPH_TERM_SFX "ts:"
#define MORPH_SURF_PFX "sp:"
#define MORPH_FREQ "fr:"
#define MORPH_PHON "ph:"
#define MORPH_HYPH "hy:"
#define MORPH_PART "pa:"
#define MORPH_HENTRY "_H:"
#define MORPH_TAG_LEN strlen(MORPH_STEM)
#define MSEP_FLD ' '
#define MSEP_REC '\n'
#define MSEP_ALT '\v'
// default flags
#define DEFAULTFLAGS 65510
#define FORBIDDENWORD 65510
#define ONLYUPCASEFLAG 65511
typedef struct {
unsigned char l;
unsigned char h;
} w_char;
#define w_char_eq(a,b) (((a).l == (b).l) && ((a).h == (b).h))
...
// free character array list
void freelist(char *** list, int n);
#endif
hunspell.cxx:
#include "license.hunspell"
#include "license.myspell"
#ifndef MOZILLA_CLIENT
#include <cstdlib>
#include <cstring>
#include <cstdio>
#else
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#endif
#include "hunspell.hxx"
#include "./config.h"
#include "./treeNode.h"
#include "cache.h"
#include <string>
#include <vector>
#ifndef MOZILLA_CLIENT
#ifndef W32
using namespace std;
#endif
#endif
Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key)
{
...
}
Hunspell::~Hunspell()
{
...
}
int Hunspell::analyze(char ***slst,const char *word,int d){
...
}
csutil.cxx:
#include "license.hunspell"
#include "license.myspell"
#ifndef MOZILLA_CLIENT
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cctype>
#else
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#endif
#include "csutil.hxx"
#include "atypes.hxx"
#include "langnum.hxx"
#ifdef OPENOFFICEORG
# include <unicode/uchar.h>
#else
# ifndef MOZILLA_CLIENT
# include "utf_info.cxx"
# define UTF_LST_LEN (sizeof(utf_lst) / (sizeof(unicode_info)))
# endif
#endif
#ifdef MOZILLA_CLIENT
#include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h"
#include "nsIUnicodeEncoder.h"
#include "nsIUnicodeDecoder.h"
#include "nsICaseConversion.h"
#include "nsICharsetConverterManager.h"
#include "nsUnicharUtilCIID.h"
#include "nsUnicharUtils.h"
static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID);
#endif
#ifdef MOZILLA_CLIENT
#ifdef __SUNPRO_CC // for SunONE Studio compiler
using namespace std;
#endif
#else
#ifndef W32
using namespace std;
#endif
#endif
...
void freelist(char *** list, int n) {
if (list && (n > 0)) {
for (int i = 0; i < n; i++) if ((*list)[i]) free((*list)[i]);
free(*list);
*list = NULL;
}
}
And here's the output on a clean build between the different projects:
6>------ Build started: Project: hunspell, Configuration: Debug Win32 ------
...
6> hunspell.vcxproj -> C:\temp\speech\divided_rm_speech_proj\Debug\hunspell.exe
...
10>------ Build started: Project: phtranscript, Configuration: Debug Win32 ------
...
10> phtranscript.vcxproj -> C:\temp\speech\divided_rm_speech_proj\Debug\phtranscript.exe
...
19>------ Build started: Project: speechRecognizer, Configuration: Debug Win32 ------
...
19> Generating Code...
19>hunspellMorph.obj : error LNK2019: unresolved external symbol "void __cdecl freelist(char * * *,int)" (?freelist@@YAXPAPAPADH@Z) referenced in function "public: void __thiscall hunspellMorph::impl::Morph(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)" (?Morph@impl@hunspellMorph@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@4@@Z)
19>hunspellMorph.obj : error LNK2019: unresolved external symbol "public: __thiscall Hunspell::Hunspell(char const *,char const *,char const *)" (??0Hunspell@@QAE@PBD00@Z) referenced in function "public: __thiscall hunspellMorph::impl::impl(void)" (??0impl@hunspellMorph@@QAE@XZ)
19>hunspellMorph.obj : error LNK2019: unresolved external symbol "public: __thiscall Hunspell::~Hunspell(void)" (??1Hunspell@@QAE@XZ) referenced in function "public: __thiscall hunspellMorph::impl::~impl(void)" (??1impl@hunspellMorph@@QAE@XZ)
19>hunspellMorph.obj : error LNK2019: unresolved external symbol "public: int __thiscall Hunspell::analyze(char * * *,char const *,int)" (?analyze@Hunspell@@QAEHPAPAPADPBDH@Z) referenced in function "public: void __thiscall hunspellMorph::impl::Morph(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)" (?Morph@impl@hunspellMorph@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@4@@Z)
19>C:\temp\speech\divided_rm_speech_proj\Debug\speechRecognizer.exe : fatal error LNK1120: 4 unresolved externals
Keep in mind there is a lot of code taken out, so if something's missing that's important let me know and I'll update this description.
In Visual Studio 2012's environment setup, phtranscript's project properties have a hunspell reference established under common properties, the include directories field includes the hunspell include directory, and the library directories field includes the hunspell library output folder (this is all under VC++ Directories), the C/C++ additional include directories also lists the hunspell include directory. I left the Linker->Input additional libraries field alone since I get "already declared symbol" linker errors when I have both it and the VC++ directory specified. Under Project Dependencies I check hunspell and ensure it precedes phtranscript in the build order. Lastly, I have manually added the existing hunspell libraries (after they've been compiled) to the phtranscript project. In the speechrecognizer project, I've taken identical steps as they correspond to the phtranscript project dependencies.
The code is pretty much a nightmare imo. What're the minimal amount of changes needed to fix this? Preferably just by changing/adding something on the IDE side instead of code changes (although those are inevitable).
UPDATE:
All the projects were designated as applications under project properties. After changing them to static libraries (all but the 1 project meant to execute), the link errors became this:
21>speechRecognizer.lib(hunspellMorph.obj) : error LNK2019: unresolved external symbol "void __cdecl freelist(char * * *,int)" (?freelist@@YAXPAPAPADH@Z) referenced in function "public: void __thiscall hunspellMorph::impl::Morph(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)" (?Morph@impl@hunspellMorph@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@4@@Z)
21>speechRecognizer.lib(hunspellMorph.obj) : error LNK2019: unresolved external symbol "public: __thiscall Hunspell::Hunspell(char const *,char const *,char const *)" (??0Hunspell@@QAE@PBD00@Z) referenced in function "public: __thiscall hunspellMorph::impl::impl(void)" (??0impl@hunspellMorph@@QAE@XZ)
21>speechRecognizer.lib(hunspellMorph.obj) : error LNK2019: unresolved external symbol "public: __thiscall Hunspell::~Hunspell(void)" (??1Hunspell@@QAE@XZ) referenced in function "public: __thiscall hunspellMorph::impl::~impl(void)" (??1impl@hunspellMorph@@QAE@XZ)
21>speechRecognizer.lib(hunspellMorph.obj) : error LNK2019: unresolved external symbol "public: int __thiscall Hunspell::analyze(char * * *,char const *,int)" (?analyze@Hunspell@@QAEHPAPAPADPBDH@Z) referenced in function "public: void __thiscall hunspellMorph::impl::Morph(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > &)" (?Morph@impl@hunspellMorph@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@4@@Z)
21>C:\temp\speech\divided_rm_speech_proj\Debug\interface11.exe : fatal error LNK1120: 8 unresolved externals
These link errors wait until compiling the application at the very end to spring up, rather than when compiling speechrecognizer as an application. There are other unresolved linkages, but they seem independent (albeit related) to this problem.
Your output tells me, that both projects are compiled to *.exe files. I do not know how do you expect 'phtranscript' to use modules, that are part of 'hunspell', in this case.
If you create such dependencies, hunspell should be static (or dynamic) library, which phtranscript links to. I believe you DO set all dependencies correctly, but VS does not have anything to link together and that's why linker is so angry at you - it can not use *.exe as a dependency.
Simple solution: change 'hunspell' type to 'static library (.lib)' or 'dynamic library (.dll)' and setup 'phtranscript' to use it as an input library.