0

I am trying to protect some C++ code by exporting as a DLL (on Windows/VS 2010).

In the example below var is set in the superclass constructor, and the debugger shows it is definitely set to reference something.

Test is constructed in the code that consumes the DLL the Test class is contained within.

But when go is called from on an instance of test (invoked from the DLL, but the invoking method is called by the DLL consumer) var is a null pointer (it's value is 0).

This is a simplification as I am not allowed to share the actual code.

//Headers

class Base {
  public:
    __declspec(dllexport) Base();      
  private:
    Foo* var;
};

class Test : Base {
public:
  __declspec(dllexport) Test();
  __declspec(dllexport) void go();
private:
};

//Body

Base::Base() {
  var = new Foo();
}

Test::Test() : Base() {
}

void Test::go() {
  var->do_something();
}

In the consuming code, the header is

class Base {
public:
  __declspec(dllimport) Base();
}; 

class Test {
public:
  __declspec(dllimport) Test();
  __declspec(dllimport) void go();
};

The actual code is much more complex, but I would be grateful if anyone can tell me whether there are known restrictions on instance variables with dllexport, or if it is more likely that I'm calling a method on a null pointer for Test, or perhaps it is a dllexport and inheritance problem. This code worked before I split the consumer code and DLL code was in the same project, it has only broken since splitting it up, dllexporting/dllimporting functions I want to expose into a second set of headers used by the consumer.

Paul Ridgway
  • 1,055
  • 2
  • 13
  • 23

3 Answers3

1

When you pass a Test by value from one place to another in the "consuming code", you'll cause slicing to occur because the client code is unaware of the variable and calculates an incorrect size for the class Test.

To solve this problem, you should declare the variable in the client code as well, or alternatively you can provide a static factory function of some kind and only allow the client code to pass pointers to Tests around to avoid slicing.

Community
  • 1
  • 1
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Thanks Ive updated the question because I realised there is inheritance in the mix too. I tried __declspec(dllexport) Foo* var; but I got "__declspec modifiers not valid for this declaration" – Paul Ridgway Feb 08 '12 at 22:42
  • @PaulRidgway ah, sorry I was wrong, just add `Foo* var` to the client's `Base` too (or maybe you could even get away with `void* var` if `sizeof(void*) == sizeof(Foo*)`) – Seth Carnegie Feb 08 '12 at 22:47
0

If you remove the instance variable from the code supplied to the customer, only your own code knows the real size of your object and can create it. From the top of my head you have two ways to go about this:

  1. Supply a createTest static function that creates an instance of your class (factory method). The nicest thing to do here is to provide a pure interface (no instance variable).

  2. If you only want to hide a specific part of your class you can use the pimpl idiom (wikipedia article).

rasmus
  • 3,136
  • 17
  • 22
0

Is there any reason to not use:

#ifdef IN_FOO_PROJECT
# define fooEXPORT __declspec(dllexport)
#else
# define fooEXPORT __declspec(dllimport)
#endif

class fooEXPORT exportClass
{
public:
  void function( void );
  Foo * var;
}

If you want to hide your class (or part of your class), you could use it as a private member:

#ifdef IN_FOO_PROJECT
# define fooEXPORT __declspec(dllexport)
#else
# define fooEXPORT __declspec(dllimport)
#endif

class classToHide;

class fooEXPORT exportClass
{
public:
  void function( void );
  classToHide * var;
}

And in Cpp:

#include "exportClass.h"
#include "classToHide.h"

void exportClass::function( void )
{
  var->function();
}
Naszta
  • 7,560
  • 2
  • 33
  • 49
  • There is a lot of the original class(es) methods that I want to hide, so cannot export and import the whole thing. I want to export a subset of methods, which in turn call other internal and external methods, some public and protected - which are accessed within the DLL but not outside. – Paul Ridgway Feb 09 '12 at 17:36