I am not sure if this OT, but I will go ahead and post. If it has to be
posted elsewhere let me know.
-vijai.
------

I have a class declared as follows:

#pragma once

// a simple reference counter implementation

#ifndef __REF_COUNTER_H__
#define __REF_COUNTER_H__

#include <iosfwd>
#include "smlibdefs.h"

namespace SMLib
{

class SMLIBAPI_CLASS ReferenceCounter
{
unsigned long m_numrefs;
public:
ReferenceCounter();
ReferenceCounter(const ReferenceCounter&);
ReferenceCounter& operator = (const ReferenceCounter&);
virtual ~ReferenceCounter();

virtual void AcquireReference();
virtual void ReleaseReference();
virtual unsigned long CountReferences() const;
};

};

SMLIBAPI_FUNCTION std::ostream& operator << (std::ostream&, const
SMLib::ReferenceCounter&);
SMLIBAPI_FUNCTION std::wostream& operator << (std::wostream&, const
SMLib::ReferenceCounter&);

#endif

SMLIBAPI_FUNCTION is defined as __declspec(dllimport) or
__declspec(dllexport) depending on whether the class is being imported
or exported. (On Windows. On *nix I don't have to do anything to export
C++ classes I believe. All symbols are exported by default?)

I build a shared library and a static library. I test the class and
it's methods with the following separate test program.

#include <refcounter.h>
#include <windows.h>
#include <iostream>
#include <assert.h>

void Test_ReferenceCounter_Construction()
{
SMLib::ReferenceCounter ctr;
std::wcout<<L"Counter: "<<ctr<<std::endl;
}

void Test_ReferenceCounter_Initialization()
{
SMLib::ReferenceCounter ctr;
std::wcout<<L"Counter: "<<ctr<<std::endl;
assert(ctr.CountReferences() == 0);
}

void Test_ReferenceCounter_CopyConstruction()
{
SMLib::ReferenceCounter ctr;
std::wcout<<L"Counter 1: "<<ctr<<std::endl;
SMLib::ReferenceCounter ctr2 = ctr;
std::wcout<<L"Counter 2: "<<ctr2<<std::endl;
assert(ctr2.CountReferences() == 0);
}

void Test_ReferenceCounter_Assignment()
{
SMLib::ReferenceCounter ctr;
std::wcout<<L"Counter 1: "<<ctr<<std::endl;
ctr.AcquireReference();
ctr.AcquireReference();
SMLib::ReferenceCounter ctr2;
ctr2.AcquireReference();
std::wcout<<L"Counter 2: "<<ctr2<<std::endl;
ctr2 = ctr;
std::wcout<<L"Counter 2 (reassigned): "<<ctr2<<std::endl;
assert(ctr2.CountReferences() == ctr.CountReferences());
}

void Test_ReferenceCounter_ReferenceIncrement()
{
SMLib::ReferenceCounter ctr;
std::wcout<<L"Counter: "<<ctr<<std::endl;
ctr.AcquireReference();
assert(ctr.CountReferences() == 1);
}

void Test_ReferenceCounter_ReferenceDecrement()
{
SMLib::ReferenceCounter ctr;
std::wcout<<L"Counter: "<<ctr<<std::endl;
ctr.AcquireReference();
ctr.AcquireReference();
ctr.ReleaseReference();
assert(ctr.CountReferences() == 1);
}

int main()
{
LoadLibrary("smlib-1.0.0.0");

Test_ReferenceCounter_Construction();
Test_ReferenceCounter_Initialization();
Test_ReferenceCounter_Assignment();
Test_ReferenceCounter_ReferenceIncrement();
Test_ReferenceCounter_ReferenceDecrement();

return 0;
}

However, in the shared library:

I get an unresolved external error only for the overloaded left shift
operator. On the other hand for the static library I get unresolved
external symbols error for all the methods as well as the function. I
use the same makefile to compile both:

executableName = smlib
programName = smlib

version = 1.0.0.0

baseDir = .
incDir = $(baseDir)/
srcDir = $(baseDir)/

!if defined(DEBUG) || defined(_DEBUG)
buildSuffix = d
buildType = DEBUG
!else
buildSuffix =
buildType = RELEASE
!endif

ExtLibs = kernel32.lib \
msvcrt$(buildSuffix).lib \
libc$(buildSuffix).lib \
libcmt$(buildSuffix).lib

Includes = basedefs.h \
smlibdefs.h \
refcounter.h

OutPath = $(MAKEDIR)\$(buildType)

CCOpts = /c /Gd /MDd /EHa /D__SMLIB__BUILD /D$(buildType) /I$(incDir)
CC = cl.exe

LibOpts = /OUT:$(OutPath)\libsmlib-$(version).lib /NOLOGO
/SUBSYSTEM:NATIVE
LIB = lib.exe

LinkOpts = /$(buildType) /DLL /NOLOGO
/OUT:$(OutPath)\smlib-$(version).dll
/IMPLIB:$(OutPath)\smlib-$(version).lib
/PDB:$(OutPath)\smlib-$(version).pdb $(ExtLibs)
LINK = link.exe

