'boost shared_ptr' and 'boost lock' together = messed up - boost

I am new to both concepts shared_ptr and mutex (boost or not boost). I am trying to use it in my classes :
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > WriteLock;
typedef boost::shared_lock< Lock > ReadLock;
class subscriptionInfo
{
public:
//this is not a copy constructible class. so I have to use shared pointer
boost::shared_ptr<Lock> myLock;
...
}
...
std::vector<DATA_MSG_PTR>& subscriptionInfo::getIncoming() {
ReadLock Lock(myLock);
return incoming;
}
and the error says:
error: no matching function for call to ‘boost::shared_lock<boost::shared_mutex>::shared_lock(boost::shared_ptr<boost::shared_mutex>&)’
I will appreciate if you help me find out what I messed up and how to solve it.
thanks

The myLock member is a pointer. A smart one but in any case a pointer. The shared_lock constructor accepts a reference to a mutex object and not a pointer. That is why the error message takes place. To solve the problem you have to dereference the pointer as ReadLock Lock(*myLock);

Related

Shared pointer to incomplete needs deleter in reset method

I am working with shared_ptr storing pointers of a C library.
Here an example of such a C library containing the header bar.h:
#pragma once
typedef struct Flupp MyFlupp;
MyFlupp *
create_flupp();
void
del_flupp(MyFlupp * fp);
void
print_flupp(MyFlupp * f);
Here the struct has a forward declaration and is defined in the bar.so.
I am using the bar.so in my C++ code:
#include <memory>
extern "C"{
#include "bar.h"
}
int main()
{
std::shared_ptr<MyFlupp> flupp_ptr(nullptr, del_flupp);
flupp_ptr.reset(create_flupp());
print_flupp(flupp_ptr.get());
return 0;
}
Here I am storing the MyFlupp* in a shared_ptr. On the declaration, MyFlupp* is unknown and set to nullptr. Later I am calling the reset operation to set the valid pointer. But when I am compling the code, I get the following error:
In file included from /usr/include/c++/8/bits/shared_ptr.h:52,
from /usr/include/c++/8/memory:81,
from test_foo.cpp:1:
/usr/include/c++/8/bits/shared_ptr_base.h: In instantiation of ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*) [with _Yp = Flupp; <template-parameter-2-2> = void; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’:
/usr/include/c++/8/bits/shared_ptr_base.h:1293:4: required from ‘std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> std::__shared_ptr<_Tp, _Lp>::reset(_Yp*) [with _Yp = Flupp; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2; std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> = void]’
test_foo.cpp:10:35: required from here
/usr/include/c++/8/bits/shared_ptr_base.h:1126:19: error: invalid application of ‘sizeof’ to incomplete type ‘Flupp’
static_assert( sizeof(_Yp) > 0, "incomplete type" );
When I am providing the deleter to the reset operation than it is working.
flupp_ptr.reset(create_flupp(), del_flupp);
Can anybody explain me whats going on? I already looked #cppreference but I does not found an answer.
The problem is that the type Flupp has only been forward-declared, but not defined. In the context of the use here, it is considered an incomplete type.
This has certain implications for the use with std::shared_ptr:
std::shared_ptr may be used with an incomplete type T. However, the
constructor from a raw pointer (template<class Y> shared_ptr(Y*)) and
the template<class Y> void reset(Y*) member function may only be
called with a pointer to a complete type (note that std::unique_ptr
may be constructed from a raw pointer to an incomplete type).
Source: cppreference.com
Instead you need to use the respective overloads that accept a pointer and the deleter as arguments.
With unique_ptr this is not necessary, as that one stores the custom deleter as part of the type. But with shared_ptr the deleter is type-erased and only recovered at runtime. This allows you to change the deleter of an existing shared_ptr when calling reset. For this reason you always need to re-state which deleter to use whenever you're calling reset. If no deleter is given, each call to reset will also implicitly reset the deleter to just calling delete on the managed pointer.
So to make it work, just change your reset call to
flupp_ptr.reset(create_flupp(), del_flupp);

Static casting to unique_ptr

I have a head file which needs to hide some internals for complexity and "secrecy" reasons. I've therefore a raw void pointer declared in the oublic header, inside the code there are static casts to convert the raw pointer to it's actual type.
Now due to general memory management changes I need to change the type internally to a unique_ptr (it's coming from an object factory now as a unique_ptr, previously it was a raw pointer).
So in my header I have this:
class SomeClass {
private:
void *_hiddenTypeInstance;
}
Is it possible to static-cast this _hiddenTypeInstance to an internally known unique_ptr type?
This is not a direct answer of what you wanted, but a proposal how to do things nicer:) You can actually still use the memory semantics of std::unique_ptr with hiding the internals and without using the ugly void*. As others have mentioned, you should look into PIMPL, but to summarize:
Forward declare the internal type in the public header
Use std::unique_ptr with that type and provide a dtor for the class which holds that member (otherwise you will get compilation errors because a default dtor will be generated, that will try to delete the forward declared class and will fail to do so).
This would look something like this:
#include <memory>
class CPrivate; // Forward declare the internal class
class CPublic
{
public:
// You need the dtor here, since when you implement it in the .cpp of your library,
// where the definition of CPrivate is known, the dtor of std::unique_ptr will know how to delete it.
// If you do not put the dtor here, a default one will be generated here which invokes the dtor of std::unique_ptr, and here
// since CPrivate is forward declared the dtor of std::unique_ptr will not know how to delete it and you will get an error
~CPublic();
private:
std::unique_ptr<CPrivate> m_pPrivate;
}
By using this, you can then escape the casts inside the implementation from the void* to the actual type.
As for the original question - you can always cast void* to a std::unique_ptr<T>* (a pointer to a unique_ptr). But I would advise to evaluate the solution above. Because the void* thing moves away all type strictness - e.g what happens if someone changes T ?
if i understand you problem in a correct way: here is what you can do. This example is just for understanding of a concept. You can use it in your own code. Since I dont have the entire code I cant write exact solution.
class SomeClass {
private:
void *_hiddenTypeInstance;
public:
std::unique_ptr<int> foo() {
int a;
a = 2;
return std::unique_ptr<int>(&a);
}
void bar() {
std::unique_ptr<int> temp_hidden_type_instance;
temp_hidden_type_instance = std::unique_ptr<int>(static_cast<int*>(_hiddenTypeInstance));
temp_hidden_type_instance = foo();
}
};

Is it safe to init a std::unique_ptr from a local raw pointer?

I know it's unwise to do so with a std::shared_ptr. But what about std::unique_ptr? E.g. :
class A {
public:
void do_something() { }
};
std::vector<std::unique_ptr<A> > uq_ptrs_;
auto p = new A();
uq_ptrs_.push_back(std::unique_ptr<A>(p));
p->do_something();
As long as you don't manually delete the object after creating the std::unique_ptr (or std::shared_ptr!) object then it's fine.
You should also avoid dereferencing the pointer p once you asked the std::unique_ptr (or std::shared_ptr) to take ownership of it. Instead use the smart pointer object.

When initializing an atomic class member it requires a 'deleted' function, but adding it would make it no longer trivially copyable

When initializing an atomic class member it requires a 'deleted' function, but adding it would make it no longer trivially copyable which is a requirement for an object/struct to be atomic. Am I just not understanding how to do this correctly, or is this a problem in the c++ standard?
Take the example below:
#include <atomic>
#include <cstdint>
template<typename T>
struct A
{
T * data;
std::atomic<uintptr_t> next;
};
template<typename T>
class B
{
std::atomic<A<T>> myA;
public:
B ( A<T> & a ) noexcept
{
myA.store(a, std::memory_order_relaxed );
}
};
int main ()
{
A<int> a;
B<int> b(a);
return 0;
}
Trying to compile this with g++ gives error: use of deleted function 'A<int>::A(const A<int>&)' myA.store(a, std::memory_order_relaxed);. My understanding of this error is that the atomic::store method is looking for that constructor in my struct A but not finding it.
Now here is what happens when I add that constructor:
#include <atomic>
#include <cstdint>
template<typename T>
struct A
{
T * data;
std::atomic<uintptr_t> next;
A(const A<T>& obj) { }
A( ) { }
};
template<typename T>
class B
{
std::atomic<A<T>> myA;
public:
B ( A<T> & a ) noexcept
{
myA.store(a, std::memory_order_relaxed );
}
};
int main ()
{
A<int> a;
B<int> b(a);
return 0;
}
I no longer receive the above compiler error but a new one coming from the requirements of the atomic class required from 'class B<int>' .... error: static assertion failed: std::atomic requires a trivially copyable type ... In other words by adding the used-defined constructors I have made my struct A a non-trivially copyable object which cannot be initialized in class B. However, without the user-defined constructors I cannot use the store method in myA.store(a, std::memory_order_relaxed).
This seems like a flaw in the design of the std::atomic class. Now maybe I am just doing something wrong because I don't have a lot of experience using C++11 and up (I'm old school). Since 11 there have been a lot of changes and the requirements seem to be a lot stricter. I'm hoping someone can tell me how to achieve what I want to achieve.
Also I cannot change std::atomic<A<T>> myA; to std::atomic<A<T>> * myA; (changed to pointer) or std::atomic<A<T>*> myA;. I realize this will compile but it will destroy the fundamental design of a class I am trying to build.
The problem here resides in the fact that std::atomic requires a trivially copiable type. This because trivially copyable types are the only sure types in C++ which can be directly copied by copying their memory contents directly (eg. through std::memcpy). Also non-formerly trivially copyable types could be safe to raw copy but no assumption can be made on this.
This is indeed important for std::atomic since copy on temporary values is made through std::memcpy, see some implementation details for Clang for example.
Now at the same time std::atomic is not copy constructible, and this is for reasonable reasons, check this answer for example, so it's implicitly not trivially copyable (nor any type which contains them).
If, absurdly, you would allow a std::atomic to contain another std::atomic, and the implementation of std::atomic contains a lock, how would you manage copying it atomically? How should it work?

initialize a string size during declaration

suppose I have a class like this and I would like to create a string with a
specific capacity. I tried doing the following but that did not work.
Any suggestions ? I know I could do it in the constructor but would like to do it during the declaration if possible.
class foo
{
std::string bar = std::string().resize(45);
}
I get the error
main.cpp: In function 'int main()':
main.cpp:8:46: error: conversion from 'void' to non-scalar type 'std::__cxx11::string {aka std::__cxx11::basic_string}' requested
std::string test = std::string().resize(45);
In C++, you probably don't want to "chain" methods unless previous methods have the correct return type.
As suggested by #James Maa, you can do simply use the constructor.
In c++11 we have new feature called move constructor so
string str = string();
doesn't cause extra time.
http://en.cppreference.com/w/cpp/language/move_constructor
with move constructor, the program would directly use the address of the temporary constructed string after = sign, without making a copy.
The problem is that resize()function in c++ actually returns void
basic_string( size_type count,
CharT ch,
const Allocator& alloc = Allocator() );
This constructor might be something you are directly interested in.
You can do something with
std::string str(45, ' ');

Resources