I have a c++ shared library that can generate some event. I've an interface for the listener and a class that is able to register observers and fire events.
This library may be used used from java, C# and C++ code( compiled with differenc compilers) so I've two file headers: *.h for ANSI C interface and *.hpp for use library directly from C++ code. Now I cannot figure how export observer pattern with a C-like interface.
Here is a little snippet of how code is structured.
// hpp file
class IListener
{
public:
virtual ~IListener() {}
virtual void event1( int ) = 0;
virtual void event2() = 0;
};
using IListenerPtr = std::shared_ptr< IListener >;
class Controller
{
public:
Controller( IListenerPtr listener );
void addListener( IListenerPtr listener );
private:
void threadFunc()
{
while ( true )
{
// an event occured
for ( auto& e : mListeners )
e->event1( 2 );
for ( auto& e : mListeners )
e->event2();
}
}
private:
std::vector< IListenerPtr > mListeners;
};
// h file
#if defined( __MSCVER ) || defined( __MINGW32__ ) || defined( __MINGW64__ )
# define LIB_CALLBACK __stdcall
# define LIB_CALL __cdecl
# if defined( AAMS_EXPORTS )
# define LIB_API __declspec( dllexport )
# else
# define LIB_API __declspec( dllimport )
# endif
#else
# define LIB_API
#endif // WIN32
typedef int libError;
LIB_API libError LIB_CALL libInit( ???? );
How can I make this library usable from C code? A first attempt maybe:
typedef struct libListenerTag
{
typedef void (LIB_CALLBACK *Event1Func)( int );
typedef void (LIB_CALLBACK *Event2Func)();
Event1Func Event1;
Event2Func Event2;
} libListener;
LIB_API libError LIB_CALL libInit( libListener* listener );
and in someway bind libListener to IListener
// cpp file
class CListener : public IListener
{
public:
CListener( libListener* listener
: mListener( listener )
{
}
void event1( int i ) { mListener->Event1( i ); }
void event2() { mListener->Event12(); }
private:
libListener* mListener;
}
Controller* g_controller = nullptr;
LIB_API libError LIB_CALL libInit( libListener* listener )
{
g_controller = new Controller( make_shared< CListener >( listener );
// ...
}
This method doesn't look very good for me. Is a better way to accomplish this?
You're missing something that's standard in C event callbacks: a context pointer.
In C++ your IListener subclass gets an implicit this pointer in its callbacks, meaning it can store state and context info in the instance.
For free functions, you don't have this, so you need to add an explicit argument.
/* c_interface.h */
typedef void (*EventCallback1)(void *context, int arg);
typedef void (*EventCallback2)(void *context);
struct Listener; /* opaque type */
struct Listener *create_listener(EventCallback1, EventCallback2, void *context);
void destroy_listener(struct Listener*);
so from the C side, you pack whatever state you need into a structure, pass it into create_listener as your context, and get it passed back to your functions when they're called.
// c_implementation.cpp
extern "C" {
#include "c_interface.h"
struct Listener: public IListener {
EventCallback1 cb1;
EventCallback2 cb2;
void *context;
Listener(EventCallback1 e1, EventCallback2 e2, void *c)
: cb1(e1), cb2(e2), context(c)
{}
virtual void event1(int arg) {
(*cb1)(context, arg);
}
virtual void event2() {
(*cb2)(context);
}
};
Listener *create_listener(EventCallback1 cb1, EventCallback2 cb2, void *context) {
return new Listener(cb1, cb2, context);
}
}
The client code looks something like
#include "c_interface.h"
struct MyContext {
int things;
double needed;
int to_handle;
char callbacks[42];
};
void cb0(void *context) {}
void cb1(void *context, int arg) {
struct MyContext *c = (struct MyContext *)context;
c->things = arg;
}
void foo() {
struct MyContext *context = malloc(sizeof(*context));
struct Listener *l = create_listener(cb1, cb0, context);
...
Related
I need to load a DLL at runtime. I don't know in advance which DLL to load, but DLLs are the implementation of an interface (pure virtual method). The final goal is to have a pointer to a DLL to call its methods.
Now, I want to test just the DLL loading, calling a test method, but I fail. The test method is void test().
#if defined (_WIN32)
const path PluginExtension(".dll");
#define STDCALL __stdcall
#else
const path PluginExtension(".so");
#define STDCALL
#endif
extern "C"
{
typedef void* (STDCALL* CreatorFunction)();
constexpr auto FunctionName{ "CommandGeneratorEngine::Interface::test" };
}
auto library = LoadLibraryExW(pluginPath.native().c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (library != nullptr) {
auto creator = (CreatorFunction)GetProcAddress(library, FunctionName);
if (creator != nullptr) {
// do something
}
else
{
throw std::exception("Error when loading comm plugin");
}
The interface
namespace TGComm {
namespace CommandGeneratorEngine {
class Interface {
public:
using Ptr = std::shared_ptr<Interface>;
virtual ~Interface() = default;
virtual void test() = 0;
};
}
}
And the interface implementation:
class LHFImplementationInterface : public CommandGeneratorEngine::Interface
{
public:
void __declspec(dllexport) __stdcall test() override{
// do something...
}
};
The line
auto creator = (CreatorFunction)GetProcAddress(library, FunctionName);
returns a null value.
Solved.
I created a new header file, h1.h
#include "Global.h"
extern "C" {
LHCOMMQT_LIB void* createTGCommPlugin();
}
its cpp is
#include "h1.h"
#include "LHFImplementationinterface.h"
void* createTGCommPlugin() {
auto p = new LHFImplementationInterface();
return p;
}
and global.h is
#ifndef LHCOMM_GLOBAL_H
#define LHCOMM_GLOBAL_H
#include <QtCore/qglobal.h>
#ifdef LHCOMMQT_EXPORTS
# define LHCOMMQT_LIB Q_DECL_EXPORT
#else // ifdef LHCOMMQT_EXPORTS
# define LHCOMMQT_LIB Q_DECL_IMPORT
#endif // ifdef LHCOMMQT_EXPORTS
#endif // LHCOMM_GLOBAL_H
in main:
typedef void* (STDCALL* CreatorFunction)();
constexpr auto FunctionName{ "createTGCommPlugin" };
try {
auto library = LoadLibraryExW(pluginPath.native().c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (library != nullptr) {
auto creator = (CreatorFunction)GetProcAddress(library, FunctionName);
...
and now works good
I want to use a member function of a CPP Class for a FreeRTOS taskFunction.
Here is my initial solution
#ifndef TaskCPP_H
#define TaskCPP_H
#include "FreeRTOS.h"
#include "task.h"
template<typename T>
class TaskBase {
protected:
xTaskHandle handle;
void run() {};
public:
static void taskfun(void* parm) {
static_cast<T*>(parm)->run();
#if INCLUDE_vTaskDelete
vTaskDelete(static_cast<T*>(parm)->handle);
#else
while(1)
vTaskDelay(10000);
#endif
}
virtual ~TaskBase() {
#if INCLUDE_vTaskDelete
vTaskDelete(handle);
#endif
return;
}
};
#endif /* __TaskCPP_H__ */
#ifndef HTTPSERVER_H_
#define HTTPSERVER_H_
#include "TaskBase.h"
#include <string.h>
#include "lwip/opt.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/tcp.h"
#include "lwip/api.h"
#include "lwip/ip_addr.h"
class HttpServer;
class HttpServer : public TaskBase<HttpServer> {
public:
HttpServer();
virtual ~HttpServer();
void run();
void listen(ip_addr *address, uint8_t port);
void print();
protected:
char taskName[50];
ip_addr address;
uint8_t port;
};
#endif /* HTTPSERVER_H_ */
#include "HttpServer.h"
HttpServer::HttpServer()
{
}
void HttpServer::listen(ip_addr *address, uint8_t port) {
char buf[16];
sprintf(taskName, "%s_%d\r\n", ipaddr_ntoa_r(address, buf, 16), port);
DEBUGOUT("ADDRESS Listen: %x\r\n", &taskName);
this->handle = sys_thread_new(this->taskName, &taskfun, NULL, DEFAULT_THREAD_STACKSIZE + 128, DEFAULT_THREAD_PRIO);
}
void HttpServer::run() {
while(1) {
print();
Board_LED_Toggle(LEDS_LED2);
vTaskDelay(configTICK_RATE_HZ / 14);
}
}
void HttpServer::print() {
DEBUGOUT("ADDRESS Listen: %x\r\n", &taskName);
}
HttpServer::~HttpServer() {
// TODO Auto-generated destructor stub
}
Now my Problem ist that the context is not the same inside the run function. Printing the memory location of taskName is different inside listen(...) and run(...)
My nasty suspicion is that doing a static_cast of the run function looses the class context? Am I wrong?
Is there any other idea to provide a static function pointer to freeRTOS of a Class member function?
Here are the missing functions. The main and the sys_thread_new which is a wrapper only.
int main(void) {
prvSetupHardware();
HttpServer server;
server.listen(IP_ADDR_ANY, 80);
/* Start the scheduler */
vTaskStartScheduler();
/* Should never arrive here */
return 1;
}
sys_thread_t sys_thread_new( const char *pcName, void( *pxThread )( void *pvParameters ), void *pvArg, int iStackSize, int iPriority )
{
xTaskHandle xCreatedTask;
portBASE_TYPE xResult;
sys_thread_t xReturn;
xResult = xTaskCreate( pxThread, ( signed char * ) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );
if( xResult == pdPASS )
{
xReturn = xCreatedTask;
}
else
{
xReturn = NULL;
}
return xReturn;
}
I use something like this:
class MyClass{
public:
static void run( void* pvParams ){
((MyClass*)pcParams)->runInner();
}
void runInner(){
while ( true ){
// TODO code here
}
}
};
then pass pointer to an object when creating task
MyClass* x = new MyClass;
xTaskCreate( MyClass::run, taskName, stackDepth, (void*) x, taskPrio, taskHandle );
edit:
Assuming you want to use templates:
template<typename T>
void run ( T* p ){
((T*)p)->run();
}
class MyClass{
public:
void run(){
while ( true ){
// task code
}
}
}
then creating the task
MyClass* x = new MyClass;
xTaskCreate( run<MyClass>, taskName, stackDepth, x, taskPrio, taskHandle );
Edit:
You need to cast run to (void (*)(void*))
xTaskCreate( (void (*)(void*))run<MyClass>, taskName, stackDepth, x, taskPrio, taskHandle );
please I need your help, I googled a lot and any answer for this problem....
I have a solution that includes various Unmanaged C++ projects, but now I need to use a Visual C++ Form (that is Managed) into the main unmanaged project, and comunicate with it.
It's possible to do it? via wrapper, dll.... how?
I googled a lot but all the cases I found talk about how use Unmanaged into Managed, or are incomplete examples. I found this example and seems that it works, but doesn't compile.
http://www.codeproject.com/Articles/9903/Calling-Managed-Code-from-Unmanaged-Code-and-vice
I'm using Visual Studio 2012.
Thanks a lot.
Finally I found a solution...
1 - When I want to have the FORM control into non managed code (for create, show, hide, set controls value...) I exported static methods as __declspec(dllexport)
2 - When I want to send info from FORM to non managed code I do it using callbacks.
I'm going to explain it....
Firstly... I create a CLR project as DLL, create a windows Form (MyForm.h) with some controls:
1 exit button
1 button that SEND info TO NON MANAGED code
and 1 checkbox that RECEIVE new status FROM NON MANAGED code
(I ommit the gode generated by visual studio)
namespace TestUnmanaged {
... code ommitted ...
private: System::Void QuitBtn_Click(System::Object^ sender, System::EventArgs^ e) {
this->Close();
}
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
//Call to non managed function!!!!
CallToCallBack();
}
};
}
Then, I create a static (non managed) class that has a pointer to the form, and manage it's creation, etc..
//Type of my specific callback function
typedef std::function<bool (int& _msg)> T_CALLBACK_FUNC;
public class unmanagedClass
{
public:
unmanagedClass();
~unmanagedClass();
void CreateForm( void );
HWND getFormHandle(void) const;
void setCallbackFunction(T_CALLBACK_FUNC _callbackFuntion);
void callToCallbackFunction(int _value);
//public for fast tests
gcroot<TestUnmanaged::MyForm^> p;
private:
HWND m_hWnd;
T_CALLBACK_FUNC m_funcionCallback;
};
unmanagedClass s_unmanaged;
Here's the .cpp implementation...
//////////////////////////////////////////////////////////////////////////
// Static Functions for DLL_EXPORT
//////////////////////////////////////////////////////////////////////////
void CloseForm( void )
{
Application::Exit();
}
void CallToCallBack( void )
{
int valor = 5;
s_unmanaged.callToCallbackFunction(valor);
}
//////////////////////////////////////////////////////////////////////////
// Methods for Unmanaged Class
//////////////////////////////////////////////////////////////////////////
HWND unmanagedClass::getFormHandle( void ) const
{
return m_hWnd;
}
unmanagedClass::unmanagedClass()
{
p = gcnew(TestUnmanaged::MyForm);
}
unmanagedClass::~unmanagedClass()
{
delete (p);
}
void unmanagedClass::CreateForm( void )
{
Application::Run(p);
}
void unmanagedClass::setCallbackFunction( T_CALLBACK_FUNC _callbackFuntion )
{
m_funcionCallback = _callbackFuntion;
}
void unmanagedClass::callToCallbackFunction( int _value )
{
m_funcionCallback(_value);
}
Now, I create all the static functions that will be exported as dllexport:
__declspec(dllexport) void CreateFormDLL(void)
{
s_unmanaged.CreateForm();
}
__declspec(dllexport) void CloseFormDLL(void)
{
s_unmanaged.p->Close();
}
__declspec(dllexport) void ShowFormDLL(void)
{
s_unmanaged.p->Show();
}
__declspec(dllexport) void HideFormDLL(void)
{
s_unmanaged.p->Hide();
}
__declspec (dllexport) void setCallbackFunctionDLL( T_CALLBACK_FUNC& _doFunction)
{
s_unmanaged.setCallbackFunction(_doFunction);
}
__declspec (dllexport) void setCheckBoxDLL( bool _newValue )
{
s_unmanaged.p->getCam01ChkBox()->Checked = _newValue;
}
__declspec (dllexport) HWND getFormHandleDLL( void )
{
return (HWND)s_unmanaged.p->Handle.ToPointer();
}
Now.... I create the Win32 console App project and link the Managed project. This will be the main process, and the class "ManagedWrapper". Here is the source....
class ManagedWrapper
{
public:
ManagedWrapper(void);
~ManagedWrapper(void);
void init(void);
void createForm(void);
void showForm(void);
void closeForm(void);
void setCam01ChkBox(const bool _newValue);
private:
void createFormThread(void);
static bool callBackReceiver(int a);
};
#include "ManagedWrapper.h"
#include <thread>
__declspec(dllimport) void CreateFormDLL(void);
__declspec(dllimport) void CloseFormDLL(void);
__declspec(dllimport) void ShowFormDLL(void);
__declspec(dllimport) void HideFormDLL(void);
typedef std::function<bool (int& _msg)> T_CALLBACK_FUNC;
__declspec (dllimport) void setCallbackFunctionDLL( T_CALLBACK_FUNC& _doFunction);
__declspec (dllimport) void setCheckBoxDLL( bool _newValue );
ManagedWrapper::ManagedWrapper(void)
{
}
ManagedWrapper::~ManagedWrapper(void)
{
}
void ManagedWrapper::createForm( void )
{
std::thread showThread (&ManagedWrapper::createFormThread, this);
showThread.detach();
}
void ManagedWrapper::createFormThread( void )
{
CreateFormDLL();
}
void ManagedWrapper::closeForm( void )
{
CloseFormDLL();
}
void ManagedWrapper::showForm( void )
{
ShowFormDLL();
}
void ManagedWrapper::init( void )
{
setCallbackFunctionDLL(T_CALLBACK_FUNC(&ManagedWrapper::callBackReceiver));
}
bool ManagedWrapper::callBackReceiver( int a )
{
printf("Hello world. Parameter value = %d\n",a);
return true;
}
void ManagedWrapper::setCam01ChkBox( const bool _newValue )
{
setCheckBoxDLL( _newValue );
}
And finally.... the simplest use from the main(), create the Form, set the checkbox values, if the user click on the button the callback function writes on console... and finally close.
int _tmain(int argc, _TCHAR* argv[])
{
ManagedWrapper umWrap;
umWrap.init();
umWrap.createForm();
system("PAUSE");
umWrap.setCam01ChkBox(true);
system("PAUSE");
umWrap.setCam01ChkBox(false);
system("PAUSE");
umWrap.closeForm();
return 0;
}
Sorry for the long answer, but I believe that may be usefull for somebody.
I tried to do a fancy macro to have some debug informations: The name of the scope you currently are! This can be picked up by e.g. an assert. I tried to made it recursive:
// Global namespace
struct ScopeDef{ static const char* GetName() {return "";} };
typedef ScopeDef ScopeDefParent;
// Macro to place into your namespace/scope
#define NG_SCOPEDEF(scopename) \
struct ScopeDef { \
static const char* GetName() {return scopename;} \
typedef ScopeDefParent Parent; \
}; \
typedef ScopeDef ScopeDefParent;
And using it like:
// Recursive template for testing
template< class T > void PrintImpl() {
PrintImpl< T::Parent >();
printf("::%s", T::GetName() );
}
template<> void PrintImpl< ::ScopeDef >() {}
template< class T > void PrintS() { PrintImpl<T>(); printf("\n");}
// Lets try it:
namespace First {
NG_SCOPEDEF( "First" );
namespace Second {
NG_SCOPEDEF( "Second" );
static void AFun() {
// This prints "::First::Second"
PrintS<ScopeDef>();
}
}
struct Third {
NG_SCOPEDEF( "Third" );
static void BFun() {
// This is endless recursion
PrintS<ScopeDef>();
}
};
}
It doesn't work in class scopes, because the order of definitions don't matter.
This is not a good solution. So is there a way to access the parent scope in a way? In regular code I would just qualify ("::First::ScopeDef"), but that's nothing for a macro.
You could do something like this in C++, where you put in the macro whenever you open a scope and let the destructor take care of cleanup when the scope exits. This example will print out the full scope to stderr, and the output of this code is as follows.
main
main::First
main::First::Second
main::First::Second::DummyClass::DummyFunction
main::First
main
Here is the code:
#include <stdio.h>
class NG_SCOPE_CLASS;
NG_SCOPE_CLASS* NG_SCOPE_END = 0;
class NG_SCOPE_CLASS
{
public:
NG_SCOPE_CLASS(const char* scope)
{
_scope = scope;
_prev = NG_SCOPE_END;
NG_SCOPE_END = this;
}
~ NG_SCOPE_CLASS()
{
NG_SCOPE_END = _prev;
}
void PrintScope()
{
if(_prev)
{
_prev->PrintScope();
fprintf(stderr, "::");
}
fprintf(stderr, "%s", _scope);
}
private:
NG_SCOPE_CLASS* _prev;
const char* _scope;
};
#define NG_SCOPE_PRINT { if(NG_SCOPE_END) { NG_SCOPE_END->PrintScope(); fprintf(stderr, "\n"); } }
#define NG_SCOPE(X) NG_SCOPE_CLASS _NG_SCOPE_CLASS(X)
// THAT'S IT FOR THE DEFINITIONS ABOVE, BELOW IS JUST SOME SAMPLE CODE.
class DummyClass
{
public:
void DummyFunction()
{
NG_SCOPE("DummyClass::DummyFunction");
NG_SCOPE_PRINT;
}
};
int main(int argc, const char* argv[])
{
NG_SCOPE("main");
NG_SCOPE_PRINT;
{
NG_SCOPE("First");
NG_SCOPE_PRINT;
{
NG_SCOPE("Second");
NG_SCOPE_PRINT;
DummyClass theDummyInstance;
theDummyInstance.DummyFunction();
}
NG_SCOPE_PRINT;
}
NG_SCOPE_PRINT;
}
For completeness, our working solution:
#if defined(_MSC_VER)
// GCC solution below. Note MSVC gives warning about unused typedefs but can be suppressed.
#define NG_SCOPEDEF(scopename) \
struct ScopeDefTag { static const char* Name(){return (scopename);}; }; \
typedef ::Scopes::Impl< ScopeDefTag, ScopeDef > ScopeDefHelper; \
struct ScopeDef : ScopeDefHelper {}
#else
// GCC seems to not look ahead.
#define NG_SCOPEDEF(scopename) \
struct ScopeDefTag { static const char* Name(){return (scopename);}; struct SDH : ::Scopes::Impl< ScopeDefTag, ScopeDef >{}; }; \
struct ScopeDef : ScopeDefTag::SDH {}
#endif
namespace Scopes {
struct Chain {
const char* const m_Lit;
const Chain* const m_Prev;
Chain(const char* lit, const Chain* prev) :m_Lit(lit), m_Prev(prev) {}
};
template< bool DMY = true >
struct RootScopeDef
{
static const Chain chain;
static const Chain* Get() { return &chain; }
};
// Being template just to have this in header:
template< bool DMY > const Chain RootScopeDef<DMY>::chain = Chain( "", 0 );
template< class TAG, class PARENT >
struct Impl {
static const Chain chain;
static const Chain* Get() { return &chain; }
typedef PARENT Parent;
};
template< class TAG, class PARENT >
const Chain Impl<TAG, PARENT>::chain = Chain( TAG::Name(), &PARENT::chain );
} // namespace
// Global namespace
typedef Scopes::RootScopeDef<> ScopeDef;
It is based on loopholes in the compiler and is not standard compliant! MSVS thinks that the ScopeDef used as template argument isn't the following one, since it is depending on that very typedef, so it resolves to the parent one, which will be shadowed after. This also works when the macro is placed in a template, because MSVS instantiates them lazily. GCC instead seems to just not look ahead and resolves the base of SDH to the correct one. MSVS would produce an infinite loop of m_Prev references here.
Bottom line: this gives you a nice way to print out namespaces and classes for debugging purposes, but also serves as types for e.g. specializations of templates!
I've got problems passing a member function of a C++ CLI class to a native C callback from a library.
To be precise its the Teamspeak 3 SDK.
You can pass a non member function using the following code without problem:
struct ClientUIFunctions funcs;
/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
funcs.onConnectStatusChangeEvent = onConnectStatusChangeEvent;
But I need to pass a pointer to a member function, for example:
funcs.onConnectStatusChangeEvent = &MyClass::onConnectStatusChangeEvent;
Any other idea how to use the event within a non static member function is welcome to.
Thanks in advance!
This can only be done via a static class function because C doesn't know anything about the vtable or what object the function is part of. See below for a C++ and Managed C++ example
This could however be a work around, build a wrapper class which handles all the callbacks you need.
#include <string.h>
struct ClientUIFunctions
{
void (*onConnectStatusChangeEvent)(void);
};
class CCallback
{
public:
CCallback()
{
struct ClientUIFunctions funcs;
// register callbacks
my_instance = this;
/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
funcs.onConnectStatusChangeEvent = sOnConnectStatusChangeEvent;
}
~CCallback()
{
// unregister callbacks
my_instance = NULL;
}
static void sOnConnectStatusChangeEvent(void)
{
if (my_instance)
my_instance->OnConnectStatusChangeEvent();
}
private:
static CCallback *my_instance;
void OnConnectStatusChangeEvent(void)
{
// real callback handler in the object
}
};
CCallback *CCallback::my_instance = NULL;
int main(int argc, char **argv)
{
CCallback *obj = new CCallback();
while (1)
{
// do other stuff
}
return 0;
}
Another possibility would be if the callback supports and void *args like void (*onConnectStatusChangeEvent)(void *args); which you can set from the plugin. You could set the object in this args space so in de sOnConnectStatusChangeEvent you would have something like this:
static void sOnConnectStatusChangeEvent(void *args)
{
if (args)
args->OnConnectStatusChangeEvent();
}
For managed C++ it should be something like this, however I can't get it to compile because it doesn't like the template brackets..
wrapper.h:
using namespace std;
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Text;
namespace Test
{
struct ClientUIFunctions
{
void (*onConnectStatusChangeEvent)(void);
};
public delegate void ConnectStatusChangeEvent(void);
public ref class ManagedObject
{
public:
// constructors
ManagedObject();
// destructor
~ManagedObject();
//finalizer
!ManagedObject();
event ConnectStatusChangeEvent^ OnConnectStatusChangeEvent {
void add(ConnectStatusChangeEvent^ callback) {
m_connectStatusChanged = static_cast<ConnectStatusChangeEvent^> (Delegate::Combine(m_connectStatusChanged, callback));
}
void remove(ConnectStatusChangeEvent^ callback) {
m_connectStatusChanged = static_cast<ConnectStatusChangeEvent^> (Delegate::Remove(m_connectStatusChanged, callback));
}
void raise(void) {
if (m_connectStatusChanged != nullptr) {
m_connectStatusChanged->Invoke();
}
}
}
private:
ConnectStatusChangeEvent^ m_connectStatusChanged;
};
class CCallback
{
public:
static void Initialize(ManagedObject^ obj);
static void DeInitialize(void);
private:
static void sOnConnectStatusChangeEvent(void);
static gcroot<ManagedObject^> m_objManagedObject;
};
}
wrapper.cpp:
#include <string.h>
#include "wrapper.h"
using namespace System;
using namespace Test;
void CCallback::Initialize(ManagedObject^ obj)
{
struct ClientUIFunctions funcs;
// register callbacks
m_objManagedObject = obj;
/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
funcs.onConnectStatusChangeEvent = sOnConnectStatusChangeEvent;
}
void CCallback::DeInitialize(void)
{
// unregister callbacks
m_objManagedObject = nullptr;
}
void CCallback::sOnConnectStatusChangeEvent(void)
{
if (m_objManagedObject != nullptr)
m_objManagedObject->OnConnectStatusChangeEvent();
}
// constructors
ManagedObject::ManagedObject()
{
// you can't place the constructor in the header but just for the idea..
// create wrapper
CCallback::Initialize(this);
}
// destructor
ManagedObject::~ManagedObject()
{
this->!ManagedObject();
}
//finalizer
ManagedObject::!ManagedObject()
{
CCallback::DeInitialize();
}
gcroot<ManagedObject^> CCallback::m_objManagedObject = nullptr;
int main(array<System::String ^> ^args)
{
ManagedObject^ bla = gcnew ManagedObject();
while (1)
{
// do stuff
}
return 0;
}