C++ smart pointers confusion - c++

I understand that in the C++ realm it is advocated to use smart pointers. I have a simple program as below.
/* main.cpp */
#include <iostream>
#include <memory>
using namespace std;
/* SQLite */
#include "sqlite3.h"
int main(int argc, char** argv)
{
// unique_ptr<sqlite3> db = nullptr; // Got error with this
shared_ptr<sqlite3> db = nullptr;
cout << "Database" << endl;
return 0;
}
When I compile with unique_ptr line got an error message:
error C2027: use of undefined type 'sqlite3'
error C2338: can't delete an incomplete type
When I compile with shared_ptr line it is successful. From several questions and answers my understanding is that unique_ptr should be preferred as I do not intended to have objects sharing resources. What is the best solution in this case? Use shared_ptr or go back to the old approach of bare pointers (new/delete)?

The general approach is in #SomeProgrammerDudes's answer (accept it). But to address your concerns I'm posting this.
You shouldn't go back to raw new and delete. Neither because sqlite3 is an opaque type nor because the overhead of std::shared_ptr. You use, as the other answer specified, a std::unique_tr.
The only difference is how you setup the custom deleter. For std::unique_ptr it's part of the type definition, not a run-time parameter. So you need to do something like this:
struct sqlite3_deleter {
void operator()(sqlite3* sql) {
sqlite3_close_v2(sql);
}
};
using unique_sqlite3 = std::unique_ptr<sqlite3, sqlite3_deleter>;

sqlite3 is an opaque structure (much like FILE from C). All you have is its declaration, not its definition. That means you can't use it in a std::unique_ptr directly without a custom deleter.

#include <memory>
#include <stdexcept>
/* sqlite 3 interface */
struct sqlite3 {};
extern void sqlite3_close(sqlite3*);
extern int sqlite3_open(sqlite3**);
/* our boilerplate */
struct closer
{
void operator()(sqlite3* p) const
{
sqlite3_close(p);
}
};
using sqlite3_ptr = std::unique_ptr<sqlite3, closer>;
/* handy maker function */
sqlite3_ptr make_sqlite()
{
sqlite3* buffer = nullptr;
int err = sqlite3_open(&buffer);
if (err) {
throw std::runtime_error("failed to open sqlite");
}
return sqlite3_ptr(buffer);
}
int main()
{
auto mysqlite = make_sqlite();
}

Solution with shared_ptr
I'm learning C++ and SQLite, so I had this question too. After reading this post, I tried some answers from it. The result is a working example and a small analysis.
First create a custom deleter for the smart pointer.
Then, create an empty share_ptr including the custom deleter
Then, create an empty pointer for the DB handler (sqlite3 * DB;)
Afterwards, open/create the DB.
Link the raw pointer to the shared one.
After the shared_ptr goes out of scope, it will delete the raw pointer too.
This is rather inefficient (see conclusion), but is the only way I manged to use smart pointers with sqlite3, so I decided to post this as an answer.
#include <iostream>
#include<sqlite3.h>
#include<memory>
//Custom deleter
auto del_sqlite3 = [](sqlite3* pSqlite)
{
std::cout << "Calling custom deleter." << std::endl;
sqlite3_close_v2(pSqlite);
};
int main()
{
//Uncomment to run
//const char* dir = "C:\\test\\db_dir\\test.db"
openOrCreateDB(dir);
return 0;
}
int openOrCreateDB(const char* dirName)
{
std::shared_ptr<sqlite3> DB(nullptr, del_sqlite3);//custom deleter
auto pDB = DB.get();
{
int exit = sqlite3_open(dirName, &pDB);
DB.reset(pDB);// Replace nullptr with pDB and link
}
return 0;
}
Why smart pointers with sqlite3?
The main reason to use a smart pointer is to automate memory management and avoid memory leaks. So, this happens if we are thinking in allocating memory on the free store, using new and delete.
But I failed with all my attempts to allocate a database handler in the free store.
Fail 1: using sqlite3* DB = new sqlite3;
int openOrCreateDB(const char* dirName)
{
sqlite3* DB = new sqlite3;//E0070: Incomplete type not allowed
int exit = sqlite3_open(dirName, &DB);
sqlite3_close(DB);
return 0;
}
Fail 2: using share_ptr
static int openOrCreateDB(const char* dirName)
{
std::shared_ptr<sqlite3> DB(new sqlite3, del_sqlite3);// Incomplete type not allowed
auto pDB = DB.get();
{
int exit = sqlite3_open(dirName, &pDB);
DB.reset(pDB);
}
return 0;
}
Fail 3: using make_shared
I didn't even try. In Meyers' Effective Modern C++, Item 21 it is clear that you can't use make_shared to construct a smart pointer on the heap with the custom deleter.
Conclusion
Maybe I'm doing something wrong, but it seems that SQLite does not like to allocate database handlers (sqlite3 objects) on the heap. So why use a smart pointer anyway? Even if you allocate the db handler on the stack, smart pointers uses more memory and more lines of code.
The other reason to use smart pointers is to manage ownership. But, in sqlite3, the workflow is quite repetitive: In a routine:
Create a DB handler.
Open DB, execute SQL statements, etc.
Finalize statement
Finalize DB connection.
So I can't see why should we pass arround a DB handler outside this workflow.
My recommendation is to keep using raw pointers and destroying them with sqlite3_close(sqlite3 * ptr).

