Jump to content
  • Advertisement
Sign in to follow this  
thesimon

[.net] managed wrappers c++/cli [solved]

This topic is 4386 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

hi there, i have a question regarding the creation of managed wrappers for an existing native c++ class. my problem is that the native c++ class relies not only on callbacks (which are no problem to marshal) but also on entire classes acting as callbacks.. code:
// native: 
struct guide_the_process
{
   bool check_a(int);
   bool check_b(int);
};
class the_class 
{ public: void do_something(guide_the_process &guide); };

// do_something often calls guide.check_XX() to see if certain criteria 
// of the process are met. so guide basically bundles two callback
// functions in one struct. the guide is meant to be a user supplied
// bundle of callbacks

// managed:

public ref class GuideTheProcess
{
public:
  bool CheckA(int);
  bool CheckB(int);
};
public ref class TheClass
{
public:
  void DoSomething(GuideTheProcess^ guide)
  {
    // assume the_class_ instance has already been constructed 
    // and is valid.
    assert(the_class_);
    Debug::Assert(guide != nullptr);
    
    // how do i marshal "guide" to be able to call the 
    // the_class_->do_something() method !? 
    // or is this even the right way to do it? 
    // i don't know whether it is more feasable to 
    // only rely on simple callbacks.. but the 
    // interface of the native class is already built
    // that way.. (using the guide struct..)

    guide_the_process native_guide = SomehowMarshalTheThing(guide);
    the_class_->do_something(native_guide);
  }

private:
  the_class *the_class_;
};
i hope you understand what i mean.. looks to me as if interfaces aren't meant to be designed that way when creating managed wrappers. [Edited by - thesimon on August 18, 2006 7:34:43 PM]

Share this post


Link to post
Share on other sites
Advertisement
So, if I understand correctly, isn't "public class GuideTheProcess" just a wrapper around "struct guide_the_process"? (If so, just get the underlying unmanaged object and pass that as a parameter.) Or is it supposed to be a managed implementation of the callback that you want to somehow pose as an unmanaged object? In that case, I can't think of a way to do this off the top of my head.

Vovan

Share this post


Link to post
Share on other sites
hi vovansim,

i suppose i'm trying to accomplish the latter case. as you can see the native do_something() call needs a native guide_the_process instance as parameter. so for the user of the managed TheClass class i must also provide a manged GuideTheProcess class that gets translated into its native counterpart. i'm going this route because the managed (wrapper) api must be called from c#, too, so i can't pass any native objects.
for a while i also tried to take the managed GuideTheProcess class apart and take pointers to each member function of its instance and somehow handing those function pointers to the native class... but it sounded weird and didn't work (basically because i can't hold pinned pointers to the member functions at class scope of the managed class).
i suppose i need to change the interface of the native class to only use function pointers as callbacks and then do all the marshalling via plain old managed delegates. that's a bit sad as i really liked the previous interface with a struct holding, in a way, a collection of callbacks.

thanks for your answer again, vovansim!

cheers,
simon

edit --
just to explain a bit why the guide_the_process object exists and why i don't want to get rid of it:
my algorithm encoded in do_something() actually doesn't take the guide_the_process parameter directly. instead, the_class has the ability to register and unregister multiple guide_the_process instances. that way the algorithm can be directed by multiple guides. all guides are evaluated/visited in the do_something() method and their return values aggregated to direct the progression of the algorithm in do_something(). i find this a very useful approach.. would hate to see it killed by the c++/cli wrapper :-(

[Edited by - thesimon on July 29, 2006 4:17:10 PM]

Share this post


Link to post
Share on other sites
well, i finally dropped the old interface and i'm now using raw function pointers as callbacks instead.. going the old C route. i figured i could more easily build a manged wrapper around them.
most of the stuff works, but the thing i haven't figured out yet is how to marshal arrays properly. i know how to take a managed array and pass it to a native function (via UnsafeAddrOfPinnedArrayElement()) but vice versa?

#pragma managed
void native_to_managed(float *array, size_t length)
{
array<float>^ managed_array = somehow_marshal_the_thing(array, length);
}

How is this done!? i can't find any useful information on that (even though i basically live on msdn..) the only example i found created a new managed array and did a full copy of the native array.. i don't want that. this code is called verrrry frequently.
has anyone done this? seems that c++ interop isn't very well documented in general. only the basics are known.. but there's no central place to go to if you need information.

thanks in advance,
simon

Share this post


