To overload, or not to overload?

L

Lance

That is the question.

In an unmanaged C++ DLL I'm making calls to, one of the function prototypes is

/////
GM_DLL_EXPORTED GM_Error_t32 __stdcall GM_DrawLayerList
(
HDC aDC, // Device context to draw to
GM_LayerHandle_t32* aLayerList, // List of layers to draw or NULL for all
uint32 aLayerCount, // Number of layers in list (0 for all)
GM_DrawFlags_t32 aDrawFlags, // Flags controlling how the draw is performed
const GM_Rectangle_t* aWorldBounds, // World bounds to draw or NULL for all
sint32 aLeftPixel, // Left pixel coordinate to draw to
sint32 aTopPixel, // Top pixel coordinate to draw to
sint32 aPixelWidth, // Width in pixels to draw
sint32 aPixelHeight // Height in pixels to draw
);
/////
which I originally translated to

/////
Public Declare Function GM_DrawLayerList Lib "GlobalMapperInterface" _
( _
ByVal aDC As IntPtr, _
ByRef aLayerList As GM_LayerHandle_t32, _
ByVal aLayerCount As Int32, _
ByVal aDrawFlags As GM_DrawFlags_t32, _
ByRef aWorldBounds As GM_Rectangle_t, _
ByVal aLeftPixel As Int32, _
ByVal aTopPixel As Int32, _
ByVal aPixelWidth As Int32, _
ByVal aPixelHeight As Int32 _
) As GM_Error_t32
/////

but I couldn't figure out how get it to receive NULL (I was sending IntPtr.Zero to the
function in order to represent NULL) when the 5th argument was expecting the
GM_Rectangle_t structure. NULL needs to be sent to the function in order for it to draw
all layers. So, I overloaded the function as such

/////
Public Declare Function GM_DrawLayerList Lib "GlobalMapperInterface" _
( _
ByVal aDC As IntPtr, _
ByRef aLayerList As GM_LayerHandle_t32, _
ByVal aLayerCount As Int32, _
ByVal aDrawFlags As GM_DrawFlags_t32, _
ByVal aWorldBounds As IntPtr, _
ByVal aLeftPixel As Int32, _
ByVal aTopPixel As Int32, _
ByVal aPixelWidth As Int32, _
ByVal aPixelHeight As Int32 _
) As GM_Error_t32
/////

Note the change to "ByVal" and "IntPtr" for the 5th argument. Now it accepts a NULL
argument for the 5th parameter in order to get the function to draw all layers. I suppose
that's all well and good and everything seems to work fine, but the developer of the DLL -
who admittedly is not a VB expert - thinks that there should be a better way to it in VB
so that I don't have to duplicate the function prototypes (I would imagine some function
prototypes would require more than just 1 overload. For example, I think I would need to
overload the above function declare a couple of more times to account for the second
argument - which can also receive NULL or a structure - and then any combination of the
two arguments that can receive a NULL or structure.) Is there a more efficient way to go
about this on my end?

Thanks,
Lance
 
C

Claes Bergefall

Change to ByVal aWorldBounds As GM_Rectangle_t in your first version. No
need to pass it by ref since it's an in parameter. Then just pass Nothing to
it.

/claes
 
L

Lance

Changing it to

/////
Public Declare Function GM_DrawLayerList Lib "GlobalMapperInterface" _
( _
ByVal aDC As IntPtr, _
ByRef aLayerList As GM_LayerHandle_t32, _
ByVal aLayerCount As Int32, _
ByVal aDrawFlags As GM_DrawFlags_t32, _
ByVal aWorldBounds As GM_Rectangle_t, _
ByVal aLeftPixel As Int32, _
ByVal aTopPixel As Int32, _
ByVal aPixelWidth As Int32, _
ByVal aPixelHeight As Int32 _
) As GM_Error_t32
/////

then sending "Nothing" (without the quotes, of course) as the 5th argument results in a
PInvokeStackImbalance.

Lance
 
L

Lance

Well drat...if Claes and the other handful of aces here are stumped, then I guess it's
safe to assume that a better solution than overloading is likely non-existent. I know,
it's only been about 5 or 6 hours since the original posting, but I can tell when no ones
biting! Overloading it is, then.

Lance
 
B

Branco Medeiros

Lance said:
That is the question.

In an unmanaged C++ DLL I'm making calls to, one of the function prototypes is

