23

I am a developer of an Open Source library. One of our classes is templated for a bunch of different types. Currently the definition resides in the header file, which has a negative effect on compilation times and also forces users to include more headers than needed. My goal is the following:

  • To reduce compilation time, I want to make use of the the explicit instantiation declaration introduced with C++11.

  • The definition of the class methods and members, which all are static, must be separate from the declaration in an implementation file. They should be available for usage outside and inside the library without users having to do an explicit instantiation definition or anything like this.

  • This must run cross-platform on all common compilers that support C++11 (Visual Studio 2013+, GCC, etc)

C++11 is providing new features for class templates, in specific the "explicit instantiation declaration". As far as I understand this can be used in this context. Previous questions dealt with this in similar contexts, e.g. How to use extern template and Separating definition/instantiation of template classes without 'extern' but those do not deal with library exports and their solution cause linker errors if a client tries to use the shared library.

Currently I have managed to implement this in a way it compiles, links and runs on Visual Studio 2015, but I am unsure if I use the keywords correctly, especially the __declspec one in this case. This is what I got (simplified):

// class.h
template<typename T>
class PropertyHelper;

template<typename T>
class PropertyHelper<const T>
{
public:
    typedef typename PropertyHelper<T>::return_type return_type;

    static inline return_type fromString(const String& str)
    {
        return PropertyHelper<T>::fromString(str);
    }

    static const int SomeValue;
};


template<>
class EXPORTDEF PropertyHelper<float>
{
public:
    typedef float return_type;

    static return_type fromString(const String& str);

    static const int SomeValue;
};

extern template EXPORTDEF class PropertyHelper<float>;

The last line is the explicit instantiation declaration. As far as I understand this means that clients do not have to declare this themselves every time. EXPORTDEF is a define that is either __declspec(dllexport) or __declspec(dllimport) on Windows. I am not sure if I need to place this in the line above, because the following also compiles, links and runs:

extern template class PropertyHelper<float>;

The cpp file looks like this:

const int PropertyHelper<float>::SomeValue(12);

PropertyHelper<float>::return_type
PropertyHelper<float>::fromString(const String& str)
{
    float val = 0.0f;

    if (str.empty())
        return val;

    //Some code here...

    return val;
}

template class PropertyHelper<float>;

The last line is the explicit instantiation definition.

So my question is most of all if I did everything correctly here according to the C++11 standard and secondly (if first is true) if the __declspec keyword is redundant in the context of the explicit instantiation declaration, or what I should do about it, as I did not find proper information in the MSDN docs.

Community
  • 1
  • 1
Ident
  • 1,184
  • 11
  • 25
  • Seems to have a similarity with this thread http://stackoverflow.com/questions/2505385/classes-and-static-variables-in-shared-libraries – Misgevolution Sep 14 '15 at 04:24
  • You really want a portable solution, ie independent on `__declspec` extensions. – Walter Sep 15 '15 at 07:46
  • @Walter could you be more specific? – Ident Sep 15 '15 at 07:47
  • Are all the types for which you want to specialise your template known at compile time of your library? If so, why don't you simply provide the member definitions and instantinations in a source file as with C++03? I don't understand the desire/need for C++11 features. – Walter Sep 15 '15 at 07:49
  • your definition for float is missing "SomeValue" – Les Sep 15 '15 at 14:10
  • @Les thank you, I fixed it – Ident Sep 15 '15 at 17:26
  • Have you though of precompiled headers? It is available in most of the modern compilers.It should reduce compilation time. – DawidPi Sep 15 '15 at 19:42
  • @DawidPi I have not looked into it deeply, but it seems like - although I could easily be wrong - that this is not a good idea for a library, or at least not very common. It might be worth considering though, but since we support multiple compilers this might be more work than benefit. – Ident Sep 15 '15 at 20:38
  • @Ident So as far as I know OpenCV is a library that uses this. They have even an option to disable precompiled headers in a CMake configuration files, also stdafx.h uses this as far as i know, so precompiled headers are used in libraries, I guess. I do not think it's a lot of work to do, even with different compilers, but yeah you should check this on your own :) – DawidPi Sep 15 '15 at 22:25

