• entries
    146
  • comments
    436
  • views
    197645

Using proxies for Managed/Unmanaged inheritance

Sign in to follow this  
Washu

334 views

Inheriting Managed classes from Unmanaged classes.
First of all, you can't. You can fudge it, however, in such a way that the user of your library will be unable to tell that they aren't inheriting from the unmanaged class.

For the examples I will be using here, we will be strictly inheriting from an interface. You could extend the system, if you wanted, to include inheriting from actual classes. Another thing that should be noted is that the examples I use here are just that, examples. In reality you would never stick such a simple class in a DLL and expose it via an interface. It's just plain wasteful.

Off on a tangent for a second, one of the many issues one finds with books and tutorials are the examples. One of the problems that authors face is the challenge of writing examples that suitably demonstrate the concepts without overburdening the reader with detail about the system being used (for instance, an example that uses bank accounts will typically not include the code for the bank, but instead small snippets where needed). You can easily see this problem just by perusing my journal. Most of the examples I use are very contrived (such as the ones you will find in this entry), but they demonstrate the concepts that I am concerned about fairly succinctly.

First up, we're going to be providing a list class from a DLL. This list class will enable users to add, remove, sort and print the elements of the list (integers in this case). Sorting of the list will be done through an interface pointer provided by the user of the DLL.

//Something.h
#pragma once

#ifdef MIXEDDLL_EXPORTS
#define DLLIMP extern "C" __declspec(dllexport)
#else
#define DLLIMP extern "C" __declspec(dllimport)
#endif

struct IComparer {
virtual bool Compare(int, int) = 0;
};

struct ISomething {
virtual void AddEntry(int) = 0;
virtual void RemoveEntry(int) = 0;
virtual void Sort(IComparer* other) = 0;
virtual void PrintList() = 0;
virtual void Release() = 0;
};

DLLIMP ISomething* GetSomething();

//SomethingImpl.h
#pragma once

#include "Original.h"
#include

class SomethingImpl : public ISomething {
public:
virtual void AddEntry(int entry);
virtual void RemoveEntry(int entry);
virtual void Sort(IComparer* other);
virtual void PrintList();
virtual void Release();
private:
std::vector<int> entries;
};

//SomethingImpl.cpp
#include "SomethingImpl.h"
#include
#include

struct ComparerFunctor {
ComparerFunctor(IComparer* comparer) : comparer(comparer) {}
bool operator()(int lhs, int rhs) {
return comparer->Compare(lhs, rhs);
}
private:
IComparer* comparer;
};

template
struct ListPrinter {
void operator()(T const& entry) {
std::cout< }
};

void SomethingImpl::AddEntry(int entry) {
entries.push_back(entry);
}

void SomethingImpl::RemoveEntry(int entry) {
entries.erase(std::remove(entries.begin(), entries.end(), entry), entries.end());
}

void SomethingImpl::Sort(IComparer* other) {
std::sort(entries.begin(), entries.end(), ComparerFunctor(other));
}

void SomethingImpl::PrintList() {
std::for_each(entries.begin(), entries.end(), ListPrinter<int>());
}

void SomethingImpl::Release() {
delete this;
}

ISomething* GetSomething() {
return new SomethingImpl();
}

Well, as you can see, it's a fairly simple implementation. Nothing really spectacular to notice here. The one thing that you should note is that I wrap up the interface pointer in a functor and pass it to a sort algorithm. Since we assume that the user of the DLL is managing the memory of the interface, it doesn't matter if the pointer gets copied around a few times inside of the functor.
At this point, we can easily use the DLL in an unmanaged application, however for a managed application, it won't be quite so simple. To be useful for our managed applications we will have to expose the ISomething interface to a managed application, along with the IComparer interface in such a way that a managed application can provide their own comparison classes without having to write an unmanaged wrapper. First we write a simple interface that mimics that of our unmanaged counterpart, and then provide an unmanaged wrapper that will expose that interface to the unmanaged code:

public interface class IComparer {
public:
bool Compare(int lhs, int rhs);
};

struct ManagedComparerWrapper : ::IComparer {
ManagedComparerWrapper(gcroot comparer) : comparer(comparer) {}
bool Compare(int lhs, int rhs) {
return comparer->Compare(lhs, rhs);
}
private:
gcroot comparer;
};


Note how the unmanaged wrapper simply delegates the call to the managed handle that was passed to it. You should also note that this wrapper has been derived from the unmanaged IComparer interface and hence can be used in with our unmanaged list.

Next we wrap up the ISomething interface, providing it with a simple wrapper that just delegates the majority of the functionality to the existing classes.

public ref class Something sealed {
public:
Something() {
something = new SomethingImpl();
}
~Something() {
if(something) {
delete something;
something = 0;
}
}
!Something() {
this->~Something();
}
void AddEntry(int entry) {
something->AddEntry(entry);
}
void RemoveEntry(int entry) {
something->RemoveEntry(entry);
}
void Sort(IComparer^ comparer) {
ManagedComparerWrapper wrapper = ManagedComparerWrapper(comparer);
something->Sort(&wrapper);
}
void PrintList() {
something->PrintList();
}
private:
ISomething* something;
};


Things you should notice here: The Sort simply creates a stack allocated wrapper and passes it the managed comparison interface handle. It then passes the address to the sort function of the unmanaged interface. Also you should note the destructor and the finalizer, and how they are used to release the unmanaged resources.

Finally, what good is an example without something to use it, so here's a simple application that makes good use of our hard work:

using System;

namespace ManagedTest {
class Program {
static void Main(string[] args) {
ManagedWrapper.Something something = new ManagedWrapper.Something();
Random r = new Random();
for(int i = 0; i < 10; ++i) {
something.AddEntry(r.Next(100));
}

Console.WriteLine("Unsorted:");
something.PrintList();
Console.WriteLine("Sorted:");
something.Sort(new TestComparer());
something.PrintList();
}
}

class TestComparer : ManagedWrapper.IComparer {
public bool Compare(int lhs, int rhs) {
return lhs < rhs;
}
}
}



You could, of course, do the opposite of this. Which is to say, you could make unmanaged classes appear to derive from managed classes by using proxies.
Sign in to follow this  


6 Comments


Recommended Comments

Tonight will be the first time trying to inherit from an unmanaged class for me.. so the question flood will commence. I was waiting for this post before I attempted it hehehe.

Although it should be a relatively simple case in my situation, the only unmanaged class I need to inherit at this point is a simple IGameState interface with 4 pure virtuals.

Share this comment


Link to comment
Guest Anonymous Poster

Posted

holly crap, this is great! Thanks a million, this is super. I will try it out later :)

Share this comment


Link to comment
Washu, tyvm :)

My engine now has the basics wrapped and is running in C# letting me build the tools. This rocks :)

Share this comment


Link to comment
Guest Anonymous Poster

Posted

is tehre any way you could post the sourcecode seperatly? I try copy pasting the snippits to various files but cant get it building....

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now