/////
GM_DLL_EXPORTED GM_Error_t32 __stdcall GM_DrawLayerList
(
HDC aDC, // Device context to draw to
GM_LayerHandle_t32* aLayerList, // List of layers to draw or NULL for all
uint32 aLayerCount, // Number of layers in list (0 for all)
GM_DrawFlags_t32 aDrawFlags, // Flags controlling how the draw is performed
const GM_Rectangle_t* aWorldBounds, // World bounds to draw or NULL for all
sint32 aLeftPixel, // Left pixel coordinate to draw to
sint32 aTopPixel, // Top pixel coordinate to draw to
sint32 aPixelWidth, // Width in pixels to draw
sint32 aPixelHeight // Height in pixels to draw
);
/////
which I originally translated to

/////
Public Declare Function GM_DrawLayerList Lib "GlobalMapperInterface" _
( _
ByVal aDC As IntPtr, _
ByRef aLayerList As GM_LayerHandle_t32, _
ByVal aLayerCount As Int32, _
ByVal aDrawFlags As GM_DrawFlags_t32, _
ByRef aWorldBounds As GM_Rectangle_t, _
ByVal aLeftPixel As Int32, _
ByVal aTopPixel As Int32, _
ByVal aPixelWidth As Int32, _
ByVal aPixelHeight As Int32 _
) As GM_Error_t32
/////

but I couldn't figure out how get it to receive NULL (I was sending IntPtr.Zero to the
function in order to represent NULL) when the 5th argument was expecting the
GM_Rectangle_t structure. NULL needs to be sent to the function in order for it to draw
all layers.
<snip>

Without knowing how the type GM_Rectangle_t is declared, I'll risk
suggesting that instead of a structure, you declare it as a Class with
fixed layout (using the StructLayout(LayoutKind.Sequential) attribute):

<StructLayout(LayoutKind.Sequential)> Class GM_Rectangle_t
Public Left As Integer
Public Top As Integer
'... etc
End Class

Then you'd change de function declaration to:
Public Declare Function GM_DrawLayerList Lib "GlobalMapperInterface" _
( _
ByVal aDC As IntPtr, _
ByRef aLayerList As GM_LayerHandle_t32, _
ByVal aLayerCount As Int32, _
ByVal aDrawFlags As GM_DrawFlags_t32, _
ByVal aWorldBounds As GM_Rectangle_t, _
ByVal aLeftPixel As Int32, _
ByVal aTopPixel As Int32, _
ByVal aPixelWidth As Int32, _
ByVal aPixelHeight As Int32 _
) As GM_Error_t32
/////

and just pass Nothing when you wanted to pass Null.

HTH.

Regards,

Branco
 
C

Claes Bergefall

Hmm, that's a new one. Can you post the definitions of the custom types
(GM_Rectangle_t, GM_LayerHAndle_t32 etc), preferably both the C++ and the
..NET code

/claes
 
L

Lance

here are the definitions of the custom types in the C++ header file.

/////
typedef struct
{
double mMinX; // Minimum x/easting/longitude coordinate
double mMinY; // Minimum y/northing/latitude coordinate
double mMaxX; // Maximum x/easting/longitude coordinate
double mMaxY; // Maximum y/northing/latitude coordinate
} GM_Rectangle_t;

typedef void* GM_LayerHandle_t32;
/////

and here are my .NET translations
/////
Public Structure GM_Rectangle_t
Public mMinX As Double ' Minimum x/easting/longitude coordinate
Public mMinY As Double ' Minimum y/northing/latitude coordinate
Public mMaxX As Double ' Maximum x/easting/longitude coordinate
Public mMaxY As Double ' Maximum y/northing/latitude coordinate
End Structure
/////

for GM_LayerHandle_t32, on the advice of the SDK developer I've defined an Alias as such:

/////
Imports GM_LayerHandle_t32 = System.Int32
/////

Lance
 
L

Lance

This seems to work. I'm curious to know if i should now start from scratch and translate
all of the C custom types into classes instead of structures. What would be the benefits
(besides not needing to overload functions that use the custom types as arguments) and
drawbacks, if any?

Lance
 
L

Lance

I went ahead and convert a handful of the structures to classes. I was having some
problems, though. It looks as though I'm not allowed to easily convert structures that
contain other structures to classes. Well, I may be allowed, but it's causing other
problems with marshalling and pointers, and I'm not good at all dealing with those.
Anyway, I can't find out a solution to that one, so for the time being, I've got a mix of
classes (that contain only Enums or native data types) and structures.

You know what? It just occurred to me that all of this work - converting some of the
stuff to classes and leaving others as structures - hasn't actually helped me with my
original post: finding an alternative to overloading. Don't get me wrong, I certainly
appreciate the help. Given what I'm realistically able to divulge in a newsgroup post
code-wise, you guys are doing your best to help me, I know.

Thanks again, and if you guys have any more suggestions, I'm all ears.
 
C

