In C++ project, we have the following dependency:
Package1.so - shared lib/functional having std::shared_ptr<T> getInstance()* static function which returns a static shared pointer. If the static pointer isn't initialized, it initializes then returns the pointer. It also uses static mutex to guarantee sync in case of MT mode call to getInstance
Package2, which depends on Package1.so and uses the getInstance function to fetch data
Package3, has dependency from Package1 and Package2 and also directly uses 'getInstance' function
It's expected that static object should be created only once and be shared between package 2& 3
However, seems the shared object is different for Package2 & 3 -> the object is created twice.
Question: Is static memory space is not shared in case of .so files?
// Package 1
// h1.hpp
#pragma once
class A {
public:
static std::shared_ptr<A> get_instance();
private:
static std::mutex m_mtx;
static std::shared_ptr<A> m_instance;
};
// Package 1
// h1.cpp
static std::mutex A::m_mtx;
static std::shared_ptr<A> A::m_instance;
std::shared_ptr<A> A::get_instance() {
const std::lock_guard<std::mutex> lck{m_mtx);
if (nullptr == m_instance) {
m_instance = std::make_shared<A>();
}
return m_instance;
}
// Package 2 depending Package 1
#include <path/h1.hpp>
void f() {
auto instance = A::get_instance(); // for first call creates new
}
// Package 3 depending Package 1 and Package 2
#include <path/h1.hpp>
void g() {
auto instance = A::get_instance(); // for first call creates new
}
// Result - 2 instances are created of A - one thought Package 3 one through Package 2
// Expected - a single A should be created and static shared ptr should be shared.
This is expected.
The shared pointers themselves are different because they are different objects but they point to the same underlying object. This is by design.
I took your example and reran it, it produces the following.
$ ./main
Instance:0x7fb7001a00a0 Object:0x55ae9c53aec0
Instance:0x7fb70019b0a0 Object:0x55ae9c53aec0
I created a main.cpp that calls f and g
void f();
void g();
int main() {
f();
g();
}
f.cpp is in a different library libf.so. I assign it to a static object otherwise calling f() and g() could print the same address for same objects just by coincidence.
#include "h1.h"
void f() {
static auto instance = A::get_instance(); // for first call creates new
printf( "Instance:%p Object:%p\n", &instance, instance.get() );
}
Same thing for g.cpp
#include "h1.h"
void g() {
static auto instance = A::get_instance(); // for first call creates new
printf( "Instance:%p Object:%p\n", &instance, instance.get() );
}
Notice that on h1.h you are returning a copy of the static variable, not a reference to it. That is okay but it will generate a copy and perhaps that's not what you expect.
#pragma once
#include <memory>
#include <mutex>
class A {
public:
static std::shared_ptr<A> get_instance();
private:
static std::mutex m_mtx;
static std::shared_ptr<A> m_instance;
};
On this file h1.cpp you had the keyword static prefixed to the implementation of the two variables. That is a bug and I commented out. It should not compile. Maybe that is the reason for your problem.
#include "h1.h"
/* static */ std::mutex A::m_mtx;
/* static */ std::shared_ptr<A> A::m_instance;
std::shared_ptr<A> A::get_instance() {
const std::lock_guard<std::mutex> lck{m_mtx};
if (nullptr == m_instance) {
m_instance = std::make_shared<A>();
}
return m_instance;
}
CMakeLists.txt
cmake_minimum_required( VERSION 3.0 )
project(deps)
add_library( h1 SHARED h1.cpp )
add_library( f SHARED f.cpp )
target_link_libraries( f h1 )
add_library( g SHARED g.cpp )
target_link_libraries( g h1 )
add_executable( main main.cpp )
target_link_libraries( main h1 f g )
Related
Some of Qt classes require that QApplication must be constructed first. So, I wrote some code for calling singleton constructor in an appropriate place. Here is a "pure" C++ code for this.
#include <functional>
#include <iostream>
#include <list>
#define INITIALIZABLE(NAME) \
private: \
template <typename T> \
friend struct Initializer; \
inline static NAME* m_instance = nullptr; \
static void create() { m_instance = new NAME; } \
inline static Initializer<NAME> m_initializer{};
struct InitQueue {
static std::list<std::function<void(void)>>& getList()
{
static std::list<std::function<void(void)>> s_list;
return s_list;
}
};
template <class T>
struct Initializer {
Initializer()
{
auto initializer = []() {
T::create();
};
InitQueue::getList().push_back(initializer);
}
};
void initialize()
{
for (auto func : InitQueue::getList()) {
func();
}
}
class Foo {
INITIALIZABLE(Foo)
public:
Foo()
{
m_count++;
std::cout << "Ctor was called: " << m_count << "\n";
}
private:
static inline int m_count = 0;
};
int main(int, char**)
{
initialize();
return 0;
}
It worked as I expected. BUT then I make a Foo Inherited from QObject, somehow ctor of Foo is calling twice.
Foo.cpp
#include "Foo.hpp"
Foo::Foo(QObject* parent)
: QObject(parent)
{
m_count++;
std::cout << "Ctor was called: " << m_count << "\n";
}
std::list<std::function<void(void)>>& InitQueue::getList()
{
static std::list<std::function<void(void)>> s_list;
return s_list;
}
void initialize()
{
for (auto func : InitQueue::getList()) {
func();
}
}
Foo.hpp
#pragma once
#include <functional>
#include <iostream>
#include <list>
#include <qobject.h>
#define INITIALIZABLE(NAME) \
private: \
template <typename T> \
friend struct Initializer; \
inline static NAME* m_instance = nullptr; \
static void create() { m_instance = new NAME; } \
inline static Initializer<NAME> m_initializer{};
struct InitQueue {
static std::list<std::function<void(void)>>& getList();
};
template <class T>
struct Initializer {
Initializer()
{
auto initializer = []() {
T::create();
};
InitQueue::getList().push_back(initializer);
}
};
void initialize();
class Foo : public QObject {
INITIALIZABLE(Foo)
public:
Foo(QObject* parent = nullptr);
private:
static inline int m_count = 0;
};
main.cpp
#include "Foo.hpp"
int main(int, char**)
{
initialize();
std::cin.get();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(DoubleCtorCall)
set(CMAKE_CXX_STANDARD 17)
#Qt specific
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
#Qt
find_package(Qt5Core CONFIG REQUIRED)
set(SOURCES
Main.cpp Foo.cpp Foo.hpp)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} Qt5::Core)
So. I have two questions: Why does this happened and how to avoid it (except once flags)?
Windows 10. MSVC 2017. Qt 5.12.
When you initialize static member variables in the header file, it is going to be initialized in each translation unit where the header is included.
In your case, m_initializer was initialized multiple times because Qt moc system generated some underlying files that include your "foo.h". Your InitQueue will contain multiple initializers that result in multiple calls to Foo Ctor.
Separating the variables defined in only one translation unit will help. For example :
#define ININIALIZEABLE(NAME) \
private: \
template <typename T> \
friend struct Initializer; \
static NAME* m_instance ; \
static void create() { m_instance = new NAME; } \
static Initializer<NAME> m_initializer;
#define IMPL_INITIALIZEABLE(NAME) \
NAME* NAME::m_instance = nullptr;\
Initializer<NAME> NAME::m_initializer{};
Then use the macro in your foo.cpp :
IMPL_INITIALIZEABLE(Foo)
Foo::Foo(QObject* parent)
: QObject(parent)
{
m_count++;
std::cout << "Ctor was called: " << m_count << "\n";
}
I can not answer the question directly. I can only be so bold to advise, that is not a "pure C++" code. I am using the following whenever I need "make it once and only once" standard C++, coding idiom:
// header only
// never anonymous namespace
namespace my_space
{
inline single_thing_type const & make_once
( /* if required arguments for initializaton go here */ )
{
auto initor_ = [&](){
/* this is where it is made */
static single_thing_type single_instance ;
/*
or calling some non-default ctor, or some factory
and doing whatever is required by single_thing_type
to be made and initialised
*/
return single_instance ;
};
/* this is resilient in presence of multiple threads */
static single_thing_type const & singleton_ = initor_() ;
/*
return by ref. so type can be
non-movable and non-copyable
if required
*/
return singleton_ ;
}
// here we can provide a process-wide global if required
inline single_thing_type const &
single_global_made_once = make_once () ;
} // my_space
There are many variations but this is the core of the idiom. I am sure it can be applied in the context of standard C++, using the Qt.
Not a Qt code bellow, is using the simplified still correct version of your class Foo from above:
namespace my_space
{
struct Foo {
Foo()
{
std::cout << "Foo() ctor called.\n";
}
}; // Foo
inline Foo const & make_once ( )
{
auto initor_ = [&](){
static Foo single_instance ;
return single_instance ;
};
static Foo const & singleton_ = initor_() ;
return singleton_ ;
}
inline Foo const &
single_global_foo_made_once = make_once () ;
} // my_space
int main ()
{
using namespace my_space;
auto const & it_is_already_made
= single_global_foo_made_once ;
}
I am not claiming I have invented this. For a good overview and details see here.
This idiom does not require the type handled, to be changed in any way. Perhaps you can try it on Qt types you need.
What is not in the so-called "Scot Meyers Singleton" is a use of the lambda ('initor' above). An good use case is creating a single instance of some event_log.
inline event_log const & const & make_event_log_once ( )
{
auto initor_ = [&](){
auto event_log_file_name
= read_it_from_environemnt_confif_or_whatever() ;
auto event_log_file_path
= ensure_platform_and_folder (event_log_file_name ) ;
return event_log( event_log_file_path ) ;
};
static event_log singleton_{ initor_() } ;
return singleton_ ;
}
inline event_log const &
event_log_instance = make_event_log_once () ;
Before we create the event log class instance we need to obtain the file name somehow and from somewhere. Then we need to make a full path form it, making sure platform is right and folder on that platform is assured. Just then we can make the instance of the event_log. This is what we do in the initor lambda, knowing all of that will be called only once.
Enjoy the standard C++
I have this setup:
ClassA.h - belongs to libA.dll
class ClassA
{
public:
static std::vector<std::string> getStrings();
~ClassA();
static void addToVector(std::string s);
static void setAddAllowed(bool addAllowed);
private:
ClassA();
};
extern "C"
{
extern void addToStrings(std::string s);
}
ClassA.cpp
#include<ClassA.h>
#include<vector>
static std::vector<std::string> strings;
static bool addAllowed = false;
std::vector<std::string> ClassA::getStrings()
{
return strings;
}
void ClassA::addToVector(std::string s)
{
if (addAllowed)
{
strings.push_back(s);
}
}
void ClassA::setAddAllowed(bool add)
{
addAllowed = add;
}
extern void addToStrings(std::string s)
{
ClassA::addToVector(s);
}
ClassB.h - belongs to libB.dll
class ClassB
{
public:
static void addString(std::string s);
};
ClassB.cpp
#include<ClassB.h>
#include<ClassA.h> //not present if libA is dynamically loaded.
void ClassB::addString(std::string s)
{
addToStrings( s ); // problem if addToStrings is called with a function pointer obtained from dynamic loading of libA.dll - libB doesn't depend on libA actually.
}
ClassC.h
class ClassC
{
public:
static void setAddAllowed(bool add);
};
ClassC.cpp
#include<ClassA.h>
#include<ClassC.h>
void ClassC::setAddAllowed(bool addAllowed)
{
ClassA::setAddAllowed(addAllowed);
}
Main.cpp
int main()
{
ClassB::addString("example1");
ClassC::setAddAllowed( true );
ClassB::addString("example2");
ClassB::addString("example3");
std::vector<std::string> v = ClassA::getStrings();
}
with this setup, every thing works fine, I could get the strings in ClassA::getStrings(). But the problem appears - the vector is empty, if I add the strings in ClassB using function pointer obtained from dynamically loaded dll of libA. I get the symbol of extern function addToStrings as function pointer and make a call. In that case, the vector v is empty.
Example code for loading libA and calling the function:
typedef void (*addToStringsFP) (std::string s);
addToStringsFP fp = NULL;
Handle libHandle = NULL;
libHandle = LoadImage("libA",0);
fp = (addToStringsFP)FindSymbol(libHandle,"addToStrings");
This is only to demonstrate how I am loading libA and calling the function pointer. LoadImage, FindSymbol are similar to dlopen(), dlsym() - I can't post actual names.
What am I misunderstanding here ?
I found the answer.
The LoadImage function that I am using is loading a new image into the process instead of looking at already loaded dlls first and then load.
I had to use a different version of the LoadImage which loads the image that is currently in the process- basically a shared image. That solved the problem.
I thought it might be useful to someone who may have some problem albeit not much probable I think.
I have lazy singleton class, that needs to be serialized using boost on the first call.
Header file:
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton theSingleInstance;
return theSingleInstance;
}
void load();
private:
Singleton();
Singleton(const Singleton& root);
Singleton& operator=(const Singleton&);
std::map<std::string,std::string > m_desc;
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& arc, const unsigned int version)
{
arc & BOOST_SERIALIZATION_NVP(m_desc);
}
const char* FILENAME = "./config.xml";
};
Source file
#include "singleton.h"
Singleton::Singleton()
{
load();
}
void Singleton::load()
{
try
{
std::ifstream f(FILENAME);
boost::archive::xml_iarchive arc(f);
arc & boost::serialization::make_nvp("Config",Instance());
}
catch(...)
{
std::cout << "Exception" << std::endl;
}
}
So when I try to start my code using this singleton, it hangs. With the debugger I can see that it does not go to load() method many times (and it's ok). When I pause the debugger, it stops on the line return theSingleInstance;, but it does not go through the breakpoint on this line many times also. What am I doing wrong?
You call load from inside the constructor. Which means that you're in the constructor of your static theSingleInstance when you... call Instance
:
#0 in Singleton::load at test.cpp <test.cpp>
#1 in Singleton::Singleton at test.cpp <test.cpp>
#2 in Singleton::Instance at test.cpp <test.cpp>
#3 in main at test.cpp <test.cpp>
Since construction c++11 function-local statics is guaranteed to be thread safe, which means - in your implementation - that execution will block until the instance is fully constructed (or construction failed, so it can be retried).
Of course, that will never happen because construction is waiting for itself.
0x00000000004030f5 <+629>: callq 0x402be0 <__cxa_guard_acquire#plt>
=> 0x00000000004030fa <+634>: test %eax,%eax
0x00000000004030fc <+636>: je 0x402ff1 <Singleton::load()+369>
Of course this is fixed by not using the external accessor while still constructing the instance, as you already found out.
You might want to consider the practice of giving singletons value-semantics - in case one day you don't want it to be a singleton any more and you want to avoid refactoring.
The other advantage is that it provides full encapsulation:
for example:
// config.h - extremely sparse interface
#include <string>
struct config
{
/// Return a named value from the program's configuration
/// #param name is the name of the required parameter
/// #post the config file shall be cached
/// #return the value if it exists
/// #except std::invalid_argument if the value does not exist
/// #except other exceptions if the config file does not load
///
const std::string& value(const std::string& name) const;
private:
struct impl;
static impl& get();
};
// config.cpp - the implementation
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/map.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <fstream>
#include <map>
struct config::impl
{
impl()
{
std::ifstream f(FILENAME);
boost::archive::xml_iarchive arc(f);
arc & boost::serialization::make_nvp("Config", *this);
}
std::map<std::string,std::string > m_desc;
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& arc, const unsigned int version)
{
arc & BOOST_SERIALIZATION_NVP(m_desc);
}
static constexpr const char* FILENAME = "./config.xml";
};
config::impl& config::get() {
static impl _;
return _;
}
const std::string& config::value(const std::string& name) const
{
return get().m_desc.at(name);
}
// demo.cpp
// note - config is copyable, and this has almost no cost
void do_something_with(config cfg)
{
}
struct no_config {}; // some other config source?
// now I can switch on config type at compile time - code can be
// more generic
void do_something_with(no_config)
{
}
int main()
{
// single-use
std::string my_value = config().value("foo");
// pass my config source to a function
do_something_with(config());
// a similar function that uses a different config source
do_something_with(no_config());
return 0;
}
The answer was simple: to replace this line
arc & boost::serialization::make_nvp("Config",Instance());
with
arc & boost::serialization::make_nvp("Config",*this);
In the below programme i use one boolean variable named check , which is being accessed inside main function by two objects of Tst1 and Test2 . But the value of check variable is not maintained in the programme . we can use static but i want to know some alternative way ..could anyone give me some hints on it ?
Thanks in advance .
Inside jointdeatils.h
#pragma once
class Jointdetails
{
public:
Jointdetails(void);
~Jointdetails(void);
bool check;
};
Inside jointdeatils.cpp
#include "Jointdetails.h"
Jointdetails::Jointdetails(void)
{
check = false ;
}
Jointdetails::~Jointdetails(void)
{
}
Inside analyzer.h
#pragma once
#include "Jointdetails.h"
class Analyzer
{
public:
Analyzer(void);
Jointdetails* GetJointDetails();
Jointdetails* m_ptheCTJointDetails;
~Analyzer(void);
};
Inside analyzer.cpp
#include "Analyzer.h"
#include "stddef.h"
Analyzer::Analyzer(void)
{
m_ptheCTJointDetails = new Jointdetails();
}
Analyzer::~Analyzer(void)
{
}
Jointdetails* Analyzer::GetJointDetails()
{
if(m_ptheCTJointDetails)
return m_ptheCTJointDetails;
else
return NULL;
}
Inside Test1.h
#pragma once
#include "Analyzer.h"
class Tst1
{
public:
Tst1(void);
Analyzer *analyzer1 ;
public:
~Tst1(void);
};
Inside Test1.cpp
#include "Tst1.h"
Tst1::Tst1(void)
{
analyzer1 = new Analyzer ;
}
Tst1::~Tst1(void)
{
}
Inside Test2.h
#pragma once
#include "Analyzer.h"
class Test2
{
public:
Test2(void);
Analyzer *analyzer2 ;
public:
~Test2(void);
};
Inside Test2.cpp
#include "Test2.h"
Test2::Test2(void)
{
analyzer2 = new Analyzer ;
}
Test2::~Test2(void)
{
}
Inside main.cpp
#include "Test2.h"
#include "Tst1.h"
#include "stdio.h"
int main()
{
Tst1 *test1 = new Tst1 ; //check = false
Test2 *test2 = new Test2 ; //check = false
test1->analyzer1->GetJointDetails()->check = true ;
if(test2->analyzer2->GetJointDetails()->check )
printf("Check value is changed");
else
printf("Check value is not changed");
return 0 ;
}
There are only two possible ways to do so:
Use static storage data
Pass automatic or dynamic storage data as parameters to destination functions/ctors
Way #1 is more handy as you can access such data directly from any function. But it shall be considered as bad design because it almost the same thing as evil global variables.
Way #2 is more correct (see answer by justin for example) but could be a bit irritating - you will need to pass required data as parameter to each required function and/or store data as class data member. Not a pleasant work in case of many classes/nested calls.
Nevertheless, if you don't care on drawbacks of way #1 consider singleton-on-demand concept. It allows to use static data in more dynamic way - create on demand, share access by several users and destroy when nobody use it anymore. See example (several details, includes etc skipped for brevity):
JointDetails.h
class JointDetails
{
// Actual class definition
// ...
public:
// Function accessing to JointDetails instance
static std::shared_ptr<JointDetails> Get();
};
JointDetails.cpp
std::shared_ptr<JointDetails> JointDetails::Get()
{
static std::weak_ptr<JointDetails> s_trackInstance;
if(s_trackInstance.expired())
{
auto instance = std::make_shared<JointDetails>();
s_trackInstance = instance;
return instance;
}
return s_trackInstance.lock();
}
Analyzer.h
// All instances of Analyzer share the same instance of JointDetails.
// But JointDetails instance is created dynamically only when first instance of
// Analyzer is created and destoyed when the last instance of Analyzer is destroyed.
class Analyzer
{
std::shared_ptr<JointDetails> m_details;
public:
Analyzer(): m_details(JointDetails::Get()) {}
const JointDetails& GetDetails() const { return *m_details; }
};
You're either going to have to make check static, or JointDetails a singleton (which also uses the static keyword).
If you make check static, you are saying that all instances of JointDetails have the same check.
If you make JointDetails a singleton, then you're saying that every reference to a JointDetails object is the same object, so your Tst1 and your Test2 will both have a pointer to the same object.
I think the latter is what you're looking for:
Jointdetails.h
#pragma once
class Jointdetails
{
public:
~Jointdetails(void);
bool check;
static Jointdetails* getInstance();
private:
Jointdetails(void);
};
Jointdetails.cpp
#include "Jointdetails.h"
Jointdetails::Jointdetails(void)
{
check = false ;
}
Jointdetails::~Jointdetails(void)
{
}
Jointdetails* Jointdetails::getInstance() {
static Jointdetails s_instance;
return &s_instance;
}
Analyzer.cpp
Analyzer::Analyzer(void)
{
m_ptheCTJointDetails = Jointdetails::getInstance();
}
0) you needn't use new so often.
1) you can construct your objects with the joint details as a parameter in your constructor.
To illustrate:
class Tst1 {
public:
Tst1(Jointdetails& pJointdetails) : analyzer1(pJointdetails) {
}
Analyzer analyzer1;
public:
~Tst1(void);
};
int RunProgram(Jointdetails& pJointdetails) {
Tst1(pJointdetails);
...
}
int main() {
Jointdetails jointdetails;
const int result(RunProgram(jointdetails));
return result;
}
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);