Related

Creating cv::Ptr cause an exception at the end of the application

I am working with openCV arUco markers to detect some markers.
So I generated pre-defined dictionary and saved it in a file.
However the aruco::detectMarkers can only get Ptr<aruco::Dictionary>. So I create a Ptr<aruco::Dictionary> object and sent to the constructor the address of the object itself.
This is causing to exception at the end of the application.
How can I solve it?
Here is my (simplified) code:
#include <opencv2/highgui.hpp>
#include <opencv2/aruco/charuco.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
aruco::Dictionary ReadDictionaryFromFile(std::string fileName)
{
cv::FileStorage fileStorage(fileName, cv::FileStorage::READ);
Mat bytesList;
int markerSize;
int maxCorrectionBits;
fileStorage\["markerSize"\] >> markerSize;
fileStorage\["maxCorrectionBits"\] >> maxCorrectionBits;
fileStorage\["bytesList"\] >> bytesList;
fileStorage.release();
aruco::Dictionary dictionary = cv::aruco::Dictionary(bytesList, markerSize, maxCorrectionBits);
return dictionary;
}
int main(int argc, char *argv\[\])
{
//Detector parameters:
Ptr<aruco::DetectorParameters> detectorParams = aruco::DetectorParameters::create();
//This works but I am afraid will generate another dictionary on another machine
//Ptr<aruco::Dictionary> dictionary = aruco::generateCustomDictionary(1, 4);
//This works but generate exception when app is terminated
aruco::Dictionary dictionaryTemp = ReadDictionaryFromFile("Dictionary.yml");
Ptr<aruco::Dictionary> dictionary = cv::Ptr<aruco::Dictionary>(&dictionaryTemp);
while (true)
{
if (camera.grab(image) != SUCCESS)
{
cout << "error on grab()" << std::endl;
return 0;
}
vector< int > ids;
vector< vector< Point2f > > corners, rejected;
vector< Vec3d > rvecs, tvecs;
// detect markers and estimate pose
aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected);
aruco::drawDetectedMarkers(image, corners, ids);
imshow("out", image);
char key = (char)waitKey(1);
if (key == 'q')
{
break;
}
}
return 0;
}
Here is the exception:
According to cv::Ptr documentation:
Template class for smart pointers with shared ownership.
A Ptr pretends to be a pointer to an object of type T. Unlike an ordinary pointer, however, the object will be automatically cleaned up once all Ptr instances pointing to it are destroyed.
You are creating an object on the stack, and then you are using it to construct the smart pointer. The smart pointer is not owning the object at all.
aruco::Dictionary dictionaryTemp = ReadDictionaryFromFile("Dictionary.yml");
Ptr<aruco::Dictionary> dictionary = cv::Ptr<aruco::Dictionary>(&dictionaryTemp);
At the end of the scope, which in your case it is the end of the main, dictionaryTemp will go out of scope and destroy the object.
After that, the smart pointer dictionary will go out of scope as well, but now the pointer is dangling and almost anything can happen.
Modify your code to give true ownership to the smart pointer:
Ptr<aruco::Dictionary> ReadDictionaryFromFile(std::string fileName)
{
...
return cv::Ptr<aruco::Dictionary>(
new cv::aruco::Dictionary(bytesList, markerSize, maxCorrectionBits));
}
For consideration related with exception safety, in modern, c++ writing the keyword new is rare and the best practise dictates to use a wrapper function called std::make_shared (which is working with the std::shared_ptr, there are similar function for others smart pointer). I am not familiar with cv::Ptr and I wasn't able to find an equivalent for it. But it worth to be aware of it.
Get familiar with RAII https://en.cppreference.com/w/cpp/language/raii and exception safety https://www.stroustrup.com/except.pdf
Also, you may want to familiarize with different types of smart pointers Smart pointers: who owns the object?

