Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

unresolved external symbol 2

Status
Not open for further replies.

sunaj

Technical User
Feb 13, 2001
1,474
DK
Hi,

I've got a function:
Code:
string jReplace(string& s, string const& find, string const& replace)

If I place the function in the main file it works as expected.

If I place the function in a static Lib (which contains other functions which works fine in the Lib):
Code:
static string jReplace(string&, string const&, string const&);
my main project compiles with the following error:

Error 2 error LNK2001: unresolved external symbol "public: static class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl JCppLib::JLib::jReplace(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?jReplace@JLib@JCppLib@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AAV34@ABV34@1@Z) testJCppLib.obj testJCppLib

Any suggestion are very welcome.
thx in advance

Sunaj
'The gap between theory and practice is not as wide in theory as it is in practice'
 
If it is static, it means it is not public i.e. the library does not export it. In the header it should be declared as extern. In the code remove static/extern.
 
Hi xwb,

C++ is definetly my no. 1 language, but I was sure that 'static' meant that you can use the function without creating an instance of the class...?? That seems to work fine on my other functions in the class.

Declaring the function as 'extern' gives the following compiler error - isn't 'extern' for dynamic libs?

Error 1 error C2720: 'JCppLib::JLib::jReplace' : 'extern' storage-class specifier illegal on members c:\dmu\programs\c++\jcpplib\JCppLib.h 10 JCppLib



Sunaj
'The gap between theory and practice is not as wide in theory as it is in practice'
 
It should of course have read
C++ is definetly NOT my no. 1 languange !!!

Sunaj
'The gap between theory and practice is not as wide in theory as it is in practice'
 
Ahh... so it's a member function?

If so, then you need to link to the static lib that has it's definition.
 
static is context dependent.

At the top level, it behaves like C i.e. not public.

In a function, it means that it retains the value on reentry. Similar to own variables in Algol 60

In a class it means it does not have to have an instance.
 
It is almost certainly my lack of understanding the C++ classes/Libraries/Linking that is the cause of the problem here. Anyway, I have not resolved this, but worked around it by simply removing the class from the Lib project. Now it's just a list of functions - and that serves my purpose so far.

My Lib header file:
Code:
#include <string>
#include <vector>
#include <time.h>

using namespace std;

namespace JCppLib
{
	double jTimer();
	double jTimer(double);
	namespace JMath {
		double jAdd(double a, double b);// Returns a + b
	}
	namespace JStr {
		string jReplace(string&, string const&, string const&);
		vector<string> jSplit(string,string);
	}
}

Sunaj
'The gap between theory and practice is not as wide in theory as it is in practice'
 
Better try to resolve the original problem.
The linker wanted JCppLib::JLib::jReplace name. What's JCppLib? What's JLib? What's your jReplace function true name?
In the OP we (you;) have ::jReplace name only!..
Obviously, it's a very trivial problem.
More info needed...

Of course, the C++ is not a trivial language but your work-around "solution" looks like as a very artificial and laboured one...
 
That's the spirit ArkM - you are absolutely right. Here is my code.

This is my Lib header:
Code:
#include <string>
using namespace std;

namespace JCppLib
{
    class JLib
    {
    public:
		static string jReplace(string&, string const&, string const&);
    };
}

This is my lib source:
Code:
#include "JCppLib.h"

namespace JCppLib
{
	string jReplace(string& s, string const& find, string const& replace)
	{
		string source = s;
		for(string::size_type i = 0; (i = source.find(find, i)) != string::npos;)
		{
			source.replace(i, find.length(), replace);
			i += replace.length() - find.length() + 1;
		}
		return source;
	}
}

This is my test program:
Code:
#include <iostream>
#include "stdafx.h"
#include "JCppLib.h"

int _tmain(int argc, _TCHAR* argv[])
{
	string ms = "hej med dig";
	string s = JCppLib::JLib::jReplace(ms," ","");
	cout << ms << endl;
	cout << s << endl;

	cout << "[PRESS ENTER TO EXIT]";
	cin.get();
    return (EXIT_SUCCESS);

	return 0;
}