6 Answers6

16

The standard (from working draft of C++0x through working draft in 2014), section 14.7.2 describes Explicit instantiation, stating that there are two forms of explicit instantiation, definition and declaration. It says, "an explicit instantiation declaration begins with the extern keyword." It further specifies that declarations, using extern, do not generate code.

Care must be taken to ensure the declarations are issued within the namespace of the template class declaration, or specifically reference the namespace in the qualified name, as in:

namespace N {
 template< class T > void f( T& ) {}
}

template void N::f<int>(int &);

Instantiating a template function, and generating it's code (definition). Whereas:

extern template void N::f<int>(int &);

Instantiates a template function for type int as a declaration, but does not generate code. The extern keyword informs the compiler that the code will be provided at link time from another source (possibly a dynamic library, but the standard doesn't discuss that platform specific concept).

Further, it is possible to instantiate members and member functions selectively, as in:

namespace N {
template<class T> class Y { void mf() { } };
}

template void N::Y<double>::mf();

This generates the code only for the function mf(), for doubles. It is, therefore, possible to declare instantiations (using extern), and then define instantations (without extern) for specific parts of a template type. One could elect to generate code or members for some portions of a template class within every compilation unit (inline), and force the generation of other portions of code into a particular compilation unit or library.

IBM's Knowledge Center article for their XLC V 11.1 compiler, supporting draft C++0x, discusses the strategy for using the extern keyword when building libraries. From their example, and the draft standards documents over several years (which have been consistent from 2008 forward on this subject), it's clear that extern has limited applicability to the specifics of dynamic libraries, but in general is limited to controlling where generated code is placed. The author will still be required to adhere to platform specific demands relative to dynamic linking (and loading). That is beyond the purpose of the extern keyword.

Extern is equally applicable to static libraries or dynamic libraries, but the limitation on a library design is significant.

Say a template class declaration, presented in a header file, exists like:

   namespace N
    {
     template< typename T >
     class Y
        { 
          private:

          int x;
          T v;

          public:

          void f1( T & );
          void f2( T &, int );
        };    
    }

Next, in a CPP file:

namespace N
{
 template< typename T> void Y<T>::f1( T & ) { .... }
 template< typename T> void Y<T>::f2( T &, int ) { .... }
}

Now consider the potential uses of Y. Consumers of the library may only require instantiations of Y for int, float and double. All other uses would be of no value. This is a design point of the author of the library, not some general notion about this concept. For whatever reason, the author only supports those three types for T.

To that end, explicit instantiation declarations may be included in the header file

extern template class N::Y< int >;
extern template class N::Y< float >;
extern template class N::Y< double >;

As this is processed by the user's various compilation units, the compiler is informed there will be code generated for these three types, but the code is not generated in each compilation unit as the user builds. Indeed, if the author doesn't include the CPP file defining the functions f1 and f2 for template class Y, the user wouldn't be able to use the libary.

Assuming, for the moment, a static library is the intended product regarding template class Y (for simplification of this discussion), the author compiles the static library with the CPP defining functions f1 and f2, along with the explicit instantiation definitions:

template class N::Y< int >;
template class N::Y< float >;
template class N::Y< double >;

This will cause the code to be generated for template class Y on behalf of these three types, creating a static library. User code will now have to link to this library, but do nothing else to use the classes. Their compilation units will not generat code for template class Y, instead incorporating that code from the library.

The same concept applies to a dynamic library, but platform specifics regarding function declarations, dynamic loading and dynamic linking are not found in the standards for C++ through 2014 working drafts, regarding C++0x, C++11 or C++14, at present. The extern keyword in explicit template instantiations is limited to creating declarations, it's absence creates definitions (where code is generated).

This brings up the question regarding users of such a library intent on using Y for unsigned long, char, or some other type not provided in a dynamic or static library. The author has the choice of refusing to support this by not distributing source for the generation of code (the function definitions of f1 and f2 for template class Y). However, if the author did wish to support such usage, distributing that source, instructions would be required for the user to generate a new library to replace the existing one, or generating a second library for the additional types.

For either case, it would be wise to separate the explicit instantiation definitions in a CPP file which includes the header declaring template class Y, the including a header of the funtion definitions of f1 and f2 for template class Y (as opposed to the practice of including a CPP file, which could also work). In this way, the user would create a CPP file that includes the header for template class Y, then the function definitions for template class Y, then issuing the new explicit instantiation definitions:

#include "ydeclaration.h" // the declaration of template class Y
#include "ydefinition.h"  // the definition of template class Y functions (like a CPP)

template class N::Y< unsigned long >;
template class N::Y< char >;

For a static library, little else would be required, and the user could opt to build the additional compilation unit within their project, obviating the need for a static library target.

However, if the user wanted to build a dynamic library, care would be required regarding the platform specific code regarding dynamic libraries on a particular platform. Specifically on Windows, for example, this may mean explicitly loading the new dynamic library.

Considering the complications involved with creating dynamic libraries, it is a wonder anyone ever does. Sometimes there simply is no other choice. Key to the decision is to determine exactly why a dynamic library should be used. In the ancient epoch of computers with under 1 GByte of RAM, one of the justifications was saving memory by sharing code, but for any particular library, what is the probability that code sharing would result in saving RAM? For something as common as the C runtime, or Windows MFC DLL's, it may be highly probable. Libraries which provide highly targeted services, on the other hand, are more likely to be used by only one running program.

One really good purpose is the concept of plug in behaviors. Browsers, IDE's, photo editing software, CAD software and others benefit from an entire industry of applications distributed as plugins to existing products, which are distributed as dynamic libraries.

Another justification is distributing updates. While this is an attractive theory, the practice can cause more problems than it's worth.

Another common justification is "modularity". To what end, though? Separating compilation units already reduces compile times. Dynamic libraries will affect link times more than compile times, but is that worth the additional complexity?

Otherwise, however, providing dynamic libraries, especially for a fairly small product, is not really worth the trouble.

An entire book could be written on the subject of writing portable dynamic libraries applicable to both Windows and Linux.

On Windows, the choice of using __declspec(dllexport/dllimport) can apply to an entire class. It's important to realize, however, that whatever compiler is used to generate the DLL can only be used with targets built with that same compiler, or compatible compilers. Within the MS VC lineage, many versions are NOT compatible with each other at this level, so a DLL built with one version of Visual Studio may not be compatible with other versions, causing a burden on the author to generate DLL's for every possible compiler/version to be supported.

There are similar issues regarding static libraries. Client code must link with the same version and configuration of the CRT as the DLL is built with (is the CRT statically linked?). Client code must also elect the same exception handling settings and RTTI settings as the library is built with.

When portability to Linux or UNIX (or Android/iOS) is to be considered, the problems magnify. Dynamic linking is a platform specific concept not handled in C++.

Static libraries would probably be the best approach, and for those __declspec(dllexport/dllimport) should not be used.

With all that said against dynamic libraries, here's one of the many ways to implement that in Windows (completely inapplicable to Linux/UNIX/etc).

A simple (perhaps naive but convenient) approach is to delcare the entire class as exported from a DLL (imported in client code). This has the slight advantage over declaring each function as exported or imported, because this approach includes class data, and just as importantly, AUTOMATIC assignment/destructor/constructor code C++ may build for your class. That could be vitally important if you didn't carefully attend to those and export them manually.

In a header to be included for the DLL production:

#define DLL_EXPORT // or something similar, to indicate the DLL is being built

That will be included at the top of your header declaring your library's template classes. The header declaring DLL_EXPORT is only used in a project configured to compile the DLL library. All client code will import an otherwise empty version. (Myriad Other methods for doing this exist).

As such, DLL_EXPORT is defined when building for the DLL, not defined when building client code.

In the header for your library's template class declarations:

#ifdef _WIN32 // any Windows compliant compiler, might use _MSC_VER for VC specific code
#ifdef DLL_EXPORT
#define LIB_DECL __declspec(dllexport)
#else
#define LIB_DECL __declspec(dllimport)
#endif

Or whatever you prefer to see in place of LIB_DECL as a means of declaring entire classes exportable from DLL's, imported in client code.

Proceed with class declarations as:

namespace N
    {
     template< typename T >
     struct LIB_DECL Y
        { 
          int x;
          T v;
          std::vector< T > VecOfT;

          void f1( T & );
          void f2( T &, int );
        };    
    }

Explicit instantiation declarations for this would be:

extern template struct LIB_DECL N::Y< int >;
extern template struct LIB_DECL N::Y< float >;
extern template struct LIB_DECL N::Y< double >;

extern template class LIB_DECL std::vector< int >;
extern template class LIB_DECL std::vector< float >;
extern template class LIB_DECL std::vector< double >;

Take note of the std::vector used in class Y in this example. Consider the problem carefully. If your DLL library uses std::vector (or any STL class, this is just an example), the implementation you used at the time you build the DLL must match what the user chooses when they build client code. The 3 explicit instantiations of vector match the requirements of the template class Y, and instantiate std::vector within the DLL, and with this declaration become exportable from the DLL.

Consider, how would the DLL code USE std::vector. What would generate the code in the DLL? It's obvious from experience that the source for std::vector is inline - it's a header only file. If your DLL does instantiate vector's code, how would client code access it? Client code would "see" std::vector source and attempt it's own inline generation of that code where the client access std::vector functions. If, and only if, the two are an exact match would that work. Any difference between the source used to build the DLL and the source used to build the client would differ. If client code had access to the std::vector in template class T, there would be chaos if the client used a different version or implementation (or had different compiler settings) when using std::vector.

You have the option of explicitly generating std::vector, and informing client code to use THAT generated code by declaring std::vector as an extern template class, to be imported in client code (exported in DLL builds).

Now, in the CPP where the DLL is built - the function definitions of the library - you must explicitly instantiate definitions:

template struct LIB_DECL N::Y< int >;
template struct LIB_DECL N::Y< float >;
template struct LIB_DECL N::Y< double >;

template class LIB_DECL std::vector< int >;
template class LIB_DECL std::vector< float >;
template class LIB_DECL std::vector< double >;

In some examples, like MS KB 168958, they suggest making the extern keyword a define, modifying this plan as:

#ifdef _WIN32 // any Windows compliant compiler, might use _MSC_VER for VC specific code
#ifdef DLL_EXPORT
#define LIB_DECL __declspec(dllexport)
#define EX_TEMPLATE
#else
#define LIB_DECL __declspec(dllimport)
#define EX_TEMPLATE extern
#endif

So that in the header file for both DLL and client builds, you may simply state

EX_TEMPLATE template struct LIB_DECL N::Y< int >;
EX_TEMPLATE template struct LIB_DECL N::Y< float >;
EX_TEMPLATE template struct LIB_DECL N::Y< double >;

EX_TEMPLATE template class LIB_DECL std::vector< int >;
EX_TEMPLATE template class LIB_DECL std::vector< float >;
EX_TEMPLATE template class LIB_DECL std::vector< double >;

While this has the advantage of issuing these lines once, in the header, I personally prefer observing the extern keyword clearly in use in the header, so that I know without a doubt the only place code generation can take place is in the CPP of the DLL build (where they appear, a second time, without the extern keyword). In this way, the extern's in the header are forward declarations, which do not conflict with the explicit instantiation definitions in the CPP, and it avoids obfuscating the extern keyword when used in the client code. It is, perhaps, a peculiar preference of my own.

You may be thinking, "what about other client code and std::vector". Well, it's important to consider. Your header file includes std::vector, but remember, your DLL is built with the code available to YOU at compile time. Your client will have their own header, it within like versions of VC, that should be the same. SHOULD is not a good plan, though. It could be different. They may just assume VC 2015 is the same and plow ahead. Any difference, be it object layout, actual code..anything, could doom the running application with very strange effects. If you export your version, they client would be well advised to include the explicit instantiation declarations throughout all their compilation units, so everything uses YOUR version of std::vector...but, there's a serious catch.

What if some other library did this, too, with yet a different version of std::vector?

It makes the use of STL a bit nasty in these contexts, so there's one fairly good design choice that eliminates this. Don't expose any use of STL.

If you keep all use of STL private to your library, and never expose an STL container to client code, you're probably in the clear. If you choose that in design, you have no need to explicitly instantiate the std::vector (or any STL) in your library.

I included the example so as to discuss it, how it's documented by MS (KB 168958), and why you probably don't want to do it. However, the reverse scenario comes up, too.

In the original inquiry, the use of std::string (or one of it's alternatives) is to be used. Think of this: in the DLL, how will that use of std::string instantiate? What if there is any difference between the std::string code available when the DLL was built compared to that which is used by the client whey they build? The client could, after all, elect to use some other STL than provided by MS. Of course, you could stipulate they not do that, but...perhaps you could explicitly instantiate std::string as extern WITHIN your DLL. In that way, you have no STL code built within the DLL, and the compiler is now informed that it should find that code built by the client, not within the DLL. I suggest it for research and thought.

The insidious problem you face is this: this will all work on your machine, in your tests, because you're using one compiler. It would be flawless there, but could fail spectacularly in client builds because of code differences, or setting differences, subtle enough to escape warnings.

So, let's assume you agree and skip the last three lines in the examples which instantiate std::vector...is it done?

That depends on your IDE settings, which I'll leave to you. The question centered around the use of __declspec(dllxxxxx) and it's usage, and there are several ways to implement its use, I focused on one. Whether or not you must explicitly load the library, rely on automatic dynamic linking features, consider the DLL_PATH...these are general DLL building topics you either know, or are beyond the real scope of the question.

JVene
  • 1,611
  • 10
  • 10
  • 2
    This was a very elaborate response, thank you. It was also interesting to read the long discussion regarding dynamic vs static libraries. Currently our focus is on dynamic libraries while static linkage is only supported half-way and have no one maintaining/testing it. I have no problems changing this in the future. However I remember there were issues /concerns regarding static linkage as well, I do not remember the details though and was not part of the decision-making process on which type of linkage to prefer. Also we have had people who mixed compilers and then complained ;) – Ident Sep 16 '15 at 19:32
  • Most likely it would be best to raise a compile-time error if different compilers (or just versions of compilers) are mixed, which could be done using a CMake generated file created during the libraries configuration, which would then be processed to determine compiler equality when the file is included and for the actual application. – Ident Sep 16 '15 at 19:36
  • Back-to-topic: regarding the extern keyword you gave the most elaborate answer and references. Other answers were definitely helpful too in other respects. But yours was the most helpful regarding the actual question and the extern keyword so I will accept yours. – Ident Sep 16 '15 at 19:37
  • Kind of you...thanks. Indeed, many of the same kinds of issues are related to both static and dynamic libraries, but the "selling" factor to me is that static libraries give problems to your programming customers, but dynamic library problems hand those over to THEIR customers! It is otherwise similar outside of loading and (sometimes linking to functions via pointer) to gain access to the DLL. Static has fewer issues, but THE SAME issues relative to compiler/settings mismatches...absolutely the same, except now THEIR customers get to have those problems, too. – JVene Sep 16 '15 at 19:43
  • Our DLL is very likely to be linked only to the final executable so there would be no further customers. Regarding the templates: I have not yet come to a conclusion how to go on about them, the whole topic seems extremely complicated. I will have to spend some days digging into this when I have time. – Ident Sep 16 '15 at 20:06
  • Sorry, that doesn't makes sense to me. If you make a DLL, that DLL has to be included and becomes part of the installation of your client's product, unless they subsequently wrap that DLL code into something that transforms it into a static unit (which defeats the purpose of making a DLL) - those customers may have issues themselves, i.e. what's called DLL hell. I realize their customers won't see code, but what I'm saying is the DLL itself is a potential hazard to the consumer of their product (this is general to all DLL usage). – JVene Sep 16 '15 at 20:11
  • On templates: which (of many possible)? The types to provide (int,float,double), or the use of STL (especially returning STL objects) – JVene Sep 16 '15 at 20:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/89855/discussion-between-ident-and-jvene). – Ident Sep 16 '15 at 20:17
  • You are correct, I misunderstood your usage of the word "customer" in this context. Sorry for the confusion. – Ident Sep 16 '15 at 20:21
4

Here is a quote from the standard:

For a given set of template arguments, if an explicit instantiation of a template appears after a declaration of an explicit specialization for that template, the explicit instantiation has no effect. [...]

So your explicit instantiation declaration

extern template EXPORTDEF class PropertyHelper<float>;

has no effect since it appears after the explicit specialization

template<>
class EXPORTDEF PropertyHelper<float>
...

Same with your explicit instantiation definition

template class PropertyHelper<float>;

in the cpp file (I assume that cpp file includes class.h, since otherwise the code there wouldn't compile).

Are you sure you want to explicitly specialize PropertyHelper<float>?

Daveed V.
  • 946
  • 8
  • 8
  • In specific I want to provide fromString and toString functions that can be accessed via PropertyHelper::toString(...) and fromString(...) - You ask if I want to explicitly specialise it at all, what other options can you think of here? – Ident Sep 15 '15 at 07:19
  • I want to add to my previous comment that I do need specific behaviour in some of the functions for specific types that cannot be replicated simply by operators or other inbuilt mechanics. – Ident Sep 15 '15 at 20:20
  • Okay. For types (if any) where the generic implementation of PropertyHelper works, do not create an explicit specialization and put an "extern template ..." in the header, with a corresponding "template ..." directive in some appropriate .cpp file (you can dllimport in the header and dllexport in the .cpp file, I believe). For types where the generic implementation doesn't work, do not do that at all and use explicit specialization instead (which you can also dllimport/dllexport; an explicit specialization behaves mostly like a contemplate entity with a funny name). – Daveed V. Sep 16 '15 at 13:26
4

I may misunderstand your question (and I've never used Windows), so perhaps this answer is inappropriate.

If all the allowed template parameters for your template class are known at the compile time of your library (rather than that of third-party user code), then why not put all the definitions and instantinations into a source file (and hide it from the user)? This is standard old C++ technique (no fancy C++11 stuff needed).

// file.h:

template<typename T>
struct bar
{
  static_assert(/* condition here */, "invalid template argument");
  using return_type = /* definition here */;
  static return_type from_string(std::string const&);            // non-inline
};


// file.cc:

template<typename T>
typename bar<T>::return_type
bar<T>::from_string(std::string const&str)
{
  /* some code here */
}

template struct bar<float>;
template struct bar<int>;
// etc., covering all T which pass the static_assert in struct bar<T>

edit (in response to comment by Ident)

If you want to base from_string on operator>>(std::istream,T) for all but few special T, then you can simply specialise the template bar. Here is an implementation where all but special cases are dealt with entirely in the header file.

// file.h:

template<typename T>              // generic case: use  istream >> T
struct bar
{
  typedef T return_type;
  static return_type from_string(std::string const&str)
  {
    return_type x;
    std::istringstream(str) >> x; 
    return x;
  }
};

template<>
struct bar<std::string>           // string: return input
{
  typedef std::string const&return_type;
  static return_type from_string(std::string const&str)
  { return_type x; }
};

template<>
struct bar<special_type>          // special_type: implement in file.cc
{
  typedef special_type return_type;
  static return_type from_string(std::string const&str);  // non-inline
};

and file.cc similar to above (though now implementing template specialisation rather than instantinating a single template). You can also use SFINAE to envoke different behaviour not for individual types, but for types meeting certain conditions etc.

Finally, presumably you want to define a standalone function template from_string<T> (and hide bar<> inside a nested namespace details)

template<typename T>
inline typename details::bar<T>::return_type
from_string(std::string const&str)
{ return details::bar<T>::from_string(str); }

Note that we can define this also without reference to bar<T>::return_type using auto:

template<typename T>
inline auto from_string(std::string const&str)
 -> decltype(details::bar<T>::from_string(str))
{ return details::bar<T>::from_string(str); }
Walter
  • 44,150
  • 20
  • 113
  • 196
  • The from_string function is mostly similar but different for a couple of the types. Although we overload the << operator for writing the strings, there are special cases that cannot be handled generally just with this one operator and need type-specific special handling. If I understand correctly, your solution only works if there is no type-specific function code necessary, right or not? We also have a couple of other requirements like the return type being T in all cases except for our String type which has T& - We return the input directly to prevent a copy... – Ident Sep 15 '15 at 17:42
  • It is easy to specialise the `from_string` functions for some of the types and also easy for specialising the return type. I will edit the posted answer. – Walter Sep 16 '15 at 11:50
  • @Ident Didn't you mean the *operator >>* and writing *from* strings? – Walter Sep 16 '15 at 12:27
  • we provide both toString and fromString, thus also both >> and << for the types we use. In the code example i made i simplified the template to the minimum (only one method), sorry for the confusion – Ident Sep 16 '15 at 13:40
3

From the language definition

A.12 Templates [gram.temp]

At the bottom we find the rules in question (sorry about the formatting):

§ A.12 1224c ISO/IEC N4527

explicit-instantiation:
    extern opt template declaration
explicit-specialization:
    template < > declaration

So I would say the answer to the 2nd question is that you shouldn't need the __declspec for the extern declaration.

As for the first question I agree with Daveed V. above in that, according to the standard, the explicit declarations seem to be extraneous. [See: 14.8.1 1 Explicit template argument specification [temp.arg.explicit] of the above]

William Jones
  • 204
  • 1
  • 5
1

I tried this on VS2013. I found without the dllexport it links, but it doesn't run. Using depends.exe shows an unresolved import as expected. Anything implemented in a C++ file must be explicitly dllexport'ed for an EXE to link to it and templates are no different, so I don't understand how you're getting it to run. Try stepping into it in the debugger and verify that it's calling the code in the .cpp file in the DLL.

BTW you don't need dllimport. For the client just define EXPORTDEF as nothing.

  • Why do I not need dllimport? I assume you meant that having `extern template class PropertyHelper;` does not run as compared to adding dllexport in this line? – Ident Aug 18 '15 at 11:42
  • I just noticed I never mentioned I was using VS2015. There the code without exporting works, in run-time too. I cannot find information as to when to export the explicit instantiation declaration anywhere. You say "anything implemented" must be exported but this is a declaration, so I am very uncertain. – Ident Aug 18 '15 at 11:51
  • If you step into the function does it call the DLL? – Dave Emberton Aug 19 '15 at 12:35
  • As far as I could tell in VS2013 the extern template made no difference to anything and this mechanism is entirely unconnected to the dllexport mechanism.Maybe VS2015 does something differently. I've never known __declspec(dllimport) to be necessary; everything I've ever done with Windows DLLs links and runs without it. – Dave Emberton Aug 19 '15 at 12:43
1

You need to forward declare usages of the template class with the proper linkage (declspec import/export for visual studio or 'extern' for everything else) so that the compiler does not try to generate code for those imported types.

Specific details are in my answer to: C++ template specialization in different dll produces linker errors

class.h

#ifdef _WIN32
#    define TEMPLATE_EXTERN
#    ifdef EXPORT
#        define LIB_EXPORT __declspec(dllexport)
#    else
#        define LIB_EXPORT __declspec(dllimport)
#    endif
#else
#    define TEMPLATE_EXTERN extern
#    define LIB_EXPORT
#endif

class PropertyHelper<const T>
{
public:
    typedef typename PropertyHelper<T>::return_type return_type;

    static inline return_type fromString(const String& str)
    {
        return PropertyHelper<T>::fromString(str);
    }

    static const int SomeValue;
};

// forward declare the specialization
TEMPLATE_EXTERN template class LIB_EXPORT PropertyHelper<float>;

class.cpp

// define the symbol to turn on exporting
#define EXPORT
#include "class.h"
// explicitly instantiate the specialization
template class PropertyHelper<float>

test.cpp

#include "class.h"
int main() {
    PropertyHelper<float> floatHelper; // should be imported, class.h was not #include'ed with EXPORT defined
    return 0;
}
Community
  • 1
  • 1
Ben
  • 1,287
  • 15
  • 24