I encountered a strange behavior for an msvc c++ build while compiling two shared libraries and an executable. The complete setup can be found on GitHub
Information:
platform toolset is v143
4 projects: 1 header only, 2 DLL`s, 1 executable
The 4 projects are:
HeaderOnlyInterface (header only)
Implementation (DLL)
Factory (DLL)
SharedLibsTest (EXE)
Projects 2 - 4 can include all header files from HeaderOnlyInterface.
HeaderOnlyInterface consists of two header files:
DLLExport.h
#pragma once
#ifdef MAKEDLL
# define EXPORT __declspec(dllexport)
#else
# define EXPORT __declspec(dllimport)
#endif
IFoo.h
#pragma once
#include "DLLExport.h"
class EXPORT IFoo
{
public:
virtual ~IFoo() = 0 {}
virtual void Bar() = 0;
};
The next project is Implementation which implements the defined abstract class. This class has MAKEDLL defined as PREPROCESSOR macro. I only show the header because the source is unnecessary:
Foo.h
#pragma once
#include <IFoo.h>
class EXPORT Foo : public IFoo
{
public:
void Bar() override;
};
Going on to Factory, which also defines MAKEDLL and links against Implementation.lib, I have these two files:
FooFactory.h
#pragma once
#include <IFoo.h>
class EXPORT FooFactory
{
public:
static IFoo* Create();
};
FooFactory.cpp
#include "FooFactory.h"
#include <Foo.h>
IFoo* FooFactory::Create()
{
return new Foo();
}
And at the last, I have a main method in an Executable, which links against Factory.lib:
#include <FooFactory.h>
int main()
{
auto* foo = FooFactory::Create();
foo->Bar();
delete foo;
}
For me, the strange thing is, that everything compiles and works fine until now. I have no compile or linker warnings.
The strange thing for me is the compilation and linkage of Factory.lib (and NOT against Implementation.lib).
This library imports a class and exports it also and I do not know why it works. I try to explain my thinking by providing in (pseudo-) code what happens if FooFactory.cpp is pre-processed:
class __declspec(dllexport) IFoo
{
public:
virtual ~IFoo() = 0 {}
virtual void Bar() = 0;
};
class __declspec(dllexport) FooFactory
{
public:
static IFoo* Create();
};
class __declspec(dllexport) Foo : public IFoo
{
public:
void Bar() override;
};
IFoo* FooFactory::Create()
{
return new Foo();
}
The macro is the same for all compiled libs meaning that Factory.lib is exporting all classes. But it does not compile the implementation of Foo::Bar(). Also, it is somehow able to import the implementation because the code works fine. I am not sure why it works, and what really happens under the hood.
If someone could lighten me up on this behavior and also maybe could explain why I should do it not like this or maybe why this is a good idea to do it like this, that would be really helpful.
Thanks!
Related
I have a main program that, in the course of its work, loads a dynamic library, imports an instance of a class from there, and executes its method.
The main program has a Test class and two files Test.h Test.cpp.
Test.h:
class Test {
public:
virtual void work() = 0;
}
class Test2 : public Test {
public:
void work() override;
virtual void test() = 0;
}
test.cpp:
void Test2::work(){
/*do something*/
}
Accordingly, this code is compiled in the main program.
To create a dynamic library (.so/.dll), only the Test.h file is provided. Then the code of the library itself looks like this:
lib.cpp:
#include "Test.h"
#include <iostream>
class Test3 : public Test2 {
public:
void test() override {
std::cout<<"I'm test 3"<<std::endl;
}
}
extern "C" BOOST_SYMBOL_EXPORT Test3 plugin;
For linux, everything builds without problems and subsequently this .so library works correctly, but for .dll on Windows it gives an error: "undefined reference to `vtable' for Test2". That is, it requires me to provide an implementation of the virtual function work (). Is there any way to get around this? I use regular CMake to build.
cmake.txt:
add_library(lib SHARED Test.h lib.cpp)
I got a weird C++ question and have no idea how to resolve it.
I have a public header:
// a.h
#ifdef A_EXPORTS
#define A_API __declspec(dllexport)
#else
#define A_API __declspec(dllimport)
#endif
struct A_API IWorker
{
virtual bool foo() { return false; }
};
I built a.DLL with -DA_EXPORTS.
But my test.exe just needs a mocked IWorker.
// test.cpp
#include "a.h"
class CMockWorker : public IWorker
{
public:
bool foo() override { return true; }
};
I built test.exe, and found it depends on a.DLL because of external symbol public: __cdecl IWorker::IWorker(void) __ptr64
I know that IWorker has a compiler-generated constructor because of the vtbl, but could it be inline so we can get rid of a.DLL?
Thanks!
As #hans-passant mentioned, yes, once I removed the definition of virtual methods, problem resolved.
// a.h
struct IWorker
{
virtual bool foo() = 0;
};
But in existing project, some so-called interface class has similar dummy body and hard to refactor them.
So I really want to find the ideal solution to force-inline the c'tor, if has. Maybe not today, hope it could be impl-ed in the future.
I am using Visual Studio and am working on some C++ code that works like this in class A:
class A
{
public:
//......
static void foo();
};
class B is exported:
class __declspec(dllexport) B
{
public:
//...
void bar()
{
A::foo();
}
};
A and B are compiled to AB.dll (and AB.lib).
Now the main program:
int main()
{
B b;
b.bar();
return 0;
}
When compiling the main program and linking it to AB.lib,
it complains about the A::foo() as unresolved external symbol
(in this case A::foo is a static function).
Do I need to somehow export A::foo() or could it be that
I introduce errors somewhere? Any help is appreciated.
Modified:
1) Sorry for the type, it should be dllexport, not dllimport
2) in my actual project, the implementation is in .cpp files. They are not inline functions.
Thanks.
I assume code showed is in your .h header file, then when you header file says:
class __declspec(dllimport) B
{
public:
//...
void bar()
{
A::foo();
}
};
First: you say B class is IMPORTED, it works for your main app, but not works for your dll.
Second: B::bar() IS NOT IMPORTED FROM dll, instead IS DIRECTLY IMPLEMENTED IN YOUR MAIN APP (compiler is readding directly in your header file, and it is not trying to import from dll)
Recomendations:
FIRST: redefine your header file like this:
class __declspec(dllimport) B
{
public:
//...
void bar();
};
And implement method B::bar in a cpp file in dll project
SECOND: remove class A from your header file (if you can)
I've got simple singleton with
class Options {
private:
Options()
{};
Options(Options const&);
void operator=(Options const&);
public:
static Options& get()
{
static Options INSTANCE;
return INSTANCE;
}
};
I've got this definition in header in shared library A
but when I call get() first from application B I see how there instance is creating and then I call methods from shared library C and using get() there I'm getting yet another instance...
How can I have something alike Application level singleton? (is it something with extern keyword?)
It should work under windows provided:
you consistently use __declspec(dllexport) for export and __declspec(dllimport) for import
the implementation of Options::get() is moved in a cpp file so that it only exists in the DLL.
Here is an example :
options.dll build with OPTIONS_EXPORT symbol defined:
options.h:
#pragma once
#ifdef OPTIONS_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif
class DLLAPI Options
{
private:
Options()
{};
Options(Options const&);
void operator=(Options const&);
public:
static Options& get();
int val;
};
options.cpp:
#include "Options.h"
Options& Options::get()
{
static Options INSTANCE;
return INSTANCE;
}
A dummy c.dll using options.dll build with C_EXPORT symbol defined and linked with options.lib:
c.h:
#pragma once
#ifdef C_EXPORTS
#define dll __declspec(dllexport)
#else
#define dll __declspec(dllimport)
#endif
#include "../a/options.h"
namespace C {
dll Options& relay();
};
c.cpp:
#include "c.h"
Options& C::relay() {
Options& opt = Options::get();
return opt;
}
And a minimal main linked with both options.dll and c.dll:
#include <iostream>
#include "../a/options.h"
#include "../c/c.h"
int main() {
Options& o1 = Options::get();
o1.val = 12;
Options& o2 = C::relay();
std::cout << ((o1.val == o2.val) ? "Ok" : "Ko") << std::endl;
return 0;
}
Output is as expected: Ok
The issue is that all of your applications and libraries compile their own copy of your class into the library, since you tell all of them how the class should look.
First of, start by moving the implementation of theget function into the source file. After this is done and you compile you should see that your shared libraries does not know how the function should look and they will not compile (linker errors except in the library that contains the class).
From there start to fix the compilation by letting the application and other libraries know where to link the function from.
On windows you need to export the class in the library that it is implemented in using __declspec(dllexport).
On the library and possibly the application you need to use __declspec(dllimport) to import the class.
On Linux this should not be necessary.
If I'm creating a static library with a header file such as this:
// Myfile.h
#include "SomeHeaderFile.h" // External library
Class MyClass
{
// My code
};
Within my own project I can tell the compiler (in my case, Visual Studio) where to look for SomeHeaderFile.h. However, I don't want my users to be concerned with this - they should be able to include my header without having to inform their compiler about the location of SomeHeaderFile.h.
How is this type of situation normally handled?
This is a classic "compilation firewall" scenario. There are two simple solutions to do:
Forward-declare any classes or functions that you need from the external library. And then include the external library's header file only within your cpp file (when you actually need to use the classes or functions that you forward-declared in your header).
Use the PImpl idiom (or Cheshire Cat) where you forward-declare an "implementation" class that you declare and define only privately (in the cpp file). You use that private class to put all the external-library-dependent code to avoid having any traces of it in your public class (the one declared in your header file).
Here is an example using the first option:
#ifndef MY_LIB_MY_HEADER_H
#define MY_LIB_MY_HEADER_H
class some_external_class; // forward-declare external dependency.
class my_class {
public:
// ...
void someFunction(some_external_class& aRef); // declare members using the forward-declared incomplete type.
};
#endif
// in the cpp file:
#include "my_header.h"
#include "some_external_header.h"
void my_class::someFunction(some_external_class& aRef) {
// here, you can use all that you want from some_external_class.
};
Here is an example of option 2:
#ifndef MY_LIB_MY_HEADER_H
#define MY_LIB_MY_HEADER_H
class my_class_impl; // forward-declare private "implementation" class.
class my_class {
private:
std::unique_ptr<my_class_impl> pimpl; // a vanishing facade...
public:
// ...
};
#endif
// in the cpp file:
#include "my_header.h"
#include "some_external_header.h"
class my_class_impl {
private:
some_external_class obj;
// ...
public:
// some functions ...
};
my_class::my_class() : pimpl(new my_class_impl()) { };
Say the external header file contains the following:
external.h
class foo
{
public:
foo();
};
And in your library you use foo:
myheader.h:
#include "external.h"
class bar
{
...
private:
foo* _x;
};
To get your code to compile, all you have to do is to forward declare the foo class (after that you can remove the include):
class foo;
class bar
{
...
private:
foo* _x;
};
You would then have to include external.h in your source file.