Hi everyone,
I am using a singleton class called "Slipnet" which is defined in two files: slipnet.hpp and slipnet.cpp. (relevant source code included below)
Then, I am including Slipnet.hpp in my main.cpp file and calling Slipnet::getInstance() in the main function.
I keep getting the following error:
Code:
main.obj : error LNK2019: unresolved external symbol "public: static class Slipnet * __cdecl Slipnet::getInstance(void)" (?getInstance@Slipnet@@SAPAV1@XZ) referenced in function _main
Debug/integratedApp.exe : fatal error LNK1120: 1 unresolved externals
What am I missing?
Slipnet.hpp
Code:
#ifndef SLIPNET_H
#define SLIPNET_H
#include <string>
#include "ConceptNode.hpp"
#include <map>
#include <vector>
using namespace std;
// a directed graph where each vertex represents a concept
// and each edge represents the semantic connection from one
// conept to another. This is read from a text file that explains
// what concepts lead to what other concepts and to what degree.
// Codelets of certain type represent a concept of sorts and
// then can find themselves a node in the slipnet that matches
// their concept - from there, they know to codelets of what
// concept they can bind and they know what types of codelets to
// post to the coderack in case they manage to bind.
class Slipnet
{
private:
Slipnet();
static bool instanceFlag;
// totally static network, read once from file: /data/slip.net
static std::map<std::string, ConceptNode*> net;
//singleton instance.
static Slipnet* instance;
// facilitates traversal of the graph
static ConceptNode* getNode(std::string type);
// loads the file in "../../data/slip.net"
static bool loadFile(std::string filePath);
// helper functions to read the text file
static bool parseChildrenAndParents(ConceptNode* node, std::string& children, std::string& parents);
static void trim(std::string& str);
public:
static Slipnet* getInstance();
// these functions can be called externally to get the neighbors of a given concept so that
// new codelets can be generated accoring to the semantic slippage of ideas.
static std::vector<std::pair < std::string, double > > getPossibleChildren(std::string codeletType);
static std::vector<std::pair < std::string, double > > getPossibleParents(std::string codeletType);
};
#endif
Slipnet.cpp
Code:
#include "Slipnet.hpp"
#include <fstream>
using namespace std;
Slipnet* Slipnet::instance = NULL;
bool Slipnet::instanceFlag = false;
std::map<std::string, ConceptNode*> Slipnet::net;
Slipnet::Slipnet()
{
// do nothing
}
bool Slipnet :: loadFile(std::string filePath)
// each concept node in the file is set up like this:
// concept : child1(weight), child2(weight) .. childN(weight) : parent1(weight), parent2(weight) .. parentM(weight) ;
{
ifstream in;
in.open(&filePath[0]);
if(!in)
{
printf("error opening slipnet file not found in %s",filePath);
return false;
}
std::string line;
int lineNumber = 0;
while(getline(in,line))
{
line = line.substr(0,line.find_first_of("//"));
trim(line);
if(line.length() == 0)
{
lineNumber++;
continue;
}
std::string concept;
std::string children;
std::string parents;
// make sure there are exactly 2 ':'s and 1 ';'s.
int colonCounter = 0;
int semicolonCounter = 0;
int index = 0;
while(index < line.length())
{
if(line.c_str()[index] == ':')
{
colonCounter++;
if(colonCounter > 2)
{
printf("there can't be more than two colons in a slipnet file line, error at line %d character %d",lineNumber,index);
in.close();
return false;
}
}
else if(line.c_str()[index] == ';')
{
semicolonCounter++;
if(semicolonCounter > 1)
{
printf("there can't be more than one semicolon in a slipnet file line, error at line %d character %d",lineNumber,index);
in.close();
return false;
}
}
index++;
}
if(colonCounter < 2)
{
printf("there must be exactly 2 colons in each slipnet file line, error found in line %d",lineNumber);
in.close();
return false;
}
if(semicolonCounter < 1)
{
printf("there must be exactly 1 semicolon in each slipnet file line, error found in line %d",lineNumber);
in.close();
return false;
}
// find 1st and 2nd colon and the semicolon.
int indexOf1stColon = line.find_first_of(':');
int indexOf2ndColon = line.find_first_of(':',indexOf1stColon + 1);
int indexOfSemiColon = line.find(';');
concept = line.substr(0,indexOf1stColon);
trim(concept);
// children is everything from after the first : and before the second :
children = line.substr(indexOf1stColon+1,indexOf2ndColon - indexOf1stColon - 1);
trim(children);
// parents is everything after the second : and to the ';'
parents = line.substr(indexOf2ndColon+1,indexOfSemiColon - indexOf2ndColon - 1);
trim(parents);
ConceptNode* node = new ConceptNode(concept);
if(!parseChildrenAndParents(node,children,parents))
{
printf("error, make sure every child and parent node is followed by a parenthesized double for the weight of the link, error in line %d",lineNumber);
in.close();
return false;
}
net.insert(make_pair(concept,node));
lineNumber++;
}
in.close();
return true;
}
bool Slipnet :: parseChildrenAndParents(ConceptNode* node, std::string& children, std::string& parents)
{
std::string helper;
int helperIndex;
while(children.length() > 0)
{
helperIndex = children.find(',');
if(helperIndex == -1)
helperIndex = children.length();
helper = children.substr(0,helperIndex);
if(helperIndex == children.length())
children = "";
else
children = children.substr(helperIndex+1,children.length() - helperIndex - 1);
trim(helper);
// separate concept name from weight
int indexOfParenthsStart = helper.find('(');
int indexOfParenthsEnd = helper.find(')');
if(indexOfParenthsStart == -1 || indexOfParenthsEnd == -1)
return false;
std::string weightString = helper.substr(indexOfParenthsStart+1,indexOfParenthsEnd - indexOfParenthsStart-1);
helper = helper.substr(0,indexOfParenthsStart);
trim(helper);
double weight = atof(&weightString[0]);
node->addChild(helper, weight);
}
while(parents.length() > 0)
{
helperIndex = parents.find(',');
if(helperIndex == -1)
helperIndex = parents.length();
helper = parents.substr(0,helperIndex);
if(helperIndex == parents.length())
parents = "";
else
parents = parents.substr(helperIndex+1,parents.length() - helperIndex - 1);
trim(helper);
// separate concept name from weight
int indexOfParenthsStart = helper.find('(');
int indexOfParenthsEnd = helper.find(')');
if(indexOfParenthsStart == -1 || indexOfParenthsEnd == -1)
return false;
std::string weightString = helper.substr(indexOfParenthsStart+1,indexOfParenthsEnd - indexOfParenthsStart-1);
helper = helper.substr(0,indexOfParenthsStart);
trim(helper);
double weight = atof(&weightString[0]);
node->addChild(helper, weight);
}
return true;
}
void Slipnet :: trim(std::string& str)
{
int beginIndex = 0;
int endIndex = str.length();
while(str.c_str()[beginIndex] == ' ')
beginIndex++;
while(str.c_str()[endIndex] == ' ')
endIndex--;
str = str.substr(beginIndex,endIndex - beginIndex);
}
ConceptNode* Slipnet :: getNode(std::string type)
{
std::map<std::string, ConceptNode*>::iterator it = net.find(type);
if(it != net.end())
return it->second;
return NULL;
}
Slipnet* Slipnet :: getInstance()
{
if(!instanceFlag)
{
instance = new Slipnet();
instanceFlag = true;
loadFile("../../../data/slip.net");
}
return instance;
}
std::vector<std::pair < std::string, double > > Slipnet :: getPossibleChildren(std::string codeletType)
{
std::vector<std::pair < std::string, double > > answer;
ConceptNode* node = getNode(codeletType);
if(node != NULL)
answer = node->getChildren();
return answer;
}
std::vector<std::pair < std::string, double > > Slipnet :: getPossibleParents(std::string codeletType)
{
std::vector<std::pair < std::string, double > > answer;
ConceptNode* node = getNode(codeletType);
if(node != NULL)
answer = node->getParents();
return answer;
}
main.cpp
Code:
#include "Slipnet.hpp"
using namespace std;
int main(int argc,char* argv [])
{
Slipnet* slipnet = Slipnet::getInstance();
.
.
.
return 0;
}