Claes Bergefall

It looks like everything is an in parameter (hence no need for ByRef), or
does the C++ function use any of the parameters to return stuff?
As Branco wrote you'll need to use a class since value types (i.e.
structures) can't be null. If would define it like this:

<StructLayout(LayoutKind.Sequential)> _
Public Class GM_Rectangle_t
Public mMinX As Double
Public mMinY As Double
Public mMaxX As Double
Public mMaxY As Double
End Class

Public Declare Function GM_DrawLayerList Lib "GlobalMapperInterface" _
( _
ByVal aDC As IntPtr, _
ByVal aLayerList As IntPtr, _
ByVal aLayerCount As Int32, _
ByVal aDrawFlags As Int32, _
ByVal aWorldBounds As GM_Rectangle_t, _
ByVal aLeftPixel As Int32, _
ByVal aTopPixel As Int32, _
ByVal aPixelWidth As Int32, _
ByVal aPixelHeight As Int32 _
) As Int32

You wrote that you had problem converting to classes in some cases where you
had structure members. There is probably a way out of that too, but I don't
think any of this is actaully worth the effort. If you have something that
already works (using overloads) then I would stick with that and not spend
more time on this optimization. I'm sure you have plenty of other, more
important things to do :)

Interesting problem none the less

/claes
 
L

Lance

Claes,

Thanks for all of your responses and for all of your help. Am I correct in
interpreting your last message as advising me to use VB Classes for C++
types that can be passed as nulls? Or, we you advising me to stick with
overloading?

This particular function doesn't return anything into the structure, just an
error code, so passing by value in this case is fine, as you said.

Thanks again,
Lance
 
C

Claes Bergefall

I was just thinking that trying to get away from the overloading that
already works might not be the most productive way of spending your time.
As for classes vs structures, you will need to use classes if you need to be
able to pass null values. A value type cannot be null.

Feel free to post the other structures that you had problems with if you're
determined to go this way
Does the code below work btw?

/claes
 
B

Branco Medeiros

Lance said:
I went ahead and convert a handful of the structures to classes. I was having some
problems, though. It looks as though I'm not allowed to easily convert structures that
contain other structures to classes. Well, I may be allowed, but it's causing other
problems with marshalling and pointers, and I'm not good at all dealing with those.
Anyway, I can't find out a solution to that one, so for the time being, I've got a mix of
classes (that contain only Enums or native data types) and structures.

Notice that when a structure 'contains' another, such as:

structure A
Dim W As Integer
DIm X As Integer
End Structure

Structure B
Dim Y As A
Dim Z As Integer
End Structure

it means that the fields of the contained structure are linearlly layed
out onto the container structure (this is true for C, C++ and VB as
well). In the example above, the physical layout of B would be similar
to:

Structure C
Dim Y_W As Integer
Dim Y_X As Integer
Dim Z As Integer
End Structure

On the other side, when you declare a class member, be it part of a
structure or another class, just the reference to the class is part of
the container, not the actual "structure" of the class.

Therefore, in your particular case, where should you use classes
instead of structures? Whenever you had a *pointer* to a structure. In
this case it's best to use classes, because the .Net marshaller will
know what to do with it.

So If you had (forgive my C):

typedef struct {
int F1;
int F2;
} T1;

typedef struct {
T1 FT1;
int F3;
} T2;

typedef struct {
T1* pFT1;
T2 FT2;
} T3

void DoIt(T3* pT3) {
....
}

Then I *guess* you'd have to declare (in the VB side) both a structure
and a class to represent, respectivelly, the structures and *the
pointers* to the structures:

Public Structure T1
Dim F1 As Integer
Dim F2 As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Class PtrT1
Public Value As T1
End Class

Structure T2
Dim FT1 As T1
Dim F3 As Integer
End Structure

Structure T3
Dim pFT1 As PtrT1
Dim FT2 As T2
End Struct

<StructLayout(LayoutKind.Sequential)>
Class PtrT3
Public Value As T3
End Class

Declare Sub DoIt Lib "whatever.dll" (ByVal pT3 As PtrT3)

I'm not sure if declaring the actual structures as values inside the
classes (as I showed above) will do. In a worst case, you'll have to be
explicit (I hope not), such as in:

<StructLayout(LayoutKind.Sequential)>
Class PtrT3b
Public pFT1 As PtrT1
Public FT2_FT1_F1 As Integer
Public FT2_FT1_F2 As Integer
Public FT2_F3 As Integer
End Class

I guess you got the idea...

HTH.

Regards,

Branco.
 
L

Lance

Thanks for clearing that up, Claies. And yes, the code you sent previously
did indeed work.

Lance
 
Top