As you can see I have incl. the JCppLib header in the test program and I have also included the folder with the JCppLib.lib file in the test (I'm using VS2008 and added the path under 'Additional Include Directories').

Janus
Ps. Sorry for the late response.




Sunaj
'The gap between theory and practice is not as wide in theory as it is in practice'
 
In your header, jReplace is a member of JLib, but in your source file, it is not. It should be like this:
Code:
string JLib::jReplace(string& s, string const& find, string const&  replace)
in your source file. Notice the extra JLib:: to indicate the jReplace function is a member of the JLib class.
 
I've never been comfortable with this type of coding even though you see it in many books
Code:
    string JLib::jReplace(string& s, string const& find, string const& replace)
    {
        string source = s;
        for(string::size_type i = 0; (i = source.find(find, i)) != string::npos;)
        {
            source.replace(i, find.length(), replace);
            i += replace.length() - find.length() + 1;
        }
        return source;
    }
The insecurity is that source is on the stack so you're actually copying something from the stack when it is, in theory deleted since it should return before copying the result. I have had some crashes in multi-threaded programs because of this. To be safe, pass the output string as a parameter even though the code doesn't look as elegant.
 
Well, I come back too late, all done ;)

By the way, the real std::string body (text buffer) is not on the stack, so it's an absolutely correct (and extremely ineffective;) code...
 
I should have spotted that myself. You both get appreciation for your persistance. Thx.

ArkM, Can you elaborate on why the function is 'extremely ineffective'? And can you suggest a more efficent approach?
Comming from C# I prefer that the function returns the result, rather than having to pass a string by reference...


Sunaj
'The gap between theory and practice is not as wide in theory as it is in practice'
 
Well, let's continue:
Code:
string jReplace(string& s, string const& find, string const& replace)
{
    if (find.empty()) return s; // !!!
    string source = s;
    for (string::size_type i = 0; (i = source.find(find, i)) != string::npos;) {
        source.replace(i, find.length(), replace);
        i += find.length(); //replace.length()- find.length() + 1;
    }        
    return source;    
}

// Now some improvisation:
typedef std::string::size_type stype;
namespace { const stype npos = std::string::npos; }

string aReplace(string& s, string const& find, string const& replace)
{
    const stype flen = find.length();
    if (flen == 0) return s;
    string o;
    o.reserve(s.length());
    stype fpos, ipos = 0;

    while ((fpos = s.find(find,ipos)) != npos) {
        if (stype glen = fpos - ipos)
           o += s.substr(ipos,glen);
        o += replace;
        ipos = fpos + flen;
    }
    o += s.substr(ipos);
    return o;
}

void testBed()
{
    std::string
        s,
        f("happy"),
        r("birthday");
        std::string t;
    const size_t N = 2000;
    s.reserve(10*N);
    for (size_t i = 0; i < N; i++) {
        s += f;
        s += "12345";
    }
    psi::StopWatch tick; // (change to your favorite watch;)
    t = jReplace(s,f,r);
    double e = tick.micros();
    std::cout << "OP: " << e << " microseconds\n";

    std::string q;
    tick.reset();
    q = aReplace(s,f,r);
    e = tick.micros();
    if (t == q) {
        std::cout << "AM: " << e << " microseconds\n";
    } else {
        std::cout << "*** Failed\n";
    }
}
Result on my VC++ 2008/AMD 5000+:
Code:
OP: 3253.49 microseconds
AM: 536.102 microseconds
Feel the difference ;)
Good luck!
 
Sorry, yet another addition:

>Comming from C# I prefer that the function returns the result, rather than having to pass a string by reference...
Aha - returns void, for example ;)

The std::string type has a value semantics in C++ so it's OK to return std::string values. If we pass the target string by reference, probably we have slightly more effective code + lots of problems with parameters dependencies (source == target case, for example)...
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top