Link to post
Share on other sites
IIRC, it's not possible to "convert" a C array to managed array. The only way is to copy the data to a managed array. But you can change your design to let unmanaged code to operate on locked managed array in most of the time.

Share this post


Link to post
Share on other sites
I'm not 100% sure I understand the part that is the problem, however...


It is not possible to mix and match unmanaged and managed members in an unmanaged class/struct. This is where the problem seems to lie. You have an unmanaged struct from which you want to call managed methods on an object, however you cannot keep a reference to that object because it is unmanaged :-)
So maybe, why not keep an integer index to the manageed object, then use a dictionary lookup to get the reference?

Something like:



interface IGuide
{
bool CheckA(int);
bool CheckB(int);
}

struct guide_the_process
{
int index = -1;
bool check_a(int value)
{
return Control.Guides[index]->CheckA(value);
}
bool check_b(int value)
{
return Control.Guides[index]->CheckB(value);
}
};

ref class Control
{
public protected:
static Dictionary<int,IGuide> Guides;
}



Does that help? maybe... Expose the IGuide interface for implementation... That sort of thing.

Share this post


Link to post
Share on other sites
seems that the .net way of passing native ararys to managed code is via Marshal::Copy() which is basically a std::copy() that works with both managed and native arrays.

Regarding my "guide" interface problem:
Thanks a lot RipTorn, I suppose your solution might work if it's true that i can access a managed static field in a managed class from a native class (which i haven't tried yet). In that case i must say that it would be a shame for c++/cli not to allow managed handles in a native class but to give a native class access to a static managed handle of another class.

I'll give your solution a try just to see if the above is the case. i already converted the interface to a purely function pointer based one. That translates more directly to the .net way of handling events using delegates.

cheers,
simon

Share this post


Link to post
Share on other sites
C++ / CLI does in fact allow references to managed classes in unmanaged classes. This is done via the gcroot generic handle. An example below:



public ref class ManagedClass
{
// implementation
};

class UnmanagedClass
{
private:

gcroot<ManagedClass ^> managedObj;

// remaining implementation
};



You can then call the methods / properties of the managed class normally via the gcroot pointer overload (->).

Hope that helps.

Magius

Share this post


Link to post
Share on other sites
hey you learn something every day :-)

I wasn't aware of gcroot, having done most of my work with older C++.net (1.1) not C++/Cli (2.0).

Share this post


Link to post
Share on other sites
didn't know about gcroot either, i must admit.

As i said i've started to recreate the native interface so it takes a bunch of plain old function pointers as parameters. however, difficulties arose.. i can't get marshalling to an array of native function pointers to work... by that i mean, i corrupt the stack in a quite unholy way (seeing the this pointer turning 0x00 really scares me..).

i have a minimal code snippet that compiles and produces the error i.e. corruption.

#include <cassert>
#include <algorithm>

using namespace System;
using namespace System::Runtime;
using namespace System::Runtime::InteropServices;
using namespace std;

#pragma unmanaged
typedef void (*fn_type)(float*, size_t);

void call_fns(fn_type *fns, size_t num_fns)
{
assert(fns);
assert(num_fns > 0);
for(size_t index=0; index < num_fns; ++index)
fns[index](0, index);
}
#pragma managed

ref class test
{
public:
test(size_t num_fns)
{
assert(num_fns > 0);
fn_type *fns = new fn_type[num_fns];
private_call_delegate^ pcd = gcnew private_call_delegate(this, &test::private_call);
pin_ptr<private_call_delegate^> pinned_pcd = &pcd;

fn_type native_fn = static_cast<fn_type>(
Marshal::GetFunctionPointerForDelegate(pcd).ToPointer());
fill(fns, fns + num_fns, native_fn);

call_fns(fns, num_fns);
delete[] fns;
}

~test() {}

private:
delegate void private_call_delegate(float*, size_t index);

void private_call(float*, size_t index)
{ Console::WriteLine("Called with index {0}", index); }
};

int main(array<System::String ^> ^args)
{
test t(2);
return 0;
}



this closely resembles my setup: function with array of native function pointers, a managed class whose member function i want to pass via this native array (multiple times as you can see).
when i call the test constructor with '1' everything works fine. The corruption occurs after the first call to the private_call() method via function pointer. Am i supposed to do something differently? of course, i could always wrap the functions into a single function and just pass that pointer, but i need this finer granularity of function calls.
How wrong am i? Quite a bit i suppose.

Thanks to all that want to help.. i almost regret that i chose to use c++/clr interop on this one. but other people do wild things with it, so i thought i could, too..

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!