How to model a vector of non-owning raw pointers extracted from unique_ptrs?

Note: Apologies if the title is unclear, I don't quite know how to express the issue in proper terms (improvement suggestions are very welcome).
Code, onlinegdb example of the working version and example of the non-working one first to simplify the explanation:
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int v = 0;
};
void some_library_function(const std::vector<A*>& objects)
{
// do something to each object without taking ownership
for(auto p : objects)
{
p->v = 42;
}
}
class B
{
public:
std::vector<std::shared_ptr<A>> m_objects; // this is a private field in my actual code
B():m_objects{std::make_shared<A>()}{};
void use_library()
{
std::vector<A*> observer_vector(m_objects.size());
for(int i=0; i<m_objects.size(); i++)
{
observer_vector[i] = m_objects[i].get(); // fails here if I use unique_ptr
}
some_library_function(observer_vector);
}
};
int main()
{
B b;
b.use_library();
std::cout << b.m_objects[0]->v;
return 0;
}
I have a library function that operates on a series of objects of class A passed in via std::vector<A*>. These objects are stored in a field of class B that owns the objects. I would like to model the "owns" part via a vector of std::vector<unique_ptr<A>>, but this makes it impossible to pass the objects down to the library function.
using shared_ptrs works, but I'm worried this is not as expressive as the unique_ptrs with regards to object ownership.
Is there a way to use unique_ptrs in the vector and still be able to use the library function?
You're already doing the right thing. A raw pointer is a perfectly reasonable way to model something with unknown or lacking ownership. You're not storing the vector anywhere, so there is no confusion and no risk.
The only problem here really is that you've had to regenerate the entire vector, which seems like a bit of a waste. Ultimately, if you're set on a vector<unique_ptr<A>> at the source, and you're stuck with vector<A*> at the destination, then there's nothing you can do about that. If the vector is small it doesn't really matter though.
observer_vector[i] = m_objects[i].get(); // fails if with unique_ptr because of operator= being deleted
No, that should be valid. You're just assigning a raw pointer.

Unhandled Exception: System.AccessViolationException : attempted to read or write protected