refcounter: refcounter.cpp $(Includes)
$(CC) $(CCOpts) /Fo$(OutPath)/refcounter.obj
refcounter.cpp

allObjs = $(OutPath)\refcounter.obj

lib: refcounter
$(LIB) $(LibOpts) $(allObjs)

dll: refcounter
$(LINK) $(LinkOpts) $(allObjs)

all: refcounter dll lib

clean:
del /Q $(OutPath)\*.obj
del /Q $(OutPath)\smlib-$(version).pdb
del /Q $(OutPath)\smlib-$(version).lib
del /Q $(OutPath)\smlib-$(version).exp
del /Q $(OutPath)\smlib-$(version).dll
del /Q $(OutPath)\smlib-$(version).ilk

Anyone have any ideas on what I am doing wrong?

thanks,

-vijai.

Re: Exporting C++ Classes from a Static Library and from a DLL by Igor

Igor
Sun Aug 21 07:55:35 CDT 2005

"Vijai Kalyan" <vijai.kalyan@gmail.com> wrote in message
news:1124593713.946864.42660@f14g2000cwb.googlegroups.com
> I have a class declared as follows:
[snip]
>
> SMLIBAPI_FUNCTION is defined as __declspec(dllimport) or
> __declspec(dllexport) depending on whether the class is being imported
> or exported. (On Windows. On *nix I don't have to do anything to
> export C++ classes I believe. All symbols are exported by default?)
>
> I build a shared library and a static library. I test the class and
> it's methods with the following separate test program.
[snip]
>
> However, in the shared library:
>
> I get an unresolved external error only for the overloaded left shift
> operator. On the other hand for the static library I get unresolved
> external symbols error for all the methods as well as the function.

Show how you compile and link the test. The library itself seems to be
fine. Except that I'm surprised that in the first case you get
unresolved external for just one function - are you sure you have
actually implemented it?

By the way, your LoadLibrary call in main does not do anything useful.
Drop it.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925



Re: Exporting C++ Classes from a Static Library and from a DLL by Vijai

Vijai
Sun Aug 21 14:37:13 CDT 2005

Hi Igor,

Thanks for the response.

Yes, I did implement it. I went over the implementation over and over
and didn't find anything particularly wrong. Here is the code:

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

namespace SMLib
{

SMLIBAPI_FUNCTION INLINE_FUNCTION
ReferenceCounter::ReferenceCounter()
{
m_numrefs = 0;
}

SMLIBAPI_FUNCTION INLINE_FUNCTION
ReferenceCounter::ReferenceCounter(const ReferenceCounter& right)
{
m_numrefs = right.m_numrefs;
}

SMLIBAPI_FUNCTION INLINE_FUNCTION ReferenceCounter&
ReferenceCounter::operator = (const ReferenceCounter& right)
{
m_numrefs = right.m_numrefs;
return *this;
}

SMLIBAPI_FUNCTION INLINE_FUNCTION
ReferenceCounter::~ReferenceCounter()
{
}

SMLIBAPI_FUNCTION INLINE_FUNCTION void
ReferenceCounter::AcquireReference()
{
++m_numrefs;
}

SMLIBAPI_FUNCTION INLINE_FUNCTION void
ReferenceCounter::ReleaseReference()
{
--m_numrefs;
}

SMLIBAPI_FUNCTION INLINE_FUNCTION unsigned long
ReferenceCounter::CountReferences() const
{
return m_numrefs;
}

};

SMLIBAPI_FUNCTION std::ostream& operator << (std::ostream& ostrm,
const SMLib::ReferenceCounter& counter)
{
ostrm << "[ ReferenceCounter @ 0x" << std::hex << (&counter)
<< ": Reference Count: " << std::dec <<
counter.CountReferences()
<< "(0x" << std::hex << counter.CountReferences() << ") ]";
return ostrm;
}

SMLIBAPI_FUNCTION std::wostream& operator << (std::wostream& wostrm,
const SMLib::ReferenceCounter& counter)
{
wostrm << L"[ ReferenceCounter @ 0x" << std::hex << (&counter)
<< L": Reference Count: " << std::dec <<
counter.CountReferences()
<< L"(0x" << std::hex << counter.CountReferences() << L")
]";
return wostrm;
}

And here is how I link the test program:


executableName = smlib
programName = smlib

version = 1.0.0.0

baseDir = .
incDir = $(baseDir)/
srcDir = $(baseDir)/

!if defined(DEBUG) || defined(_DEBUG)
buildSuffix = d
buildType = DEBUG
!else
buildSuffix =
buildType = RELEASE
!endif

smlibIncDir = $(MAKEDIR)\..\smlib
smlibLibDir = $(MAKEDIR)\..\smlib\$(buildType)

ExtLibs = kernel32.lib \
msvcrt$(buildSuffix).lib \
libc$(buildSuffix).lib \
libcmt$(buildSuffix).lib \
$(smlibLibDir)\libsmlib-$(version).lib

