Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

DIFFICULT: C++ class exporting & __declspec(dllexport)

Posted on 2004-08-20
5
3,533 Views
Last Modified: 2008-01-09
Problem: Dynamically link C++ classes into my application at run-time to provide specific ustomized behavior.  E.g. in my case, I have several formats of file to read, say F1 & F2.  Currently, my app has a class F1Reader that reads F1 and creates a bunch of other objects of type Factor1, Factor2, Factor3, etc.  I want to add support for a new file type F2, which will produce the same type of Factor1,2,3 objects, but with different data.  Factor objects are already well-defined C++ classes within my application.  Therefore, F1Reader & F2Reader are class factories.  Further, I would like to be able to add F3 in the future without modifying my primary application.  That is I want to be able to provide F3Reader.DLL & some registry settings, and boom, it works.

One obvious solution would be to encapsulate all the Factor type objects as well as the F1Reader & F2Reader classes as COM objects.  This is certainly straight-forward, but it is a LOT of work considering the amount of current code that uses Factor1,2,3 objects.  Therefore, I'm looking for a solution that will continue to create native C++ objects with dynamically linked code, without creating COM wrappers.

My proposed solution is to refactor F1Reader & F2Reader so they derive from a common base (FactorFactoryBase), and put F1Reader & F2Reader in separate C++ DLLs that might or might not be present on the user's machine.  The interface to FactorFactoryBase will provide all my application needs to create the Factor objects.  

Consider this little attempt to test out the concept:

struct __declspec(dllexport) B
{
private:
      string      m_s;

public:
      int            m_x;
      B()
      {
            m_x = 1;
            const type_info& t = typeid(*this);      
            m_s = t.name();  
      }
};

returns a compiler warning:

c:\dev\test\cppdll\b.h(12) : warning C4251: 'm_s' : class 'std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >' needs to have dll-interface to be used by clients of struct 'B'

While this is only a warning, it is a level 1 warning, and sounds pretty fatal since I want the DLLs to derive from B.

It seems I need to instantiate an specific instance of string and export that.  But I haven't been able to figure out how.

I tried:

class __declspec(dllexport) stringEx: public basic_string<char>
{
};

& got the same warning message.

Any suggestions on how to solve this problem?

TIA,
Brad
0
Comment
Question by:millsoft
  • 3
  • 2
5 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 11857333
DLLs are the Windows way to reuse code, and templates are the C++ way. They do not coexist well, though. My suggestion would be to provide pure abstract interfaces as base classes and let the implementations handle that seamlessly and, even more importand, leave STL objects in the modules they were created in. Provide access interfaces instead. Check out http://support.microsoft.com/default.aspx?scid=kb;en-us;168958 ("How To Exporting STL Components Inside & Outside of a Class") and http://support.microsoft.com/default.aspx?scid=kb;en-us;168958 ("How To Exporting STL Components Inside & Outside of a Class")
0
 
LVL 86

Accepted Solution

by:
jkr earned 500 total points
ID: 11857350
Argh - sorry, the 2nd link should have been http://support.microsoft.com/default.aspx?scid=kb;en-us;172396 ("PRB: Access Violation When Accessing STL Object in DLL")
0
 
LVL 5

Author Comment

by:millsoft
ID: 11876365
Hi jkr,

Thanks for your feedback.  Below follows some more research I've done after reading your articles.  You may (or may not) find it interesting.  

Brad
===
Ok, I've been looking into the article "How To Exporting STL Components Inside & Outside of a Class".  When you mentioned it, it rang a bell.  Seems like I've done that before.  Anyway, looking through the CRT code is loaded with related stuff like this:
// copied from Xstring in VC6.

#ifdef      _DLL
#ifdef __FORCE_INSTANCE
template class _CRTIMP2 basic_string<char, char_traits<char>, allocator<char> >;
template class _CRTIMP2 basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >;
#else            // __FORCE_INSTANCE
#pragma warning(disable:4231) /* the extern before template is a non-standard extension */

extern template class _CRTIMP2 basic_string<char, char_traits<char>, allocator<char> >;
extern template class _CRTIMP2 basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >;

#pragma warning(default:4231) /* restore previous warning */
#endif            // __FORCE_INSTANCE
#endif            // _DLL

_CRTIMP2 is defined to __declspec(dllimport) whenever the DLL version of the C-runtime is selected.  So, based upon the article, it appears that MSFT is exporting std::string from their own DLL for our use.  That makes sense, although then it seems that it's not possible to export a class (B) containing an imported data member from the CRT (string), even though both my EXE and DLL should both be able to see (and import) the same exported symbol.  Go figure.

BTW, the two articles you mentioned are not quite consistent.  E.g. one article (168958) says exporting maps is impossible, and the other article (172396) uses that as an example!

0
 
LVL 5

Author Comment

by:millsoft
ID: 11876614
Hi jkr,

It turns out my problem didn't have anything to do with the templates at all.  The simple problem was that my projects didn't have the CRT set to the DLL selections.  I normally use CRT in DLLs, but somewhere I missed it here, and they were all set to static linked.  Changing that fixed all the problems because string is now easily imported into the DLLs.

However, your articles did help because I found I couldn't compile the examples from the article which is how I uncovered my settings problem. :)

Brad
====
Here's the code:

// b.h in the EXE
#pragma once

#include <string>
using namespace std;
// DOING_EXE is defined for compiling the EXE containing the actual code.  
// not defined in all other cases.

#ifdef DOING_EXE
#    define EXE_EXPORT_SPECIFIER __declspec(dllexport)
#pragma message("exporting")
#else
#    define EXE_EXPORT_SPECIFIER __declspec(dllimport)
#pragma message("Importing")
#endif

struct EXE_EXPORT_SPECIFIER B
{
      string      m_s;
      int      m_x;
      B();
      virtual ~B();
};

struct EXE_EXPORT_SPECIFIER D:public B
{
      D();
      virtual ~D() {};
};

// derived class in my DLL

#ifdef CPPDLL1_EXPORTS
#define CPPDLL1_API __declspec(dllexport)
#else
#define CPPDLL1_API __declspec(dllimport)
#endif
#include "..\B.h"
// This class is exported from the CPPDLL1.dll

struct CPPDLL1_API D1:public B
{
      D1();
      virtual ~D1();
};



0
 
LVL 86

Expert Comment

by:jkr
ID: 11876899
Glad to have been of some help, thanx :o)
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
thread-safe code in c++ 2 115
GUI: DIalog Stacking and Popping in MS C++ 4 82
Unresolved External Symbols 3 105
One named event, multiple event handlers 2 24
Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

861 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question