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?
Related
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.
I'd like to store objects of a class in an std::map. Here is a working example showing how I am doing it currenty
#include <iostream>
#include <map>
class A
{
private:
int a;
std::string b;
public:
A(int init_a, std::string init_b) : a(init_a), b(init_b){};
void output_a() {std::cout << a << "\n";}
};
int main()
{
std::map<size_t, A> result_map;
for (size_t iter = 0; iter < 10; ++iter)
{
A a(iter, "bb");
result_map.insert(std::make_pair(iter, a));
}
return 0;
}
I have two question to this example:
Is this the professional C++-way to store objects in an std::map in the above case? Or should I create a pointer to an object of A and store that instead? I like the first (current) option as I don't have to worry about memory management myself by using new and delete - but most importantly I'd like to do things properly.
How would I go about calling a member function of, say, result_map[0]? I naively tried result_map[0].output_a(), but that gave me the error: error: no matching function for call to ‘A::A()’
Is this the professional C++-way to store objects in an std::map in the above case?
It is fine, simpler code could be:
result_map.emplace(iter, A(iter, "bb") );
you should use whatever you find more readable. By the way calling integer counter iter is not a way to write a readable code.
How would I go about calling a member function of, say, result_map[0]?
You better use std::map::find:
auto f = result_map.find( 0 );
if( f != result_map.end() ) f->output_a();
problem with operator[] in your case - it has to create and instance if object does not exist with that index but you do not have default ctor for A.
1- It depends: If your class can be copied and you're not worried about performance issues with copying objects into the map, then that's a good way to do it. However, if say your class held any immutable data (std::mutex for example) you'd have to use a pointer, as the copy constructor c++ automatically generates would be ill formed, so it merely wouldn't be able to copy the class
2- result_map.at(0).output_a() or result_map.at(0)->output_a() if you're using a map of pointers
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).
I am new to c++, i am on a check scanner's project and i am using an API that was provided with the scanner. Here's my code:
.h file :
#include <iostream>
#include<Windows.h>
#include<vector>
using namespace std;
class Excella
{
public:
vector<char*> getDevicesName();
};
.cpp file :
vector<char*> Excella::getDevicesName()
{
DWORD dwResult;
vector<char*> listeDevices;
char pcDevName[128]="";
int i = 6;
// the device's name is stored in the variable 'pcDevName'
while ((dwResult = MTMICRGetDevice(i, (char*)pcDevName)) != MICR_ST_DEVICE_NOT_FOUND) {
dwResult = MTMICRGetDevice(i, (char*)pcDevName);
i++;
listeDevices.push_back((char*) pcDevName);
}
return listeDevices;
}
main.cpp
vector<char*> liste = excella.getDevicesName();
if (liste.empty()!= true)
{
for (vector<char*>::iterator IterateurListe = liste.begin(); IterateurListe != liste.end(); ++IterateurListe)
{ string str(*IterateurListe);
auto managed = gcnew String(str.c_str());
devices->Items->Add(managed);
}
}
else {
MessageBox::Show("The vector is empty");
}
The problem is that i can get the right device number.. i just have some weird caracters.
Thank u for ur help.
That's not surprising.
char pcDevName[128]=""; will go out of scope at the end of of the function vector<char*> Excella::getDevicesName(). So any pointers to this that you've pushed to the vector will no longer be valid. Formally speaking, the behaviour of your program is undefined.
It's far simpler to use std::vector<std::string> instead. Remarkably, that's the only change you'd have to make: push_back((char*) pcDevName) will take a value copy of pcDevName (that's how the std::string constructor works). Drop the unnecessary (char*) casts though.
Here:
listeDevices.push_back((char*) pcDevName);
you are pushing into listeDevices a pointer to stack array. There are two problems with this - mayor one is that once your getDevicesName function ends, those pointers are invalid and use of them is Undefined, the other is that in each iteration of your loop you overwrite pcDevName and also your stored pointer content.
What you should do is to make listeDevices store std::string, ie. std::vector<std::string>, and then you can use listeDevices.push_back((char*) pcDevName); to safely store your names in a vector.
I am testing with the shared_ptr of vector. The purpose is simple, I want to return a pointer of vector and access the value in it. But it gives exception. "Unhandled exception at.. std:out_of_range at memory location ..". I am using Visual Studio 2012.
vector<int>* func()
{
boost::shared_ptr<vector<int> > vec(new vector<int>());
vec->push_back(123);
return vec.get();
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<int>* result = func();
cout << result->at(0); // ERROR here
return 0;
}
If you want to use shared pointers, then return a shared pointer, not a raw one. Otherwise, when the shared_ptr goes out of scope, it will destroy the vector. Accessing that vector will lead to undefined behaviour.
boost::shared_ptr<vector<int> > func()
{
boost::shared_ptr<vector<int> > vec(new vector<int>());
vec->push_back(123);
return vec;
}
However, note that it's much better to return the vector by value:
vector<int> func()
{
vector<int> vec;
vec.push_back(123);
return vec;
}
This way, there are no copies being made, either through move constructors or RVO.
The type you return from func() needs to be boost::shared_ptr<vector<int>> - not vector<int>*.
The whole point of shared pointers is you can pass them around as much as you like and when they all stop being referenced the memory they pointed to is reclaimed.
As you "forget" the reference to the shared pointer when you leave the function it will automatically reclaim the memory assigned at that point - leaving you with a pointer to an invalid memory location.
Your shared_ptr in your function is the only one referencing the pointer to your vector. When it goes out of scope (when the function returns), it therefore deletes the referenced pointer.
Make your function return a shared_ptr instead of a regular pointer.
I'd suggest you read up on how shared_ptr should be used, cause you're doing it all wrong.. It's meant to not having to deal with raw pointers anymore, definitely not mixing them. Basically you pass around the shared_ptr instance like you used to with raw pointers, but without having to care about deleting it anymore. Do this instead:
typedef std::vector< int > my_vector;
typedef boost::shared_ptr< my_vector > my_vector_ptr;
my_vector_ptr func()
{
my_vector_ptr vec( boost::make_shared< my_vector >() );
vec->push_back(123);
return vec.get();
}
int _tmain(int argc, _TCHAR* argv[])
{
my_vector_ptr result = func();
cout << result->at(0);
return 0;
}
Use back inserter to avoid mixing inserting and returning the same vector. Let the client specify witch type of vector it is ... thus making the function template-able.
code:
typedef std::vector< int > my_vector;
typedef boost::shared_ptr< my_vector > my_vector_ptr;
template <typename OutputIterator>
void func1(OutputIterator it)
{
// std::copy (anotherVector.begin(), anotherVector.end(), it);
*it++ = 123;
}
void func2(my_vector& v)
{
v.push_back(123);
}
int main()
{
my_vector_ptr vec( new my_vector() );
func1(std::back_inserter(*vec)); // func is now an algorithm ..
func2(*vec);
}
In func1, the signature of the function says what this function does. You can't erase from vector, you can't do anything else, just what it says. Insert.
Think of func1 as an algorithm.