Includes = $(smlibIncDir)\basedefs.h \
$(smlibIncDir)\smlibdefs.h \
$(smlibIncDir)\refcounter.h

OutPath = $(MAKEDIR)\$(buildType)

CCOpts = /c /Gd /MDd /EHa /D$(buildType) /I$(incDir) /I$(smlibIncDir)
CC = cl.exe

LinkOpts = /$(buildType) /OUT:$(OutPath)\smlibtests-$(version).exe
/PDB:$(OutPath)\smlibtests-$(version).pdb $(ExtLibs)
LINK = link.exe

refcounter: TestSMLib.cpp $(Includes)
$(CC) $(CCOpts) /Fo$(OutPath)/TestSMLib.obj TestSMLib.cpp

allObjs = $(OutPath)\TestSMLib.obj


exe: refcounter
$(LINK) $(LinkOpts) $(allObjs)

all: exe

clean:
del /Q $(OutPath)\*.obj
del /Q $(OutPath)\smlibtests-$(version).pdb
del /Q $(OutPath)\smlibtests-$(version).lib
del /Q $(OutPath)\smlibtests-$(version).exp
del /Q $(OutPath)\smlibtests-$(version).dll
del /Q $(OutPath)\smlibtests-$(version).ilk


regards,

-vijai.


Re: Exporting C++ Classes from a Static Library and from a DLL by Igor

Igor
Sun Aug 21 18:02:22 CDT 2005

"Vijai Kalyan" <vijai.kalyan@gmail.com> wrote in message
news:1124653033.205449.29580@g49g2000cwa.googlegroups.com
> Yes, I did implement it. I went over the implementation over and over
> and didn't find anything particularly wrong. Here is the code:

What's INLINE_FUNCTION? What happens if you define it as nothing?

Nothing jumps at me as obviously wrong. Check all your paths, I suspect
the linker does not find the library. Try linking with /VERBOSE switch,
the linker then outputs a report of which symbols and which libraries it
looks at.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925



Re: Exporting C++ Classes from a Static Library and from a DLL by Tom

Tom
Mon Aug 22 07:47:02 CDT 2005

Vijai Kalyan wrote:
> I am not sure if this OT, but I will go ahead and post. If it has to be
> posted elsewhere let me know.
> -vijai.
> ------
>
> I have a class declared as follows:
>
> #pragma once
>
> // a simple reference counter implementation
>
> #ifndef __REF_COUNTER_H__
> #define __REF_COUNTER_H__
>
> #include <iosfwd>
> #include "smlibdefs.h"
>
> namespace SMLib
> {
>
> class SMLIBAPI_CLASS ReferenceCounter
> {
> unsigned long m_numrefs;
> public:
> ReferenceCounter();
> ReferenceCounter(const ReferenceCounter&);
> ReferenceCounter& operator = (const ReferenceCounter&);
> virtual ~ReferenceCounter();
>
> virtual void AcquireReference();
> virtual void ReleaseReference();
> virtual unsigned long CountReferences() const;
> };
>
> };

No ; required to close a namespace.

>
> SMLIBAPI_FUNCTION std::ostream& operator << (std::ostream&, const
> SMLib::ReferenceCounter&);
> SMLIBAPI_FUNCTION std::wostream& operator << (std::wostream&, const
> SMLib::ReferenceCounter&);
>
> #endif

Probably unrelated to your problem, but you should declare and define
the two operator<< functions inside namespace SMLib, so that argument
dependent lookup can find them. In general, all operators should be
declared in the same namespace as one of the arguments, generally the
argument that the operator is most closely related to.

Tom

Re: Exporting C++ Classes from a Static Library and from a DLL by Ulrich

Ulrich
Mon Aug 22 08:06:45 CDT 2005

Vijai Kalyan wrote:
> #ifndef __REF_COUNTER_H__
> #define __REF_COUNTER_H__

Sorry, this is wrong and bad practice. Symbols containing consecutive
underscores or beginning with an underscore followed by an uppercase letter
are reserved to the implementation (i.e. the compiler and its
standardlibrary). In your case, you heaped additional guilt upon yourself
by choosing a name that refers to something so incredibly common in CS as
reference counting - I wouldn't bet anything on not having a system header
using the exact same include guard as your header. My advise: combine a
readable part with a GUID-like part or the current date and time to create
a unique string.

Other than that, you can use #pragma message() to output messages (i.e. the
content of macros) during debugging when using one of the Microsoft
compilers. That, and a helper to make a string out of a macro should help
diagnose your problems:

#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#if defined _MSC_VER
# pragma message("SMLIBAPI_CLASS=" STRINGIZE(SMLIBAPI_CLASS) "\n")
#endif

Uli



Re: Exporting C++ Classes from a Static Library and from a DLL by Vijai

Vijai
Tue Aug 23 06:49:31 CDT 2005

HI Ulrich and Tom,

Thanks for pointing out the errors. However, do see my other post. I
think the problem was related to that!

regards,

-vijai,.