Problem: Getting an error when running my .exe
An unhandled exception of type 'System.AccessViolationException'
occurred in AddingWrapper.dll
Additional information: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
In the console it writes this:
Unhandled Exception: System.AccessViolationException : attempted to
read or write protected memory. This is often an indication that other
memory is corrupt. at gcroot (Add ^).. P$AAVAdd##(gcroot(Add^)) at
AddingWrapper.Adding(AddingWrapper, Int32* x, Int32* y)
Code snippet:
VB code:
Public Class Add
Public Function Adding(ByVal x As Double, ByVal y As Double) As Integer
Return x + y
End Function
End Class
AddingWrapper.h:
#pragma once
#include "stdafx.h"
class AddingWrapperPrivate;
class __declspec(dllexport) AddingWrapper {
private: AddingWrapperPrivate* _private;
public: AddingWrapper();
int Adding(int* x, int* y);
~AddingWrapper();
};
AddingWrapper.cpp
#include "stdafx.h"
#include "AddingWrapper.h"
#using "Class1.dll"
#include <msclr\auto_gcroot.h>
using namespace System::Runtime::InteropServices;
class AddingWrapperPrivate {
public: msclr::auto_gcroot<Add^> add;
};
AddingWrapper::AddingWrapper()
{
_private = new AddingWrapperPrivate();
_private->add = gcnew Add();
};
int AddingWrapper:: Adding(int* x, int* y) {
return _private->add->Adding(*x, *y);
};
AddingWrapper::~AddingWrapper()
{
delete _private;
};
calling code:
#include "stdafx.h"
#include "AddingWrapper.h"
#include <iostream>
int main()
{
int *a = 0;
int *b = 0;
AddingWrapper *add;
int results = add->Adding(a,b);
std::cout << "here is the result";
std::cout << results;
return 0;
}
Could it be due to my Class1.dll in AddingWrapper.cpp is using VB.net? Or it's a question of other issues? All the other threads seem to all differ in answer (i.e one is suggesting the user account doesn't have all the rights to the computer). If ever I missed on of those thread, please link it to me, this error is killing me
I should also add this error is at run time not compile time.
In the main function, you are using a "null" object pointer and passing in NULL pointers - that will cause the error you are seeing.
int main()
{
int a = 1;
// ^^^ remove the pointer (and give it a "interesting" value)
int b = 2;
// ^^^ remove the pointer
AddingWrapper add; // remove the pointer (or allocate with new)
// ^^^ remove the pointer
int results = add.Adding(&a, &b); // pass in the address of the integers
// ^^^ syntax change
std::cout << "here is the result";
std::cout << results;
return 0;
}
The variable a, b and add where only pointers, pointing to nothing; this causes access violations. Changing them to be automatic objects ("on the stack") will fix this. If dynamic objects are needed, you can new them (and delete them afterwards); but favour library utilities such as std::shared_ptr and std::unique_ptr etc. to help manage the lifetime of the object.
Several things:
You haven't shown your VB code. Since you've written an unmanaged class, not a managed one, it seems likely that either the import is not correct, or that you're passing a bad pointer.
Why are you passing an int* to the wrapper, only to dereference it right there? Why not pass an int?
You're in C++/CLI, why are you not writing a managed class? You wouldn't need auto_gcroot, and you don't need to deal with DLL imports/exports: VB.Net would be able to see your class the same as it can see any .Net class, and reference it just as easily as you can reference any .Net library.
Edit
OK, it wasn't obvious that you were trying to call some VB.Net code from C++. I thought you were trying to go the other direction.
The problem is almost certainly that you're passing a bad pointer to AddingWrapper::Adding.
You don't need to pass a pointer for basic data types, so you can get rid of that entire thing if you want. The fact that it's a double in VB but an int in C++ is fine, C++/CLI knows that the VB code takes a double and will convert appropriately.
Also, note that you're not passing a pointer between managed and unmanaged code. You're passing a pointer from one unmanaged class to another unmanaged class (whatever calls AddWrapper, to AddWrapper), but across the managed/unmanaged border, you're passing a plain old int.

Returning boost::interprocess memory-mapped file from function?

How can I put this code in to a function so that I pass a file path and it returns the file-mapped bytes in to a char array? Whenever I have tried I can never read the contents once the function finishes?
using boost::interprocess;
using boost;
boost::shared_ptr<char> getBytes(const char* FilePath){
shared_ptr<file_mapping> fm(new file_mapping(FilePath, read_only));
shared_ptr<mapped_region> region(new mapped_region(*fm, read_only));
shared_ptr<char> bytes(static_cast<char*>(region->get_address()));
return bytes;
}
You probably need to go about your objective quite differently! Clearly you don't want to just delete the pointer to the memory mapped array which is what the boost::shared_ptr<char> initialized with the pointer to the base address would do. In fact, you probably should not release that pointer at all.
The other two objects you create will go out of scope when getBytes() is exited but these are the objects which actually cling to the shared memory. What you might want to do is to put the file_mapping and the mapped_region together into an object which is put into the returned boost::shared_ptr<char> as a deleter object: this way these two objects would live long enough to keep the pointed to array alive. Upon the deleter function being called the two objects would be released. Here is how this would roughly look like although I haven't checked whether these are indeed the correct interfaces:
struct shared_memory_deleter
{
shared_memory_deleter(char const* file)
: d_fm(new file_mapping(file, read_only))
, d_region(new mapped_region(*fm, read_only))
{
}
void operator()(char*) {
this->d_region.reset(0);
this->d_fm.reset(0);
}
shared_ptr<file_mapping> d_fm;
shared_ptr<mapped_region> d_region);
};
boost::shared_ptr<char> getBytes(const char* FilePath){
shared_memory_deleter deleter(FilePath);
shared_ptr<char> bytes(deleter.d_region->get_address(), deleter);
return bytes;
}
I'm not sure if this would work but it has, at least, a chance. It is probably not a good idea to do something like this. Instead, you are probably much better off wrapping the logic into an object and not using any shared pointers at all:
class shared_memory
{
public:
shared_memory(char const* name)
: d_file(name, read_only)
, d_region(d_file, read_only)
{
}
char const* buffer() const { return d_region.get_address(); }
};

