none
Using callback functions in C++/CLI RRS feed

  • Question

  • I have a native C++ implementation of a function that takes a callback function.  I am finding myself having to create a "helper" unmanaged c++ class to contain the callback function because the following code doesn't work. 

    Without the helper class workaround, the following code would look like this:

    typedef void (*CallbackFunction)();

    void SomeFunction( CallbackFunction callback )
    {
       callback();
    }

    class UnmanagedClass // this compiles
    {
    public:
       UnmanagedClass()
       {
          SomeFunction( Print );
       }
    private:
       static void Print()
       {
          printf(
    "in UnmanagedClass\n" );
       }
    };

    ref class ManagedClass // this doesn't compile
    {
    public:
       ManagedClass()
       {
          SomeFunction( Print ); // <-- gives error
       }
    private:
       
    static void Print()
       {
          printf(
    "in ManagedClass\n" );
       }
    };

    The error is:

    error C2664: 'SomeFunction' : cannot convert parameter 1 from 'void (__clrcall *)(void)' to 'CallbackFunction'

    Is there a way I can accomplish what I'm trying to do here?

     

    Wednesday, November 9, 2005 9:12 PM
    Moderator

Answers

All replies

  • Yes there is a way using delegates.

    I had a similar problem just the other day. Please see

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=127377&SiteID=1

    -- Henrik
    Thursday, November 10, 2005 7:57 AM
  • That definitely was what I was after.  I jumped around a bit and found this article to be most directly relevant: http://msdn2.microsoft.com/en-us/library/367eeye0.aspx

    Thanks, Henrik!

    Brian

    Thursday, November 10, 2005 3:48 PM
    Moderator
  • Since I have to do this in several places, so I squashed everything in the above link into a helper class and a macro, hiding away all the plumbing.  May not be the best implementation, but it could be useful for others.  (Hopefully, the GC pinning within a temporary PinDelegate object is correct.)

    ref class PinDelegate
    {
    public:
       PinDelegate( Delegate^ _d ) 
       : d( _d )  
       {
          gch = GCHandle::Alloc( d );
       }

       ~PinDelegate()   
       {
          gch.Free();
       }

       
    operator Delegate^()
       {
          
    return d;
       }

    private:
       Delegate^ d;
       GCHandle gch;
    };

    #define MEMBER_CALLBACK( mcbtype, ucbtype, mcb ) \
       static_cast<ucbtype>( Marshal::GetFunctionPointerForDelegate( \
       (Delegate^)PinDelegate(
    gcnew mcbtype( this, &mcb ) ) ).ToPointer() ) )

    The macro takes the managed callback type (a delegate you have to define yourself), the unmanaged callback type, and the managed callback method.  I use a bound delegate, meaning that the managed callback method is NOT a static method.

    The managed version of the class above was changed to:

    ref class ManagedClass
    {
    public:
       delegate void PrintDelegate();
       ManagedClass()
       {
          SomeFunction( MEMBER_CALLBACK( PrintDelegate, CallbackFunction, ManagedClass::Print ) );
       }

    private:
       v
    oid Print() // no longer static (a benefit)
       {
          printf(
    "in ManagedClass\n" );
       }
    };

    Note: the seemingly redundant qualification for ManagedClass::Print was to avoid a compiler error, and I wonder if it is a compiler bug.  Also, it would be nice if the compiler would allow the PrintDelegate definition to be function scope, just like typedefs can.

    Aside: there is enough information for a hypothetical C++/CLI 2.0 compiler to "just work" by introducing a built-in cast unmanaged_fnptr (along the lines of pin_ptr).  Then I could say:

        SomeFunction( unmanaged_fnptr<CallbackFunction>(Print) );

    (I actually did do some experimentation with generics, but I don't think I could have pulled it off.)

    Thursday, November 10, 2005 5:51 PM
    Moderator
  • Pretty old topic this

    However, I was stuck exactly with the same problem and I was able to fix it with the info that was available here.

    Thanks Brian for posting your question as well as the solution.

    Friday, May 7, 2010 6:59 AM