Assuming you want to expose a managed OutputLog for .NET clients to consume, and pass the wrapped, native OutputLog to a library while allowing .NET consumers to be notified of errors, you could use something along these lines.
#include "stdafx.h"
#include <string>
#pragma region NATIVE
typedef void (*ErrorCallback)(class OutputLog& log, const std::string& message, void* userData);
class OutputLog
{
private:
ErrorCallback m_callback;
void* m_userData;
public:
OutputLog()
: m_callback(nullptr), m_userData(nullptr) { }
void SetCallback(ErrorCallback callback, void* userData) {
m_callback = callback;
m_userData = userData;
}
void Error(const std::string& message)
{
if (m_callback) {
m_callback(*this, message, m_userData);
}
}
};
#pragma endregion
#pragma region MANAGED
#include <msclr/gcroot.h>
using namespace System;
using namespace System::Runtime::CompilerServices;
class NativeErrorCallbackHandler
{
public:
NativeErrorCallbackHandler(ref class OutputLogManaged^ owner);
private:
static void OnError(class OutputLog& log, const std::string& message, void* userData);
msclr::gcroot<OutputLogManaged^> m_owner;
};
public delegate void ErrorEventHandler(ref class OutputLogManaged^ log, String^ message);
public ref class OutputLogManaged
{
public:
OutputLogManaged()
: m_nativeOutputLog(new OutputLog),
m_nativeHandler(new NativeErrorCallbackHandler(this)) { }
~OutputLogManaged() { // = Dispose
this->!OutputLogManaged();
}
!OutputLogManaged() // = Finalize
{
delete m_nativeOutputLog;
m_nativeOutputLog = nullptr;
delete m_nativeHandler;
m_nativeHandler = nullptr;
}
event ErrorEventHandler^ Error
{
[MethodImplAttribute(MethodImplOptions::Synchronized)]
void add(ErrorEventHandler^ value) {
m_managedHandler = safe_cast<ErrorEventHandler^>(Delegate::Combine(value, m_managedHandler));
}
[MethodImplAttribute(MethodImplOptions::Synchronized)]
void remove(ErrorEventHandler^ value) {
m_managedHandler = safe_cast<ErrorEventHandler^>(Delegate::Remove(value, m_managedHandler));
}
private:
void raise(OutputLogManaged^ log, String^ message) {
auto managedHandler = m_managedHandler;
if (managedHandler != nullptr)
managedHandler(this, message);
}
}
internal:
void RaiseErrorEvent(String^ message) {
Error(this, message);
}
OutputLog* GetNative() { return m_nativeOutputLog; }
private:
OutputLog* m_nativeOutputLog;
NativeErrorCallbackHandler* m_nativeHandler;
ErrorEventHandler^ m_managedHandler;
};
NativeErrorCallbackHandler::NativeErrorCallbackHandler(OutputLogManaged^ owner)
: m_owner(owner)
{
m_owner->GetNative()->SetCallback(&OnError, this);
}
void NativeErrorCallbackHandler::OnError(OutputLog& log, const std::string& message, void* userData)
{
static_cast<NativeErrorCallbackHandler*>(userData)->m_owner->RaiseErrorEvent(
gcnew String(message.c_str(), 0, message.size()));
}
#pragma endregion
#pragma region Test
void Test(OutputLog& log)
{
log.Error("This is a test.");
}
void OnError(OutputLogManaged^ sender, String^ message)
{
Console::WriteLine(message);
}
int main(array<System::String ^> ^args)
{
OutputLogManaged managedLog;
managedLog.Error += gcnew ErrorEventHandler(&OnError);
Test(*managedLog.GetNative());
return 0;
}
#pragma endregion