Sharing class pointers between processes

I have a server library which my client executable injects into a remote process.
It is the server's responsibility to set up some sort of IPC/RPC implementation to allow the client to seamlessly communicate with the remote process.
Update
Take a look at the following server-side header:
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
using namespace boost::interprocess;
typedef allocator<int, managed_shared_memory::segment_manager> ShmIntAllocator;
typedef vector<int, ShmIntAllocator> IntVector;
class A
{
public:
A();
A(string str, offset_ptr<IntVector> ints)
: m_str(string(str)), m_ints(ints) {};
~A();
string m_str;
offset_ptr<IntVector> m_ints;
};
class B
{
public:
B();
B(offset_ptr<A> obj) : m_obj(obj);
~B();
VOID DoSomethingUseful()
{
MessageBoxA(NULL, m_obj->m_str.c_str(), "SomethingUseful", MB_ICONINFORMATION);
}
offset_ptr<A> m_obj;
};
And here's the server-side implementation:
managed_shared_memory g_shm;
offset_ptr<A> g_objA = nullptr;
PVOID g_hMem = nullptr;
BOOL StartServer()
// Create a shared memory pool
try {
g_shm = managed_shared_memory(create_only, "MySharedMem", OVR_MAPSIZE);
} catch(interprocess_exception& e) {
std::string msg(e.what());
MessageBoxA(NULL, msg.c_str(), "Error", MB_ICONERROR);
return FALSE;
}
// Construct a local class instance
const ShmIntAllocator alloc_intVector (g_shm.get_segment_manager());
offset_ptr<IntVector> ints = g_shm.construct<IntVector>(unique_instance)(alloc_intVector);
ints->push_back(10);
ints->push_back(20);
ints->push_back(30);
g_objA = new A("Testing", ints);
B objB(g_objA);
// Copy data into shared memory
size_t len = sizeof(objB); // <-- Doesn't seem to make a difference if I set this to be something higher
g_hMem = g_shm.allocate(len);
std::memcpy(g_hMem, &objB, len);
return TRUE;
}
VOID StopServer()
{
// Free used resources
if(g_objA) {
delete g_objA;
g_objA = nullptr;
}
try{
g_shm.destroy<B>(unique_instance);
g_shm.deallocate(g_hMem);
g_hMem = nullptr;
shared_memory_object::remove("MySharedMem");
} catch(interprocess_exception& e) {
std::string msg(e.what());
MessageBoxA(NULL, msg.c_str(), "Error", MB_ICONERROR);
}
}
And the client implementation:
BOOL Connect()
{
// Grab the shared memory pool and extract the class
managed_shared_memory shm(open_only, "MySharedMem");
std::pair<B*, std::size_t> ret = shm.find<B>(unique_instance); // <-- Always ends up being 0x00000000!
B *objB = static_cast<B*>(ret.first);
if(!objB) return FALSE;
objB->DoSomethingUseful();
return TRUE;
}
You'll notice that managed_shared_memory::find() always fails to return a valid pointer to the client. But as far as I can tell, the code is perfectly valid. There are no compiler warnings or errors, and everything appears to run smoothly up until this point.
So why is this failing?
How can I get this to work as expected?
Distinct address spaces for pointers
When you are sharing values, everything is clear. For example when put a float value such as 1234.5 in the shared memory, in the other side you can read it simply.
But when you are sharing complex objects (which contain pointers) there is an important problem. The address space of two processes are completely different and distinct. For example you can not put a std::vector object in the shared memory, because it has a pointer to its data in process1 e.g. 0x1ABF212 and this number is meaningless for process2.
So, you have to write down the items of std::vector one by one in the shared memory. Read about Serialization technique to share complex data.
Also, if you're insisting to share complex object as a unique object between processes, you can make those objects with specific memory allocators.
AFAIK Boost.Interprocess has that allocators. It manages shared memory and makes pointers inside the shared memory which is meaningful for both processes.
Sorry for ambiguity, My bad English
You are trying to find B that supposed be created by Unique instance construction. Boost documentation says
The find function obtains a pointer to the only object of type T that can be created using this "unique instance" mechanism.
but in your code you are allocating raw memory and just copy your B object. So your B was not created as Unique instance
So i would suggest change your code as following:
try use
B &objB = *g_shm.construct<B>(unique_instance) (g_objA);
instead of
B objB(g_objA);