I'm currently developing a nodejs module, written in C++ and I've been looking for a way to convert a v8::Object to a cv::Mat object from opencv, without any luck for now.
I saw that nan library could help to convert objects, but i couldn't find how, I don't even know if it's possible to convert them in my case.
The v8 juice project would meet my expectations but since it has been abandoned, I just don't know how to do this.
Here is a snippet of what I'm trying to do :
void
BRMatcher::run(const v8::FunctionCallbackInfo<v8::Value>& args)
{
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
if (args.Length() < 1)
{
isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, "Missing parameter [Mat img].")));
return ;
}
if (!args[0]->IsObject())
{
isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, "Parameter [Mat img] must be an object.")));
return ;
}
v8::Local<v8::Object> cvMat(args[0]->ToObject());
Mat img = ??? // This is where I ended up...
// ...
}
All the posts talking about this on StackOverflow are outdated (older versions or tools that are not working anymore...)
So my questions are: how can I convert the argument I receive in my function to a cv::Mat object ? To any type I want ?
Any help would be appreciated, thanks!
At first I would recommend to look on existing openCV bindings for Node.js like node-opencv.
If you need to bind C++ and JavaScript code, there are several libraries. As an author of one of them, v8pp, I know about several other:
vu8 (abandoned)
v8-juice (abandoned)
Script bindng in cpgf
Embind
As I know, to convert a C++ object into v8::Object all of them use v8::Object::SetAlignedPointerInInternalField() function.
C++ object to v8::Object conversions are usually performed with mapping of a C++ pointer to persistent handle of the V8 object in a map container.
Do have a look on the Nodejs.org C++ addon and Nan tutorials. Though both are a little misleading, they anyhow describe the canonical way. Use Nan over direct V8 APIs since especially this part (was and still) is changing a lot.
With Nan what you're looking for is passing wrapped objects. More precisely this line is the heart of it.
In this fork of Node-OpenCV, I am doing exactly that with cv::Mat in order to make it a first class object for JS-land. Maybe this implementation may help you.
TL;DR
Wrap the object with Nan::ObjectWrap and pass it around. Internally uses v8::SetInternalFieldPointer(); for you. This is basically copy-paste-able.
// lib/mat.js
var cv = require('./bindings')('addon');
function Mat() {
}
/**
* returns the wrapped c++ object
* [arguments optional]
*/
Mat.prototype.createMat = function (a) {
var args = Array.prototype.slice.call(arguments);
return cv.Mat.createMat(args[0])
}
// src/addon.cc
// just initializes all your modules. Magic happening in mat.h and matwrap.h
// matwrap.cc is the implementation of the wrapped object. mat.cc holds
// JS-libarary specific methods
#include <nan.h>
#include "opencv.h"
#include "imgproc.h"
#include "mat.h"
#include "matwrap.h"
void InitAll(v8::Local<v8::Object> exports) {
Opencv::Init(exports);
ImgProc::Init(exports);
Matwrap::Init();
Mat::Init(exports);
}
NODE_MODULE(addon, InitAll)
Important stuff here...
// src/matwrap.h
#ifndef MATWRAP_H
#define MATWRAP_H
#include <opencv2/opencv.hpp>
#include <nan.h>
class Matwrap : public Nan::ObjectWrap {
public:
static void Init();
static v8::Local<v8::Object> NewInstance(v8::Local<v8::Value> arg);
cv::Mat Val() const { return val_; }
private:
Matwrap();
~Matwrap();
static Nan::Persistent<v8::Function> constructor;
static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);
cv::Mat val_;
};
#endif
...and here you're are wrapping it (basically that's it; follow below for consumption):
// src/matwrap.cc
#include <node.h>
#include "matwrap.h"
#include <opencv2/opencv.hpp>
Matwrap::Matwrap() {};
Matwrap::~Matwrap() {};
Nan::Persistent<v8::Function> Matwrap::constructor;
void Matwrap::Init() {
Nan::HandleScope scope;
// Prepare constructor template
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->SetClassName(Nan::New("Matwrap").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
constructor.Reset(tpl->GetFunction());
}
void Matwrap::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
// wrap it...
Matwrap* obj = new Matwrap();
cv::Mat src;
obj->val_ = src;
obj->Wrap(info.This());
// return wrapped here...
info.GetReturnValue().Set(info.This());
}
v8::Local<v8::Object> Matwrap::NewInstance(v8::Local<v8::Value> arg) {
Nan::EscapableHandleScope scope;
// const unsigned argc = 1;
// v8::Local<v8::Value> argv[argc] = { arg };
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
v8::Local<v8::Object> instance = cons->NewInstance();
return scope.Escape(instance);
}
For consumption you could do stuff like this:
// lib/mat.js
/**
* Returns true if the array has no elements.
* #param {Object} mat - native cv::Mat
* #return {Boolean}
*/
Mat.prototype.empty = function (mat) {
var args = Array.prototype.slice.call(arguments);
return cv.Mat.empty(args[0])
}
// src/mat.h
// This is your API
#ifndef MAT_H
#define MAT_H
// #include <opencv2/opencv.hpp>
#include <nan.h>
class Mat : public Nan::ObjectWrap {
public:
static void Init(v8::Local<v8::Object> exports);
private:
explicit Mat(double value = 0);
~Mat();
static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);
static void CreateMat(const Nan::FunctionCallbackInfo<v8::Value>& info);
static void Empty(const Nan::FunctionCallbackInfo<v8::Value>& info);
static void Total(const Nan::FunctionCallbackInfo<v8::Value>& info);
static void Type(const Nan::FunctionCallbackInfo<v8::Value>& info);
static Nan::Persistent<v8::Function> constructor;
double value_;
};
#endif
// src/mat.cc
#include "mat.h"
#include "matwrap.h"
#include <opencv2/opencv.hpp>
Nan::Persistent<v8::Function> Mat::constructor;
Mat::Mat(double value) : value_(value) {
}
Mat::~Mat() {
}
void Mat::Init(v8::Local<v8::Object> exports) {
Nan::HandleScope scope;
// Prepare constructor template
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->SetClassName(Nan::New("Mat").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
Nan::SetPrototypeMethod(tpl, "createMat", CreateMat);
Nan::SetPrototypeMethod(tpl, "empty", Empty);
Nan::SetPrototypeMethod(tpl, "total", Total);
Nan::SetPrototypeMethod(tpl, "type", Type);
constructor.Reset(tpl->GetFunction());
exports->Set(Nan::New("Mat").ToLocalChecked(), tpl->GetFunction());
}
void Mat::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (info.IsConstructCall()) {
// Invoked as constructor: `new Opencv(...)`
double value = info[0]->IsUndefined() ? 0 : info[0]->NumberValue();
Mat* obj = new Mat(value);
obj->Wrap(info.This());
info.GetReturnValue().Set(info.This());
} else {
// Invoked as plain function `Opencv(...)`, turn into construct call.
const int argc = 1;
v8::Local<v8::Value> argv[argc] = { info[0] };
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
info.GetReturnValue().Set(cons->NewInstance(argc, argv));
}
}
void Mat::CreateMat(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(Matwrap::NewInstance(info[0]));
}
void Mat::Empty(const Nan::FunctionCallbackInfo<v8::Value>& info) {
Matwrap* obj = Nan::ObjectWrap::Unwrap<Matwrap>(info[0]->ToObject());
// check through cv::Mat::empty()
if (obj->Val().empty()) {
// return JS bool
info.GetReturnValue().Set(Nan::True());
} else {
// TODO: logically not correct
info.GetReturnValue().Set(Nan::False());
}
}
Related
How can an object access a variable belonging the class containing it?
Right now I have a class called system that contains some other objects, and those objects need to access and modify one of the variables in the System class.
Example:
Class System {
BlockA _blockA = new BlockA();
BlockB _blockB = new BlockB();
BlockC _blockC = new BlockC();
BlockD _blockD = new BlockD();
int myVariable;
...stuff...
}
Class BlockA {
...stuff...
void someFunction () {
System.myVariable++;
}
...stuff...
}
etc...
Alright so I thought about this some more and realized that when initializing the objects, I will pass a pointer to the variable of interest. That way all objects can read that variable. For anyone else with this problem, if you need to write, you'll have to make sure that the variable is atomic.
Hard to know exactly what you're after, but appears something along these lines:
#BlockA.h
#ifndef BLOCKA_H
#define BLOCKA_H
class System;
class BlockA {
System* sys;
public:
BlockA(System* sys) : sys(sys) {}
void SomeFunction();
};
#endif // BLOCKA_H
#BlockA.cpp
#include "System.h"
void BlockA::SomeFunction() {
sys->setMyVariable(sys->getMyVariable() + 1);
}
#System.h
#ifndef SYSTEM_H
#define SYSTEM_H
class BlockA;
class System {
BlockA* _blockA;
int myVariable;
public:
System();
int getMyVariable() const;
void setMyVariable(int value);
BlockA& getBlockA() const;
};
#endif // SYSTEM_H
#System.cpp
#include "System.h"
#include "BlockA.h"
System::System()
: _blockA(new BlockA(this)) { }
int System::getMyVariable() const {
return myVariable;
}
void System::setMyVariable(int value) {
myVariable = value;
}
BlockA& System::getBlockA() const {
return *_blockA;
}
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;
}
Before I present the code which is found at the bottom of this post I would like to talk about the issue and the fix's that I do not desire. Okay basically I've created a GUI from scratch sort of and one requirement I wanted for this was allow components to have their own click executions so if i click a button or tab etc.. It would call Component->Execute(); Well normally you would do something like a switch statement of ids and if that components ID equaled n number then it would perform this action. Well that seemed kinda dumb to me and I thought there has to be a better way. I eventually tried to incorporate a feature in JAVA where you would do like Component.AddActionListener(new ActionListener( public void execute(ActionEvent ae) { })); or something like that and I thought that this feature has to be possible in C++. I eventually came across storing void functions into a variable in which could be executed at any time and modified at any time. However I hadn't noticed an issue and that was this only worked with static functions. So below you'll see my problem. I've patched the problem by using a pointer to SomeClass however this would mean having an individual function call for every class type is there no way to store a function callback to a non-static class member without doing the below strategy? and instead doing a strategy like the commented out code?
//Main.cpp
#include <iostream> //system requires this.
#include "SomeClass.h"
void DoSomething1(void)
{
std::cout << "We Called Static DoSomething1\n";
}
void DoSomething2(void)
{
std::cout << "We Called Static DoSomething2\n";
}
int main()
{
void (*function_call2)(SomeClass*);
void (*function_call)() = DoSomething1; //This works No Problems!
function_call(); //Will Call the DoSomething1(void);
function_call = DoSomething2; //This works No Problems!
function_call(); //Will Call the DoSomething2(void);
SomeClass *some = new SomeClass(); //Create a SomeClass pointer;
function_call = SomeClass::DoSomething3; //Static SomeClass::DoSomething3();
function_call(); //Will Call the SomeClass::DoSomething3(void);
//function_call = some->DoSomething4; //Non-Static SomeClass::DoSomething4 gives an error.
//function_call(); //Not used because of error above.
function_call2 = SomeClass::DoSomething5; //Store the SomeClass::DoSomething(SomeClass* some);
function_call2(some); //Call out SomeClass::DoSomething5 which calls on SomeClass::DoSomething4's non static member.
system("pause");
return 0;
}
//SomeClass.hpp
#pragma once
#include <iostream>
class SomeClass
{
public:
SomeClass();
~SomeClass();
public:
static void DoSomething3(void);
void DoSomething4(void);
static void DoSomething5(SomeClass* some);
};
//SomeClass.cpp
#include "SomeClass.h"
SomeClass::SomeClass(void)
{
}
SomeClass::~SomeClass(void)
{
}
void SomeClass::DoSomething3(void)
{
std::cout << "We Called Static DoSomething3\n";
}
void SomeClass::DoSomething4(void)
{
std::cout << "We Called Non-Static DoSomething4\n";
}
void SomeClass::DoSomething5(SomeClass *some)
{
some->DoSomething4();
}
Secondary Fix for what I'll do not an exact answer I wanted but it meets my needs for now along with allowing additional features which would have become overly complicate had this not existed.
//Component.hpp
#pragma once
#include <iostream>
#include <windows.h>
#include <d3dx9.h>
#include <d3d9.h>
#include "Constants.hpp"
#include "ScreenState.hpp"
#include "ComponentType.hpp"
using namespace std;
class Component
{
static void EMPTY(void) { }
static void EMPTY(int i) { }
public:
Component(void)
{
callback = EMPTY;
callback2 = EMPTY;
callback_id = -1;
}
Component* SetFunction(void (*callback)())
{
this->callback = callback;
return this;
}
Component* SetFunction(void (*callback2)(int), int id)
{
this->callback_id = id;
this->callback2 = callback2;
return this;
}
void execute(void)
{
callback();
callback2(callback_id);
}
}
The syntax for pointers-to-member-functions is as follows:
struct Foo
{
void bar(int, int);
void zip(int, int);
};
Foo x;
void (Foo::*p)(int, int) = &Foo::bar; // pointer
(x.*p)(1, 2); // invocation
p = &Foo::zip;
(x.*p)(3, 4); // invocation
Mind the additional parentheses in the function invocation, which is needed to get the correct operator precedence. The member-dereference operator is .* (and there's also ->* from an instance pointer).
I was trying to compile a simple Helloworld program for BADA but through command prompt.After compiling i ma getting the errors as
c:/Helloworld/src/Helloworld.cpp:12: error: prototype for 'Osp::App::Application HelloWorld::CreateInstance()' does not match any in class 'HelloWorld'
C:/Helloworld/inc/HelloWorld.h:21: error: candidate is: static Osp::App::Application* HelloWorld::CreateInstance()
could any body help that what needs to be done with it.
Thanks
Code for Helloworld.h
#ifndef __HELLOWORLD_H__
#define __HELLOWORLD_H__
#include <FBase.h>
#include <FGraphics.h>
#include <FLocales.h>
#include <FSystem.h>
#include <FApp.h>
using namespace Osp::Base;
using namespace Osp::Graphics;
using namespace Osp::Locales;
using namespace Osp::System;
using namespace Osp::App;
class HelloWorld :
public Application // must inherit from Application class
{
public:
// The application must have a factory method that creates an instance of the application.
static Application* CreateInstance(void);
public:
HelloWorld();
~HelloWorld();
public:
// The application must provide its name.
String GetAppName(void) const;
protected:
// The application must provide its ID.
AppId GetAppId(void) const;
AppSecret GetAppSecret(void) const;
public:
// This method is called when the application is initializing.
bool OnAppInitializing(AppRegistry& appRegistry);
// This method is called when the application is terminating.
bool OnAppTerminating(AppRegistry& appRegistry);
// Thie method is called when the application is brought to the foreground
void OnForeground(void);
// This method is called when the application is sent to the background.
void OnBackground(void);
// This method is called when the application has little available memory.
void OnLowMemory(void);
// This method is called when the device's battery level changes.
void OnBatteryLevelChanged(BatteryLevel batteryLevel);
};
#endif
Code for Helloworld.cpp
#include "HelloWorld.h"
HelloWorld::HelloWorld()
{
}
HelloWorld::~HelloWorld()
{
}
Application*
HelloWorld::CreateInstance(void)
{
// You can create the instance through another constructor.
return new HelloWorld();
}
String
HelloWorld::GetAppName(void) const
{
static String appName(L"HelloWorld");
return appName;
}
AppId
HelloWorld::GetAppId(void) const
{
static AppId appId(L"93bt1p123e");
return appId;
}
AppSecret
HelloWorld::GetAppSecret(void) const
{
static AppSecret appSecret(L"9C645DDBA19C71BAD1204DA4DAA7A0B9");
return appSecret;
}
bool
HelloWorld::OnAppInitializing(AppRegistry& appRegistry)
{
// TODO:
// Initialization including UI construction can be done here.
// Load the application's latest data, if necessary.
// If this method is successful, return true; otherwise, return false.
return true;
}
bool
HelloWorld::OnAppTerminating(AppRegistry& appRegistry)
{
// TODO:
// Deallocate or close any resources still alive.
// Save the application's current states, if applicable.
// If this method is successful, return true; otherwise, return false.
return true;
}
void
HelloWorld::OnForeground(void)
{
result r = E_SUCCESS;
Canvas* pCanvas = GetAppFrame()->GetCanvasN();
if(pCanvas == null)
return;
Font* pFont = new Font();
pFont->Construct(FONT_STYLE_PLAIN | FONT_STYLE_BOLD, 50);
pCanvas->SetFont(*pFont);
r = pCanvas->DrawText(Point(30, 30), GetAppName());
if (IsFailed(r))
{
AppLog("pCanvas->DrawText() failed.\n");
delete pCanvas;
return;
}
r = pCanvas->Show();
if (IsFailed(r))
{ AppLog("pCanvas->Show() failed.\n");
delete pCanvas;
return;
}
delete pCanvas;
}
void
HelloWorld::OnBackground(void)
{
}
void
HelloWorld::OnLowMemory(void)
{
// TODO:
// Deallocate as many resources as possible.
}
void
HelloWorld::OnBatteryLevelChanged(BatteryLevel batteryLevel)
{
// TODO:
// It is recommended that the application save its data,
// and terminate itself if the application consumes much battery
}
Code for Helloworldentry.cpp
/**
* OSP Application entry point(OspMain) introduced.
*/
#include <fapp.h>
#include "HelloWorld.h"
using namespace Osp::Base::Collection;
extern "C"
{
__declspec(dllexport) void OspMain(int hInstance, int argc, char *argv[]);
}
/**
* Entry function of OSP Application which is called by the operating system.
*/
extern "C" {
void OspMain(int hInstance, int argc, char *argv[])
{
AppLog("OspMain() Started. \n");
result r = E_SUCCESS;
ArrayList* pArgs = new ArrayList();
pArgs->Construct();
for (int i = 0; i < argc; i++)
{
String* pEachArg = new String(argv[i]);
pArgs->Add(*pEachArg);
}
r = Osp::App::Application::Execute(HelloWorld::CreateInstance, pArgs);
if (IsFailed(r))
{
AppLog("Application execution has failed.\n");
}
if (pArgs)
{
pArgs->RemoveAll(true);
delete pArgs;
}
AppLog("OspMain() Ended. \n");
}
}
The compiler is complaining that you have defined a method with this signature:
Osp::App::Application HelloWorld::CreateInstance()
whereas the HelloWorld class declares that its CreateInstance method has this signature:
Osp::App::Application* HelloWorld::CreateInstance()
Note the difference in the return type. The class definition says that the method with this name returns an Application pointer but you have implemented a method that returns an Application object.
In the future, please post code along with compiler errors. It's rarely possible to adequately explain compiler errors in isolation from the code that produced them. For example, I can't tell you which return type is correct in this case; I can only tell you that the return types don't match (which is exactly what the compiler already said).
In my application I got many instances of class CDbaOciNotifier. They all share a pointer to only one instance of class OCIEnv.
What I like to achieve is that allocation and deallocation of the resource class OCIEnv will be handled automatically inside class CDbaOciNotifier.
The desired behaviour is, with the first instance of class CDbaOciNotifier the environment will be created, after that all following notifiers use that same environment. With the destruction of the last notifier, the environment will be destroyed too (call to custom deleter).
Later on, this cycle can start again with the creation of a new environment.
What I've got so far (using a static factory method to create notifiers):
#pragma once
#include <string>
#include <memory>
#include "boost\noncopyable.hpp"
class CDbaOciNotifier : private boost::noncopyable
{
public:
virtual ~CDbaOciNotifier(void);
static std::auto_ptr<CDbaOciNotifier> createNotifier(const std::string &tnsName, const std::string &user, const std::string &password);
private:
CDbaOciNotifier(OCIEnv* envhp);
// All notifiers share one environment
static OCIEnv* m_ENVHP;
// Custom deleter
static void freeEnvironment(OCIEnv *env);
OCIEnv* m_envhp;
};
CPP:
#include "DbaOciNotifier.h"
using namespace std;
OCIEnv* CDbaOciNotifier::m_ENVHP = 0;
CDbaOciNotifier::~CDbaOciNotifier(void)
{
}
CDbaOciNotifier::CDbaOciNotifier(OCIEnv* envhp)
:m_envhp(envhp)
{
}
void CDbaOciNotifier::freeEnvironment(OCIEnv *env)
{
OCIHandleFree((dvoid *) env, (ub4) OCI_HTYPE_ENV);
*env = null;
}
auto_ptr<CDbaOciNotifier> CDbaOciNotifier::createNotifier(const string &tnsName, const string &user, const string &password)
{
if(!m_ENVHP)
{
OCIEnvCreate( (OCIEnv **) &m_ENVHP, OCI_EVENTS|OCI_OBJECT, (dvoid *)0,
(dvoid * (*)(dvoid *, size_t)) 0,
(dvoid * (*)(dvoid *, dvoid *, size_t))0,
(void (*)(dvoid *, dvoid *)) 0,
(size_t) 0, (dvoid **) 0 );
}
//shared_ptr<OCIEnv> spEnvhp(m_ENVHP, freeEnvironment); ...got so far...
return auto_ptr<CDbaOciNotifier>(new CDbaOciNotifier(m_ENVHP));
}
I'd like to avoid counting references (notifiers) myself, and use something like shared_ptr.
Do you see an easy solution to my problem?
There is a lot going on in your code. Here is the solution, but simplified to just the bare essentials.
class CDbaOciNotifier
{
public:
CDbaOciNotifier() :
m_resource(get_env())
{ }
private:
shared_ptr<OCIEnv> m_env;
struct Delete_env
{
void operator()(OCIEnv* env)
{
OCIHandleFree( ... );
}
};
static shared_ptr<OCIEnv> get_env()
{
// make sure a mutex is involved if CDbaOciNotifier
// can be constructed concurrently.
static weak_ptr<OCIEnv> s_env;
shared_ptr<OCIEnv> env = s_env.lock();
if( ! env )
{
OCIEnv* env_ptr = OCIEnvCreate( ... );
env.reset( env_ptr, Delete_env() );
s_env = env;
}
return env;
}
};
As written you cannot construct CDbaOciNotifier concurrently. You'll need a static mutex to protect s_env if you want that ability.
The weak_ptr needs to be a function local static otherwise you're app might explode if a global or static CDbaOciNotifier is created (static initialization order is undefined).
Does this work for you?
// In .h file
class CDbaOciNotifier
{
// ...
private:
static shared_ptr<OCIEnv> envPtr;
};
// In .cpp file
// Write freeEnvironment as a free function.
shared_ptr<OCIEnv> CDbaOciNotifier::envPtr(new OCIEnv, freeEnvironment);