Thursday, January 3, 2013

C++ Plugin Debug Log

When I was writing a C++ plugin for Unity, I was wondering that it will be very good if I could write some debug logs into the Unity console, after going on and progressing the plugin without logs, suddenly an idea came to my mind! This is an explanation of how to redirect some debug text from a C++ plugin to Unity's console.

The method is based on Delegates in C# that it can be treated as a typedef of a function pointers in C/C++ and you can call them from unmanaged code.

First of all define a delegate like this:

using System.Runtime.InteropServices;
...
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void MyDelegate(string str);
...

Take care of your calling conventions, by default calling convention of a C++ project in Visual Studio is __cdecl (/Gd) you can find it in the project properties -> C/C++ -> Advanced options.
This is a delegate of type a function that has a string argument and returns nothing.

Then write a method that you will call it from C++ to print the log string, this is a member function of a Unity script file:

static void CallBackFunction(string str)
{
    Debug.Log("::CallBaaaaaaack : " + str);
}

In the Start() function of your Unity script instantiate the defied delegate :

MyDelegate callback_delegate = new MyDelegate( CallBackFunction );

// Convert callback_delegate into a function pointer that can be
// used in unmanaged code.
IntPtr intptr_delegate = 
    Marshal.GetFunctionPointerForDelegate(callback_delegate);
 
// Call the API passing along the function pointer.
SetDebugFunction( intptr_delegate );

The SetDebugFunction is a method that assign the function pointer to a pointer that you defined in your C++ code :

typedef void (*FuncPtr)( const char * );

FuncPtr Debug;

You can access this pointer in other source codes by extern modifier or any global access method you know such as writing a singleton classes.

extern "C"
{
    EXPORT_API void SetDebugFunction( FuncPtr fp )
    {
        Debug = fp;
    }
}

Don't forget to import it in your C# code :

[DllImport ("UnityPlugin")]
public static extern void SetDebugFunction( IntPtr fp );

Now you can call this function every where in your C++ plugin :

...
Debug( "String From C++ Dll" );
...

And the resault :

6 comments:

  1. Completely awesome. Thanks

    ReplyDelete
  2. Thank you, saved me a good amount of time.

    ReplyDelete
  3. FYI, I used your base idea but implemented it a completely other way, by using the "directors" feature of SWIG (as our project already uses it - swig.org). The drawback is that you have to integrate SWIG in your project (of course!), but it clearely eases some aspects (calling conventions, export from C++, etc.).

    Cheers,

    ReplyDelete
  4. Bah, I can't use this because I need debug statements from an audio plugin loaded into the editor at runtime :( Great implementation though!

    ReplyDelete