By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,782 Members | 1,328 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,782 IT Pros & Developers. It's quick & easy.

having linking troubles

P: n/a
I have a template class that has static members, so in the .cpp file I
have defined them with templated definitions. Now when in a different
..cpp file that includes the header file for the template class I derive
a new class from the template class and pass that class as its template
parameter (CRTP). Now when I instantiate this new class it complains it
cannot find the statics. Doesnt the template definition define them?
The code compiles fine but causes linker errors.
heres a boiled down example of whats causing the trouble....

header....
#ifndef CTYPE
#define CTYPE
#include <cstddef>
namespace ns
{
template <typename CT>
class OC
{
public:
static std::size_t count()
{
return OC<CT>::c_;
}
protected:
OC()
{
++OC<CT >::c_;
}
OC( const OC< CT >& )
{
++OC< CT >::c_;
}
~OC()
{
--OC< CT >::c_;
}
private:
static std::size_t c_;
};
}
#endif

..cpp ....
#include "templates.h"

namespace ns
{
// mandatory static definition
template < typename CT >
std::size_t OC< CT >::c_ = 0;
}

main.cpp
#include "templates.h"
#include <iostream>
using namespace std;

class C : private ns::OC<C>
{
public:
using ns::OC<C>::count;
};

int main()
{
C array[3];
cout << C::count()<<endl;
return 0;
}

How do I make my code linkable as well as compileable?

Oct 9 '05 #1
Share this Question
Share on Google+
15 Replies


P: n/a
Improving wrote:
I have a template class that has static members, so in the .cpp file I
have defined them with templated definitions.


This is wrong, all template code must go in header files, that includes
static members.

john
Oct 9 '05 #2

P: n/a
wont that cause trouble with redefinitions if I link together two
different translation units both using the same class?

Oct 9 '05 #3

P: n/a
Improving wrote:
wont that cause trouble with redefinitions if I link together two
different translation units both using the same class?


No, templates are different. I know it seems strange but it's the right
thing to do. All template code goes in header files, the linker will
know to eliminate duplicate definitions.

Try reading the FAQ

http://www.parashift.com/c++-faq-lite/templates.html

question 35.7 onwards.

john
Oct 9 '05 #4

P: n/a
i tried it, im still getting lnk1004

Oct 9 '05 #5

P: n/a
sorry thats lnk2001. Im getting one for each static member even though
I have moved the definitions to just below the class in the header
files. I dont understand whats going wrong here.

Oct 9 '05 #6

P: n/a
Improving wrote:
sorry thats lnk2001. Im getting one for each static member even though
I have moved the definitions to just below the class in the header
files. I dont understand whats going wrong here.


I took your code, put everything in the header file, and it worked for
me. I'm using MSVC++ 7.1

john
Oct 9 '05 #7

P: n/a
msvc 7 here.
can you try these john. sorry for length.

criticalsection.h

#ifndef CRITICALSECTION_090171
#define CRITICALSECTION_090171
#include <windows.h>

namespace threading
{
class CriticalSection
{
public:
CriticalSection()
{
InitializeCriticalSection(&lock_);
}
~CriticalSection()
{
DeleteCriticalSection(&lock_);
}
void enter()
{
EnterCriticalSection(&lock_);
}
void leave()
{
LeaveCriticalSection(&lock_);
}

private:
// disallow copying operations because they make no sense.
CriticalSection( const CriticalSection& );
CriticalSection& operator =( const CriticalSection& );

CRITICAL_SECTION lock_;
};

class Guard
{
public:
Guard(CriticalSection& lock) : lock_( lock )
{
lock_.enter();
}
~Guard()
{
lock_.leave();
}
private:
// disallow copying as again its senseless.
Guard( const Guard& );
Guard& operator =( const Guard& );

CriticalSection& lock_;
};

} // end namespace threading

#endif //inclusion guard

counted_type.h

#ifndef COUNTEDTYPE_090171
#define COUNTEDTYPE_090171
#include <cstddef>
#include <windows.h>
#include "criticalsection.h"

