views:

143

answers:

2

I have two Perl modules which I want to expose as object types to C#. One of them constructs objects of the other type and returns it using a method as shown below. I'm including a reference to Type2.dll in Type1.dll, and referencing them both in C#. As the code shows I can construct a Type2 object directly from C#, but I can't return a Type2 object that was constructed by a method in Type1. Any ideas?

(Cross-posted from http://community.activestate.com/forum/return-perl-object-different-perl-class-c)

C#:

Type1 obj1 = new Type1(); // Works
Type2 test = new Type2(); // Works
Type2 obj2 = obj1.make2(); 
// Fails: System.InvalidCastException: Unable to cast object of type 
// 'PerlRunTime.SV' to type 'Type2' at Type1.make2()

Perl: Type1.pm

package Type1;

use strict;
use Type2;

=for interface
    [interface: pure]
    static Type1();
    Type2 make2();
=cut

sub new {
    my $class = shift;
    return bless {}, $class;
}

sub make2 {
    my $this = shift;
    return Type2->new();
}

1;

Perl: Type2.pm

package Type2;

use strict;

=for interface
    [interface: pure]
    static Type2();
=cut

sub new {
    my $class = shift;
    return bless {}, $class;
}

1;
+2  A: 

Since this was cross-posted to community.activestate.com and has been answered there, I'll copy the answer from there to here, but I'll trim it down because I think it's too lengthy.

The main problem is that the way you wrote it, Type2 is not considered a type, and calling Type2->new() does not translate to a constructor call (but a static method invocation).

The following changes to your code fix this:

  • In Type2.pm, change package Type2 to package Sample::Type2. This makes Type2 a type and Sample a namespace.
  • In Type1.pm, similarly change package Type1 to package Sample::Type1.
  • In Type1.pm, change use Type2; to use namespace "Sample";. This imports Type2 as a type.

The posted C# code works as required after these changes.

Timwi
+1  A: 

I've also found that I can create one file like this:

package Type2;

=for interface
    [interface: pure]
    static Type2();
=cut

require Type2;


package Type1;
use Type2;

=for interface
    [interface: pure]
    static Type1();
    Type2 make2();
=cut

Then my Type1.pm and Type2.pm files are the same as before but without the interface POD. With this setup, plc creates a single dll that includes both classes, and the Type1 class is able to create and return instances of Type2.

This actually turned out to be more convenient for me, because my Type1 and Type2 classes are part of a legacy library that I want to make available to C# code without making extensive .NET-specific changes to them. I've created one pm file for my C# assembly, and within it I've included the interface definitions for accessing the legacy library methods and a bunch of .NET specific methods for properties and converting complex Perl hash/array data structure return values into Hashtable and Array/ArrayList data structures.

DougWebb