1

I have a large C++ API, which I need to wrap in a C++/CLI skin to make available in .NET

For the most part it works fine, but I have encountered one area that causes a polymorphism problem.

On the pure C++ API I have a function like:

vector<Parent*> getCppObjects()
{
    return myVector;
}

Parent is a type with two children ChildA and ChildB. On the CLI side I will have a function:

List<CLIParent^> getCliObjects()
{
    List<CLIParent^> myList = gcnew List<CLIParent^>();
    vector<Parent*> myVector =  getCppObjects();
    for int i=0; i < myVector.size(); ++i)
    {
        myList->add(gcnew CLIParent(myVector->at(i)));
    }
    return myList;
}

CLI Parent has constructor that accepts type Parent, and there are similar classes for the child types. My problem is that at the C++ layer I can cast objects to their correct type, but because of the way I have wrapped them at the CLI layer (as always being of the parent type) I cannot use them as CLI versions of the child types.

Do I need to use something like typeid and a switch/factory to create the appropriate types in my CLI API or is there a more elegant solution?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Does `CLIParent` just hold a pointer to the `Parent` object? – James McNellis Nov 05 '10 at 17:44
  • @James, yes that is the idea, and it replicates/approximates its API. In this case the CLIParent would hold a pointer to a correct ChildA type, but I can't cast it as a CLIChildA. – Downward Facing God Nov 05 '10 at 17:48
  • 1
    I think similar problems exist when using PIMPL with inheritance [pimpl-idiom-with-inheritance](http://stackoverflow.com/questions/491304/pimpl-idiom-with-inheritance/494714#494714) – Greg Domjan Nov 05 '10 at 19:10
  • @Greg, This is very like the PIMPL pattern, I didn't think to look for a solution there though. I'll check it out. Thanks. – Downward Facing God Nov 05 '10 at 20:46

2 Answers2

3

One option would be to have clients use a method (rather than dynamic_cast) when they need to see if the underlying cpp class is a child. Something like the following:

public ref class ParentChildUtil
{
   static CLIChild ^ CastParentToChild(CLIParent ^ pParent)
   {
       Parent * pNativeParent = pParent->GetNative();
       Child * pNativeChild = dynamic_cast<Child *>(pNativeParent);
       if (!pNativeChild)
           return nullptr;

       return gcnew CLIChild(pNativeChild);
   }
};

This isn't ideal, but it at least delays the dynamic_cast to when you actually need to do it.

Another option (if it is possible to compile the native Parent class with /clr) is to add a virtual to the Parent class which creates its own corresponding CLI type:

class Parent
{
    // ...
    virtual CLIParent ^ CreateManagedWrapper()
    {
       return gcnew CLIParent(this);
    }
};

class Child
{
    // ... 
    virtual CLIChild ^ CreateManagedWrapper()
    {
        return gcnew CLIChild(this);
    }
}

then your client code becomes:

List<CLIParent^> getCliObjects()             
{             
    List<CLIParent^> ^ myList = gcnew List<CLIParent^>();             
    vector<Parent*> myVector =  getCppObjects();             
    for (int i=0; i < myVector.size(); ++i)             
    {
        Parent * pParent = myVector->at(i);             
        myList->add(pParent->CreateManagedWrapper());             
    }
    return myList;             
 }             
Matt Smith
  • 17,026
  • 7
  • 53
  • 103
  • Thanks Matt, I'm going to see if I can get your second proposal working. It looks promising. I'll mark it as an answer if it works. – Downward Facing God Nov 08 '10 at 23:56
  • Sounds good, if you are unable to compile the parent and children classes with /clr, there is another indirect way you could accomplish it--so let me know if you run into any trouble. Good luck! – Matt Smith Nov 09 '10 at 14:07
0

Why do you need multiple types at the CLI layer? A CLIParent wrapping a Parent* is going to be polymorphic and behave like the object actually pointed to, you don't need virtual functions in the CLI wrapper (this is actually a lot like the pattern of public non-virtual functions calling protected or private virtual functions). I guess the subclasses introduce some additional methods into the public API, and you are trying to call these?

A design using dynamic downcasts is a code smell, and I think you've just discovered why.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720