namespace utility
{
// A template class to generically count objects safely in a
multithreaded app.
template < typename CountedType >
class ObjectCounter
{
public:
static std::size_t count()
{
threading::Guard guard( lock_ );
return ObjectCounter< CountedType >::count_;
}
protected:
ObjectCounter()
{
threading::Guard guard( lock_ );
++ObjectCounter< CountedType >::count_;
}
ObjectCounter( const ObjectCounter< CountedType >& )
{
threading::Guard( lock_ );
++ObjectCounter< CountedType >::count_;
}
~ObjectCounter()
{
threading::Guard( lock_ );
--ObjectCounter< CountedType >::count_;
}
private:
static std::size_t count_;
static threading::CriticalSection lock_;
};

template < typename CountedType >
threading::CriticalSection ObjectCounter< CountedType
::lock_;

template < typename CountedType >
std::size_t ObjectCounter< CountedType >::count_ = 0;

} // end namespace utility
#endif // inclusion guard

limited_type.h

#ifndef LIMITEDTYPE_090171
#define LIMITEDTYPE_090171
#include <cstddef>
#include <stdexcept>
#include <string>
#include <windows.h>
#include "criticalsection.h"
#include "object_counter.h"

namespace utility
{
class TooManyObjects : public std::runtime_error
{
public:
TooManyObjects( const std::string& problem )
: runtime_error( problem )
{;}
};

// A template class that manages numbers of objects and ensures they
dont exceed
// the held maximum value.
template < typename CountedType >
class LimitedType : private ObjectCounter< CountedType >
{
public:
// This function controls the maximum amount of objects.
static void allow( std::size_t maxnum )
{
threading::Guard guard( lock_ );
LimitedType< CountedType >::maxObjects_ = maxnum ;
}
// propagate the count function into the public interface
using ObjectCounter< CountedType >::count;

protected:
LimitedType()
{
init();
}
LimitedType( const LimitedType& )
{
init();
}
~LimitedType()
{;}
private:
void init()
{
threading::Guard guard( lock_ );
if ( count() + 1 > maxObjects_ )
throw TooManyObjects( "Limit on number of objects
exceeded" );
}

static std::size_t maxObjects_;
static threading::CriticalSection lock_;
};

template < typename CountedType >
threading::CriticalSection LimitedType< CountedType >::lock_;
template < typename CountedType >
std::size_t LimitedType< CountedType >::maxObjects_ = 1;

} // end namespace utility
#endif // inclusion guard

main.cpp

#include "limited_type.h"
#include <iostream>

using namespace std;
class MyClass : private utility::LimitedType< MyClass >
{
public:
using utility::LimitedType< MyClass >::allow;
using utility::LimitedType< MyClass >::count;
};

void exceptiontest()
{
try
{
MyClass array[3];
}
catch( utility::TooManyObjects& )
{
cout << "exception correctly caught" << endl;
}
}

int main()
{
exceptiontest();

return 0;
}

this gives:-
object counter with tests error LNK2001: unresolved external symbol
"private: static unsigned int utility::LimitedType<class
MyClass>::maxObjects_"
(?maxObjects_@?$LimitedType@VMyClass@@@utility@@0I A)
object counter with tests error LNK2001: unresolved external symbol
"private: static class threading::CriticalSection
utility::LimitedType<class MyClass>::lock_"
(?lock_@?$LimitedType@VMyClass@@@utility@@0VCritic alSection@threading@@A)
object counter with tests error LNK2001: unresolved external symbol
"private: static unsigned int utility::ObjectCounter<class
MyClass>::count_" (?count_@?$ObjectCounter@VMyClass@@@utility@@0IA)
object counter with tests error LNK2001: unresolved external symbol
"private: static class threading::CriticalSection
utility::ObjectCounter<class MyClass>::lock_"
(?lock_@?$ObjectCounter@VMyClass@@@utility@@0VCrit icalSection@threading@@A)
object counter with tests fatal error LNK1120: 4 unresolved externals

What am I doing wrong? Am I missing something here or is my compiler
sucky?

Oct 9 '05 #8

P: n/a
Improving wrote:
msvc 7 here.
can you try these john. sorry for length.


Missing a file called object_counter.h.

Can't see anything obviously wrong.

john
Oct 9 '05 #9

P: n/a
its there, i mistook its name. its not counted_type.h its
object_counter.h

Oct 9 '05 #10

P: n/a
Improving wrote:
its there, i mistook its name. its not counted_type.h its
object_counter.h


Well there seem to be a couple of typos, for instance

~ObjectCounter()
{
threading::Guard( lock_ );
--ObjectCounter< CountedType >::count_;
}

should be

~ObjectCounter()
{
threading::Guard guard( lock_ );
--ObjectCounter< CountedType >::count_;
}

similarly in one of the constructors.

With those fixed it compiles and links for me.

john
Oct 9 '05 #11

P: n/a
dammit i fixed those typos before then while playing with the code
reverted to an earlier version. doh. still fixed them again and still 4
unresolved externals. Oh how i wish I had 7.1. Im stuck with .net. Do
you know where there is a list of standard conformance issues for
msvc7. I'd like to know if this is a compiler problem. Thanks anyway. I
guess if msvc7.1 takes it the code must be fine. Any idea of the
workaround for msvc7? I think its pretty obvious from the code what I'm
trying to do.

Oct 9 '05 #12

P: n/a
Improving wrote:
dammit i fixed those typos before then while playing with the code
reverted to an earlier version. doh. still fixed them again and still 4
unresolved externals. Oh how i wish I had 7.1. Im stuck with .net. Do
you know where there is a list of standard conformance issues for
msvc7.
I have seen a list from the Deep C++ column

http://msdn.microsoft.com/library/de...deep051099.asp

Not precisely sure which version of MSVC++ this applies to.

I'd like to know if this is a compiler problem. Thanks anyway. I guess if msvc7.1 takes it the code must be fine. Any idea of the
workaround for msvc7? I think its pretty obvious from the code what I'm
trying to do.


You might try something like this

template < typename CountedType >
class ObjectCounter
{
public:
static std::size_t count()
{
threading::Guard guard( the_lock() );
return the_count();
}
protected:
ObjectCounter()
{
threading::Guard guard( the_lock() );
++the_count();
}
ObjectCounter( const ObjectCounter< CountedType >& )
{
threading::Guard guard( the_lock() );
++the_count();
}
~ObjectCounter()
{
threading::Guard guard( the_lock() );
--the_count();
}
private:
static std::size_t& the_count()
{
static std::size_t count = 0;
return count;
}
static threading::CriticalSection& the_lock()
{
static threading::CriticalSection lock;
return lock;
}
};

Not compiled, so apologies for any typos etc.

john
Oct 9 '05 #13

P: n/a
John Harrison wrote:
Improving wrote:
dammit i fixed those typos before then while playing with the code
reverted to an earlier version. doh. still fixed them again and still 4
unresolved externals. Oh how i wish I had 7.1. Im stuck with .net. Do
you know where there is a list of standard conformance issues for
msvc7.

I have seen a list from the Deep C++ column

http://msdn.microsoft.com/library/de...deep051099.asp
Not precisely sure which version of MSVC++ this applies to.


From that link click on the Three Strikes article.

john
Oct 9 '05 #14

P: n/a
went right through those deep c++ articles. Didn't see anything
pertinent. The workround you suggested looks workable but i still
prefer my design. Oh well I've wanted 7.1 for a long time now i got an
excuse to go out and buy it. might even be able to get it cheap as 8
will be out soon.

Oct 9 '05 #15

P: n/a
Sorted it. Made a new project. then it compiled and linked. very
strange.
Thanks for your time john.

Oct 9 '05 #16

This discussion thread is closed

Replies have been disabled for this discussion.