0

Prior to refactoring my previous question, which I believe was a little bit off...

The title pretty much asks my question.

How can I keep a class definition on it's own without giving it methods & producing the error below?

The reason for this is because I want to create an object in a separate DLL (which contains the methods), but only return a reference pointer to my main program. This is explicit exporting by the way.

Error 1 error LNK2001: unresolved external symbol "public: int __thiscall ObjTest::getValue(void)" (?getValue@ObjTest@@QAEHXZ)

class ObjTest
{
private:
    int x;
public:
    int getValue();
};
Andy Carter
  • 201
  • 3
  • 12
  • 1
    The class definition must have the methods' declarations. But the method definitions can go in a separate `.cpp` file. – juanchopanza Jan 16 '14 at 13:45
  • What if I want to create an object in a separate DLL (which contains the methods), but only reference a pointer from my main program towards it? – Andy Carter Jan 16 '14 at 13:46
  • What you mention above should work fine, since C++ knows a pointer is just an int. However, if you try to deref the pointer and call one of its functions(implemented in the DLL) from the main file, without loading that DLL, it will error out I believe. – Dean Knight Jan 16 '14 at 13:51
  • The DLL has the .h and .cpp of the ObjTest class. It's exporting 1 function which creates an object of it and returns a pointer. In my main program, I need to get that pointer and the only way I can think of it is simply add the DLL's .h file and work with it. But as I said, I get linker compilation errors. Also, let me make it clear that this is explicit export, not dll-importing. – Andy Carter Jan 16 '14 at 13:54
  • @hyde: Making it pure virtual would cause a compiler error whenever an instance of the class was declared. – Skizz Jan 16 '14 at 13:55
  • @Skizz Ahm, yes, seems I misread the question... – hyde Jan 16 '14 at 16:25

2 Answers2

2

Since you need to load the .dll with LoadLibrary(), you can expose a pure virtual class, and have the .dll return a sub class of it:

You separate them in two files:

File ObjTest.h:

class ObjTest
{

public:
    virtual int getValue() = 0;
};
ObjTest *CreateObjTest();

File ObjTest.cpp:

#include "ObjTest.h"

class ObjTestImpl : public ObjTest
{
    int x;
public:
    virtual int getValue();
};

int ObjTestImpl::getValue()
{
   return x;
}
ObjTest *CreateObjTest()
{
  return new ObjTestImpl();
}

You compile ObjTest.cpp and create a .dll out of it. Your main executable program will need to LoadLibrary() your .dll, use GetProcAddress() to extract the CreateObjTest as a function pointer and call it to return a new ObjTest .

(You might have to create a DeleteObjTest() function too - if your main executable and .dll end up with a different CRT, they'll have different heaps, so you need to call into the .dll instead of just doing delete myObj.)

The other approach is to wrap everying in a C API, and just pass opaque handles to C functions across the .dll instead of dealing with C++ objects.

nos
  • 223,662
  • 58
  • 417
  • 506
  • I'm not implicitly importing a dll, I'm exporting it, therefore the above will not work unfortunately ;/ – Andy Carter Jan 16 '14 at 13:55
  • Because what you're saying is basically implicitly importing a dll. The dll will contain the .h and .cpp of the file. The main program will contain the dll's .h file and the linker is fixed by implicitly pointing it to the dll's .lib file. Well, I'm exporting functions - so there is no .lib file - it's just the .dll which I LoadLibrary() it and read it's function signatures with GetProcAddress(). All in all, adding a naked dll .h function will give me linker errors. – Andy Carter Jan 16 '14 at 14:01
  • 1
    @AndyCarter The very, very important fact is that you want to dynamically load the .dll with LoadLibrary(), and not statically link to the .dll. In that case, yes, it will not work. – nos Jan 16 '14 at 14:02
  • Edited the answer with an alternative when using LoadLibrary. – nos Jan 16 '14 at 14:11
  • Yay! That worked perfectly fine. Really appreciate it @nos. So I just had to manually export the class and declare it virtual. – Andy Carter Jan 16 '14 at 14:18
  • I'd put the create method into the class as a static method and call it `Create`, then use: `ClassName::Create ()`. This helps by moving the method out of the global namespace. – Skizz Jan 16 '14 at 15:58
0

You are confusing definition and implementation. You have the method defined but not implemented. Hence, the compiler compiles the code without error, as the method is defined, the linker creates an error as there is no implementation for the method (undefined symbol).

The DevStudio compiler does let you import classes from DLLs into an application:-

class __declspec (dllimport) ClassName
{
  // members, etc
}

and in the DLL source, change the 'dllimport' to 'dllexport'.

See this MSDN article for more information.

If you want to hide the data members and the private methods then you'd need to look into the pimpl idiom.

Skizz
  • 69,698
  • 10
  • 71
  • 108
  • "When you declare a class dllexport, all its member functions and static data members are exported. You must provide the definitions of all such members in the same program. Otherwise, a linker error is generated. " That's exactly my problem. "If you export data of class type or functions that return classes, be sure to export the class." Okay, I'll export the class, but how will that solve my linker errors? – Andy Carter Jan 16 '14 at 14:09
  • @AndyCarter: The linker error will be resolved by including the class' header file in the main app in those translation units that require it, but declare the class as `dllimport`. In the DLL, declare the class as `dllexport`. This means the app will know the definition and the DLL will contain the implementation. The linker will put stub code in when an access to an imported class is found to load the DLL and resolve the addresses of the various methods and data. – Skizz Jan 16 '14 at 15:53