I have a third party dll that contains a template class with several specializations. I have my own specialization on Linux, trying to compile a windows dll however results in linker errors.
I tried around a bit and found out that the dllimport specification on the template header may be the cause and removing it would fix my problem. However I don't want to modify or copy the header since it could break with every update to the third party library.
Here is a minimal example to reproduce my problem
test.h - dll/so header:
#ifdef _WIN32
#ifdef EXPORT
# define LIB_EXPORT __declspec(dllexport)
#else
# define LIB_EXPORT __declspec(dllimport)
#endif
#else
# define LIB_EXPORT
#endif
template <typename A> class LIB_EXPORT Foobar
{
public:
virtual void helloWorld(){}
virtual ~Foobar(){}
};
test.cpp - dll/so impl:
#define EXPORT
#include "test.h"
template class __declspec(dllexport) Foobar<int>;
main.cpp - example program:
#include "test.h"
//explicit instanciation - does not help
template class __declspec(dllexport) Foobar<char>;
int main(int argc, char** argv)
{
Foobar<char> a;
a.helloWorld();
}
Is there a clean way to get a complete instantiation of Foobar in my executable ?
Compilers used: Visual Studio 2010, g++ mingw w64 4.9.1
I know you said that you dont want to modify the header since it could break the third party library updates, but the header that the template is defined in is not set up correctly. Hopefully you can get your vendor to modify their headers to be more import/export friendly.
The goal:
Define (export) a template specialization in a dll/so and then use (import) that specialization to your exe.
test.h
We dont want to only import or export every specialization of the template, so we remove the LIB_EXPORT from the class.
template <typename A> class Foobar {
...
}
We do want to import/export a specific specialization of the template however. We will forward declare the specialization and then explicitly instantiate it later in the compilation unit you want it to reside in.
Since you are also building with gcc, you will want to make use of the 'extern' keyword. Visual Studio 2010 does not implement it for templates.
#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
The final forward declaration looks like
TEMPLATE_EXTERN template class LIB_EXPORT Foobar<int>;
test.cpp
We explicitly instantiate the template class here since our efforts in the header file have turned off the automatic instantiation features of the compiler.
#define EXPORT
#include "test.h"
template class Foobar<int>;
main.cpp
The default state of the headers is to implicitly instantiate the Foobar class with any type that is not int. The int specialization has been specifically tagged as 'export' on gcc and __declspec(dllimport) on win32. So you are able to make other specializations wherever you wish.
#include "test.h"
// explicit instantiation
template class Foobar<char>;
int main(int argc, char** argv)
{
Foobar<char> a;
a.helloWorld();
}
Related
In visual studio I've set configuration type as .dll instead of .exe and because of that sometimes I need to use __declspec(dllexport) or __declspec(dllimport) . So I've created macros for them inside header file called "Core"
#pragma once
#ifdef B5_PLATFORM_WINDOWS
#ifdef B5_BUILD_DLL
#define B5_API __declspec(dllexport)
#else
#define B5_API __declspec(dllimport)
#endif // B5_BUILD_DLL
#else
#error Bos5 only supports Windows!
#endif // B5_PLATFORM_WINDOWS
I have a class "Application" inside of my namespace "Bos5" which uses BS_API
#pragma once
#include "Core.h"
namespace Bos5 {
class B5_API Application
{
public:
Application();
~Application();
void Run();
};
}
everything works fine inside this project but when I reference this to another project for some reason visual studio thinks B5_API is a class and "Application" isn't. therefore code below doesn't compile saying namespace Bos5 doesn't have struct or class called "Application"
#include <FinalBos5.h>
class Sandbox : public Bos5::Application
{
public:
Sandbox(){}
~Sandbox(){}
};
int main() {
}
I think this image can better explain what I'm saying
Okay I've fixed it. turns out problem was completely different thing. My preprocessor didn't save some of the definitions so in second project B5_API wasn't getting defined
I am trying to compile an application involving both C and C++ files. With one particular header I face issues. The file in question (a C++ header file), would look something like this:
#ifndef TASK_H
#define TASK_H
#include "MyCCPObject.h"
int foo1(int);
int foo2(int);
int fooObject(MyCCPObject myCppObject); // Function involves a Class "MyCCPObject" type
#ifdef __cplusplus
extern "C" {
#endif
int foo3(void); // Function called in a C file
#ifdef __cplusplus
}
#endif
#endif //TASK_H
I have a function fooObject() which has a MyCCPObject class type as a parameter. Also, one of the functions, foo3() would be called from a C file.
When the C compiler, compiles this header, I get the following error: "error: #20:identifier "class" is undefined". To avoid this, I had to:
Place the fooObject() declaration within compiler guards:
#ifdef __cplusplus
int fooObject(MyCCPObject myCppObject);
#endif
Place compiler guards also in the class declaration in the header file MyCCPObject.h:
#ifdef __cplusplus
class MyCCPObject
{
public:
MyCCPObject(uint32_t val);
private:
uint32_t value;
};
#endif
Note: The MyCCPObject would not be called in any C file.
So, what would be a better approach, when I have a C++ header file, which involves:
Function would involves a class object
A extern call to a C file
Use separate headers for your C and C++ code.
Move the foo3 declaration (including the __cplusplus guards) inside a separate header. Let's call it Foo3.h
You now have the following files:
Task.h - contains the declarations for foo1 and foo2, fooObject and includes MyCCPObject.h
Foo3.h - contains the declarations for foo3
Task.cpp - includes Task.h and Foo3.h and provides definitions for foo1, foo2 and foo3
App.c - includes Foo3.h and uses foo3
From your build system (make, cmake etc.), when building the C++ library, add the files Task.h, Foo3.h, Task.cpp (and the other files related to MyCCPObject)
When building the C application, add only Foo3.h and App.c. That way, the other headers (which contain C++ code) will not be compiled and hence not give out any errors.
The makers of C++ wrote a FAQ which is also giving some guidance on how to mix C and C++. They are also looking at the possibility to use C++ objects from C code.
Option 1: If you just want the C compiler to be able to parse your task.h header file, then you could hide the C++ parts my using #ifdef __cplusplus:
#ifndef TASK_H
#define TASK_H
#ifdef __cplusplus
#include "MyCCPObject.h"
int foo1(int);
int foo2(int);
int fooObject(MyCCPObject myCppObject); // Function involves a Class "MyCCPObject" type
extern "C" {
#endif
int foo3(void); // Function called in a C file
#ifdef __cplusplus
}
#endif
#endif //TASK_H
Option 2: If you want to make the fooObject function accessible from C, then you can change MyCppObject.h to provide the full class information to C++ and only a minimal typedef for C. The typedef makes sure that C understands just the class name MyCCPObject without writing class or struct before it.
#ifdef __cplusplus
class MyCCPObject
{
public:
MyCCPObject(uint32_t val);
private:
uint32_t value;
};
#else
typedef struct MyCCPObject MyCCPObject;
#endif
and task.h to
#ifndef TASK_H
#define TASK_H
#include "MyCCPObject.h"
int foo1(int);
int foo2(int);
#ifdef __cplusplus
extern "C" {
#endif
int fooObject(MyCCPObject *myCppObject); // Function involves a Class "MyCCPObject" type
int foo3(void); // Function called in a C file
#ifdef __cplusplus
}
#endif
#endif //TASK_H
Please note that I needed to change the signature of fooObject to take a pointer to the object as the C code does not see the complete class and does not know the size of the object.
This question already has answers here:
How do I safely pass objects, especially STL objects, to and from a DLL?
(4 answers)
Closed 7 years ago.
I have a class which has two overloaded functions. How do I export it from a dll and also how to use it by other C++ classes? My class looks like this:
#define DECLDIREXP __declspec(dllexport)
#define DECLDIRIMP __declspec(dllimport)
class DECLDIREXP xyz
{
public:
void printing();
void printing(int a);
};
using namespace std;
void xyz::printing()
{
cout<<"hello i donot take any argument";
}
void xyz::printing(int a)
{
cout<<"hello i take "<< a <<"as argument";
}
A common approach is to have a single macro (let's call it EXPORT) which either expands to dllimport or dllexport depending on whether some sort of "building the DLL right now" define is set, like this:
#ifdef MAKEDLL
# define EXPORT __declspec(dllexport)
#else
# define EXPORT __declspec(dllimport)
#endif
class EXPORT xyz {
// ...
};
The idea is that when building your DLL, you add MAKEDLL to the preprocessor definitions. That way, all the code will be exported. Clients who link against your DLL (and hence include this header file) don't need to do anything at all. By not defining MAKEDLL, they will automatically import all the code.
The advantage of this approach is that the burden of getting the macros right is moved from the many (the clients) to just the author of the DLL.
The disadvantage of this is that when using the code above as it is, it's no longer possible to just compile the code directly into some client module since it's not possible to define the EXPORT macro to nothing. To achieve that, you'd need to have another check which, if true, defines EXPORT to nothing.
On a slightly different topic: in many cases, it's not possible (or desired!) to export a complete class like that. Instead, you may want to just export the symbols you need. For instance, in your case, you may want to just export the two public methods. That way, all the private/protected members won't be exported:
class xyz
{
public:
EXPORT void printing();
EXPORT void printing(int a);
};
As I remember, normally, you export not a class but a factory function that creates a new instance of class and returns a pointer. The class declaration resides in header file for compile time.
I may be wrong about the example (that was long ago), but here how it should approximately look like:
Header file (.h):
class MyClass { ... };
extern "C" DLL_API MyClass* createMyClass();
Source file (.cpp):
DLL_API MyClass* createMyClass() {
return new MyClass();
}
Define MY_DLL_EXPORT while compiling, see foraidt's answer example.
One another option:
Use the default defined macro local to the project.
You can see the default defined macros local to the project in the below location:
Properties -> C/C++ -> Preprocessor -> Preprocessor Definition.
Example:
Suppose your Project Name is: MyDLL
Default Macro Local to that project: MYDLL_EXPORTS
#ifdef MYDLL_EXPORTS
/*Enabled as "export" while compiling the dll project*/
#define DLLEXPORT __declspec(dllexport)
#else
/*Enabled as "import" in the Client side for using already created dll file*/
#define DLLEXPORT __declspec(dllimport)
#endif
class DLLEXPORT Class_Name {
//....
}
When compiling your library you should define a macro (command line preprocessor definition), let's call it MY_DLL_EXPORT.
Then in your library's code do something like this:
#ifdef MY_DLL_EXPORT
# define DLL_API __declspec(dllexport)
#else
# define DLL_API __declspec(dllimport)
#endif
class DLL_API some_class { /*...*/ }
I've created Dynamic library project Foo and it has following code in the Foo.h:
#pragma once
#include <memory>
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
class MYLIB_API Foo
{
};
template class MYLIB_API std::tr1::shared_ptr<Foo>;
typedef std::tr1::shared_ptr<Foo> FooPtr;
I use Foo class from my ConsoleApplication1:
#include "stdafx.h"
#include "Foo.h"
template class std::tr1::shared_ptr<Foo>; // (1)
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
{
std::tr1::shared_ptr<Foo>(new Foo()); // (2)
return 0;
}
The code above is compiled without error/warnings. I use Visual Studio 2008 (v90) toolset to compile this. Both projects is compiled with /W4.
Questions:
1. Why (1) doesn't produce any compiler errors/warnings? I've expected here something like C2011 type redefinition. I suspect (1) is ignored.
2. How many instantiations of std::tr1::shared_ptr<Foo> there are? As it is compiled, I've expected that there two instantiation: one in the Foo and another in the consoleapplication1.
3. Which instantiation (if there are many) is used at (2)?
UPDATE1:
I compiled this with Assembly With Source Code (/FAs), and seems that both Foo and ConsoleApplication1 contain implementation of shared_ptr<Foo>. Doesn't this mean that there are two shared_ptr<Foo> explicit instantiation?
(1) isn't a redefinition, it's a forward declaration.
(2) There only needs to be one. I don't know why you expect any more. I supposed it's possible that a given compiler might produce more than one, but why? and it would figure pretty high on their bug list if it did.
(3) See (2).
I'm working on a C++ project built with Visual Studio 2008.
I'm defining (in a project producing a dll) a template class and a derived, not-template, one:
template <class T>
struct DLL_EXPORT Base {
int base() { return 1; }
};
struct DLL_EXPORT Deriv: public Base<Deriv> {
int deriv() { return 1; }
};
the DLL_EXPORT is the usual stuff:
#ifdef COMPILING_MY_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
Now, when I'm trying to use my Deriv class in another project, the trouble is starting. If I use only the function from Deriv, it works well:
Deriv d;
d.deriv();
But if I try to call d.base(), I get the following linker error:
error LNK2019: unresolved external symbol "__declspec(dllimport) public: int __thiscall Base<struct Deriv>::base(void)" (__imp_?base#?$Base#UDeriv####QAEHXZ)
If, elsewhere in my dll code, I'm using the d.base() function, it works well, and the linker error disappear in the "user" project. And if I remove the template part (it's not really useful in my example, but in the real-life situation it is), everything is fine.
It looks like the function don't get compiled if it's not used, or something equivalent. Any idea ?
Thanks in advance.
don't use __declspec(dllexport) on template definitions since they are not exported (they are not real code until instantiated).
They are only in the header file for usage by sources. You can use __declspec(dllexport) on specializations of templates ,but you must instatiated those at least once in your DLL.
If that is your problem, you might be able to fix it by explicitly instantiating the template.
In one of the .cc files in your DLL (not header file), include your template definition and write:
template int Base<Deriv>::base();
This should force the compiler to put the code for Base<Deriv>::base() into that compilation unit so your linker can find it.
I don't know much about this, but I think you might be able to fix it by writing
template struct Base<Derive>;
somewhere in your source file. (or perhaps template DLL_EXPORT struct Base<Derive>;)