shared_ptr initialization - c++

A member is defined as
std::shared_ptr<std::array<std::string, 6> > exit_to;
which points to additional data shared among others.
When try to initiate the pointer "exit_to". The correct way is
node_knot.exit_to = std::make_shared<std::array<std::string, 6> >();
But it's in another file and I'd like to keep the pointer type consistent, something like this:
node_knot.exit_to = std::make_shared<decltype(*node_knot.exit_to)>();
But won't compile:
/usr/include/c++/4.6/bits/shared_ptr_base.h:798:54: error: '__p'
declared as a pointer to a reference of type
'std::array<std::basic_string<char>, 6> &'
__shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, _Tp* __p)
^ /usr/include/c++/4.6/bits/shared_ptr.h:93:31: note: in instantiation
of template class
'std::__shared_ptr<std::array<std::basic_string<char>, 6> &, 1>'
requested here
class shared_ptr : public __shared_ptr<_Tp>
^ ../node_booker.h:757:20: note: in
instantiation of template class
'std::shared_ptr<std::array<std::basic_string<char>, 6> &>' requested
here
n.exit_to = std::make_shared<decltype(*n.exit_to)>();
I'm under Ubuntu 12.10, clang++ 3.2, with --std=c++11

You need to remove the reference from the type you are passing to make_shared.
The following should work:
node_knot.exit_to = std::make_shared<std::remove_reference<decltype(*node_knot.exit_to)>::type>();

The problem is that the type of *exit_to is a reference, and you can't have a shared_ptr to a reference.
You could remove the reference, but instead of finding the type returned by operator* and then stripping the reference off it, it's probably easier to just ask the shared_ptr what type it contains:
node_knot.exit_to = std::make_shared<decltype(node_knot.exit_to)::element_type>();
The nested element_type is the type stored by the shared_ptr.
Another option would be to add a typedef to the class and use it consistently wherever you need it:
typedef std::array<std::string, 6> string_array;
std::shared_ptr<string_array> exit_to;
// ...
node_knot.exit_to = std::make_shared<Node::string_array>();
This is a lot more readable than using decltype

Related

How to change raw pointer to unique_ptr in container of pointers wrapped by boost::variant

Live code example
I am trying to hold a variant of pointers to templated versions of a base class in a vector. The boost::variant of pointers happens to be contained in a struct. It works fine if these pointers are raw pointers, but things start going wrong when I change them to unique_ptr.
struct Sayer {
struct Category {
using GetterVariant = boost::variant<
//Getter<string>*, // works OK
//Getter<double>*, // ...
//Getter<int>* // ...
unique_ptr<Getter<string>>,
unique_ptr<Getter<double>>,
unique_ptr<Getter<int>>
>;
Category(GetterVariant g) :
getter(g)
{}
GetterVariant getter;
};
vector<Category> categories;
template <typename G>
void addGetter() {
categories.emplace_back(new G()); // allocate here, transfer ownership to Sayer::categories
}
};
Compiler error:
/usr/include/boost/variant/variant.hpp:1627:28: error: no matching member
function for call to 'initialize'
initializer::initialize(
~~~~~~~~~~~~~^~~~~~~~~~
/usr/include/boost/variant/variant.hpp:1798:9: note: in instantiation of function
template specialization
'boost::variant<std::unique_ptr<Getter<std::__cxx11::basic_string<char,
std::char_traits<char>, std::allocator<char> > >,
std::default_delete<Getter<std::__cxx11::basic_string<char,
std::char_traits<char>, std::allocator<char> > > > >,
std::unique_ptr<Getter<double>, std::default_delete<Getter<double> > >,
std::unique_ptr<Getter<int>, std::default_delete<Getter<int> > >
>::convert_construct<AgeGetter *>' requested here
convert_construct( detail::variant::move(operand), 1L);
...
main.cpp:54:16: note: in instantiation of function template specialization
'std::vector<Sayer::Category, std::allocator<Sayer::Category>
>::emplace_back<AgeGetter *>' requested here
categories.emplace_back(new G());
^
main.cpp:65:9: note: in instantiation of function template specialization
'Sayer::addGetter<AgeGetter>' requested here
sayer.addGetter<AgeGetter>();
...
/usr/include/boost/variant/detail/initializer.hpp:115:24: note: candidate
function not viable: no known conversion from 'typename
::boost::move_detail::remove_reference<AgeGetter *&>::type'
(aka 'AgeGetter *') to 'std::unique_ptr<Getter<int>,
std::default_delete<Getter<int> > >' for 2nd argument
/usr/include/boost/variant/detail/initializer.hpp:149:17: note: candidate
function not viable: requires 0 arguments, but 2 were provided
static void initialize();
How do I set this up so that the memory ownership is in the container?
Two things:
First, you have to move g in the Category Constructor, since a variant is non-copyable if any of its members is non-copyable.
Second, while every conversion in the chain AgeGetter* to Getter<int>* to std::unique_ptr<Getter<int>> to Category is implicit, C++ only does a limited number of implicit conversions. So basically this chain is too long and you can fix it for example by using emplace_back(std::make_unique<G>()) instead of emplace_back(new G()).
Also, this is safer, since if emplace_back throws (which it can), the new G() would not be deleted and hence leak. But the destructor unique_ptr returned by std::make_unique<G>() would be called if emplace_back throws and hence there would be no leak. You should always try to avoid raw new in your code.

How can I initialize a map without explicitly invoking an explicit constructor?

I am trying to initialize a std::map with an initializer list (in production, this is a member initializer of a class, but my minimal failing example is below). Given
#include <map>
struct Cfg {};
struct Alg
{
explicit Alg(Cfg const&) {}
};
using MyMap = std::map<int, Alg>;
int main()
{
Cfg cfg;
MyMap m = {
{1, {cfg}}, // error #1
{2, cfg}, // error #2
{3, Alg(cfg)}, // works fine
};
return 0;
}
When compiling, error #1 is:
foo.cc: In function ‘int main()’:
foo.cc:22:5: error: converting to ‘const Alg’ from initializer list would use
explicit constructor ‘Alg::Alg(const Cfg&)’
};
^
This is pretty straightforward. Passing a Cfg to the initializer requires conversion and the explicit constructor prohibits it.
Error #2 is
foo.cc: In function ‘int main()’:
foo.cc:22:5: error: converting to ‘std::pair<const int, Alg>’ from initializer list would use explicit constructor ‘constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U1 = int; _U2 = Cfg&; typename std::enable_if<(std::_PCC<true, _T1, _T2>::_MoveConstructiblePair<_U1, _U2>() && (! std::_PCC<true, _T1, _T2>::_ImplicitlyMoveConvertiblePair<_U1, _U2>())), bool>::type <anonymous> = 0; _T1 = const int; _T2 = Alg]’
};
^
This is a little more confusing. I think the error talks about implicitly invoking an explicit std::pair constructor; reading the documentation for std::pair, I get lost in which constructors are explicit when.
The third case is explicit construction. For reasons of maintainability, I'd rather not do that.
It seems like I've read that many initializer list counter-intuitive issues are solvable by adding more braces (gross simplification) but I confess I'm lost at this point.
If I remove the explicit qualification of the Alg constructor, all three cases compile. However, I'm not sure it makes sense to provide implicit conversion just to simplify an initializer list.
Is there a way to initialize my map elements without explicitly constructing the Alg members? Using g++ 7.3
As far as I know, there is no way around specifying the Alg type. This is the intention of explicit constructors anyhow. Just for the sake of [I don't know what to say here], you can nevertheless invoke std::pair's in-place constructor like the following.
MyMap m{{std::piecewise_construct, std::forward_as_tuple(1), std::tie(cfg)}};
This way, you don't have to type Alg, which kind of answers your questions. Besides, do the above only if you hate yourself and your co-workers.
Note: the in-place constructor of std::pair is actually present to allow for non-copyable, non-movable types.

Can I pass a const object to db.persist in ODB?

I'm trying out ODB ORM and I have to stick to an interface, so I need to take a const object and persist it. I'm not sure if the ODB API allows to persist a const object, because some part seems prepared for that, but it doesn't work.
I am receiving this error from gcc here:
void OdbReportRW::write_object(const MyObject &my_object)
{
odb::core::transaction t{db->begin()};
db->persist(my_object);
t.commit();
}
this is the error, which I think it says that my_object should not be const:
In file included from /usr/local/include/odb/database.hxx:632:0,
from odb_report.hpp:21,
from src/vcf/odb_report.cpp:18:
/usr/local/include/odb/database.txx: In instantiation of ‘typename odb::object_traits<T>::id_type odb::database::persist_(T&) [with T = const MyObject; odb::database_id DB = (odb::database_id)5u; typename odb::object_traits<T>::id_type = long unsigned int]’:
/usr/local/include/odb/database.ixx:167:45: required from ‘typename odb::object_traits<T>::id_type odb::database::persist(const T&) [with T = MyObject; typename odb::object_traits<T>::id_type = long unsigned int]’
src/vcf/odb_report.cpp:134:26: required from here
/usr/local/include/odb/database.txx:38:39: error: no matching function for call to ‘odb::object_traits_impl<MyObject, (odb::database_id)5u>::persist(odb::database&, const MyObject&)’
object_traits::persist (*this, obj);
^
/usr/local/include/odb/database.txx:38:39: note: candidate is:
In file included from src/vcf/odb_report.cpp:27:0:
my-object-error-odb.hxx:247:5: note: static void odb::access::object_traits_impl<MyObject, (odb::database_id)1u>::persist(odb::database&, odb::access::object_traits<MyObject>::object_type&)
persist (database&, object_type&);
^
my-object-odb.hxx:247:5: note: no known conversion for argument 2 from ‘const MyObject’ to ‘odb::access::object_traits<MyObject>::object_type& {aka MyObject&}’
Same error with clang, a bit more descriptive:
In file included from src/vcf/odb_report.cpp:18:
In file included from inc/vcf/odb_report.hpp:21:
In file included from /usr/local/include/odb/database.hxx:632:
/usr/local/include/odb/database.txx:38:36: error: binding of reference to type 'object_type' (aka 'MyObject') to a value of type 'const MyObject' drops qualifiers
object_traits::persist (*this, obj);
^~~
/usr/local/include/odb/database.ixx:167:12: note: in instantiation of function template specialization 'odb::database::persist_<const MyObject, 5>' requested here
return persist_<const T, id_common> (obj);
^
src/vcf/odb_report.cpp:134:13: note: in instantiation of function template specialization 'odb::database::persist<MyObject>' requested here
db->persist(my_object);
^
inc/vcf/error-odb.hxx:247:37: note: passing argument to parameter here
persist (database&, object_type&);
^
However, I can see in the database interface (from odb) that both types of persist are provided: reference and const reference (and other with pointers):
// in odb/database.hxx
template <typename T>
typename object_traits<T>::id_type
persist (T& object);
template <typename T>
typename object_traits<T>::id_type
persist (const T& object);
I saw that a similar error is raised (for find, not persist) when there's no default constructor in the class to persist (MyObject here), but it is there, so it's not the problem. I already checked that the default constructor only generates an extra method find in the generated code.
It worked removing the const specifier in my write_object method, but as I said, I have to stick to an interface.
Any ideas to persist a const object?
reading more thoroughly the documentation I found this (http://www.codesynthesis.com/products/odb/doc/manual.xhtml#3.8):
The first persist() function expects a constant reference to an instance being persisted. The second function expects a constant object pointer. Both of these functions can only be used on objects with application-assigned object ids (Section 14.4.2, "auto").
indeed, I was using the auto specifier for database-handled ids:
// class MyObject
#pragma db id auto
unsigned long id_;
So it seems that I can not use at the same time the auto id and the const reference persist.

Can converting structure elements to const-reference save some overhead?

Scenario,
I have a trace file having thousands of 'Entries'(I call them 'tick').
Each 'Entry' contains several lines of information.
There are two types of information(location data and multicast data) so each line is either about 'location' or 'multicast'.
Goal:
My goal is to store them into my data structure efficiently:
Implementation:
for storing information of each entry, I have :
//////////////////
struct multicast_data{
//... integers, vectors of integers, string etc.
}
std::list<multicast_data> multicasts;
///////////////////////
struct AgentLocation{
//... integers, operator overload etc.
};
std::set<AgentLocation> agentsLocation;
////////////////////////
Next, I want to store the entries in a queue, So I first bundle them in another structure like this:
struct tickDataBundle{
const std::list<multicast_data> & multicasts;
const std::set<AgentLocation> & agentsLocation;
tickDataBundle(const std::list<multicast_data> &multicasts,
const std::set<AgentLocation> &agentsLocation):
multicasts(multicasts),
agentsLocation(agentsLocation){}
};
then I put them into a queue:
MessageQueue<tickDataBundle> m_processed_data;
Questions:
My confusion is, the containers(list and set) in tickDataBundle are created and populated in the scope of some method, and naturally these containers are destroyed when the scope ends(no heap). So even returning the reference to these containers will create dangling reference. So I figured if,before the scope ends, I add the const-reference of these containers to tickDataBundle object, then push this object to the MessageQueue , I would have benefitted from the C++'s optimization to save the full containers from being destroyed and reduce some copying overhead. was this a correct and valid assumption?
why cant't I save a const-reference of tickDataBundle in the above queue?
(MessageQueue<const tickDataBundle&> m_processed_data; generates error)
thank you
UPDATE: this part is JUST FOR YOUR REFERENCE
I am copying
the structure of my MessageQueue and
the error I will get if I declare MessageQueue<const tickDataBundle&> m_processed_data;
template<class T>
class MessageQueue {
std::queue<T> messageList;
boost::shared_mutex mutex;
public:
MessageQueue();
virtual ~MessageQueue();
bool ReadMessage();
void post(T message);
bool pop(T&);
void clear();
int size();
};
error:
In file included from /usr/include/c++/4.8/deque:64:0,
from /usr/include/c++/4.8/queue:60,
from ./ns3/drop-tail-queue.h:22,
from ./ns3/network-module.h:24,
from ../src/simmobility/examples/simmobility-RR-baseline.cc:2:
/usr/include/c++/4.8/bits/stl_deque.h: In instantiation of ‘class std::_Deque_base<const sim_mob::RoadRunnerBaseLine::tickDataBundle&, std::allocator<const sim_mob::RoadRunnerBaseLine::tickDataBundle&> >’:
/usr/include/c++/4.8/bits/stl_deque.h:730:11: required from ‘class std::deque<const sim_mob::RoadRunnerBaseLine::tickDataBundle&, std::allocator<const sim_mob::RoadRunnerBaseLine::tickDataBundle&> >’
/usr/include/c++/4.8/bits/stl_queue.h:96:46: required from ‘class std::queue<const sim_mob::RoadRunnerBaseLine::tickDataBundle&, std::deque<const sim_mob::RoadRunnerBaseLine::tickDataBundle&, std::allocator<const sim_mob::RoadRunnerBaseLine::tickDataBundle&> > >’
./ns3/smb_message_queue.h:18:16: required from ‘class sim_mob::MessageQueue<const sim_mob::RoadRunnerBaseLine::tickDataBundle&>’
./ns3/smb_roadrunner_baseline.h:87:47: required from here
/usr/include/c++/4.8/bits/stl_deque.h:448:60: error: forming pointer to reference type ‘const sim_mob::RoadRunnerBaseLine::tickDataBundle&’
typedef _Deque_iterator<_Tp, _Tp&, _Tp*> iterator;
^
/usr/include/c++/4.8/bits/stl_deque.h:449:60: error: forming pointer to reference type ‘const sim_mob::RoadRunnerBaseLine::tickDataBundle&’
typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
^
/usr/include/c++/4.8/bits/stl_deque.h:488:61: error: forming pointer to reference type ‘const sim_mob::RoadRunnerBaseLine::tickDataBundle&’
typedef typename _Alloc::template rebind<_Tp*>::other _Map_alloc_type;
AND THE ERROR CONTINUES...
1. I figured if I make const-reference if the elements in the tickDataBundle, it will reduce some copying overhead. was this a correct and valid assumption?
The answer is yes. All copy of tickDataBundle objects only own the const reference of multicasts and agentsLocation, not coping real data in them.
*2. why cant't I save a const-reference of tickDataBundle in the above queue? (MessageQueue m_processed_data; generates error)*
The const reference must be assigned at the object construct time point. You can use const pointer as const reference, like this:
MessageQueue<const tickDataBundle*> m_processed_data;

std::for_each compile error with g++, not VS2012

I have the following code (as much simplified as possible):
#include <vector>
#include <algorithm>
using std::vector;
enum class Foo {
BAR, BAZ
};
void print_to_file(const vector<const vector<Foo> >& sequences) {
std::for_each(sequences.begin(), sequences.end(), [](const vector<Foo>& sequence) {
});
}
int main(int argc, char **argv) {
return EXIT_SUCCESS;
}
when compiling with g++ (SUSE Linux) 4.7.1 20120723 [gcc-4_7-branch revision 189773] as g++ --std=c++11 test.cpp I get the following error message:
In file included from /usr/include/c++/4.7/x86_64-suse-linux/bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/vector:62,
from test.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘struct __gnu_cxx::new_allocator<const std::vector<Foo> >’:
/usr/include/c++/4.7/bits/allocator.h:89:11: required from ‘class std::allocator<const std::vector<Foo> >’
/usr/include/c++/4.7/bits/alloc_traits.h:89:43: required from ‘struct std::allocator_traits<std::allocator<const std::vector<Foo> > >’
/usr/include/c++/4.7/ext/alloc_traits.h:109:10: required from ‘struct __gnu_cxx::__alloc_traits<std::allocator<const std::vector<Foo> > >’
/usr/include/c++/4.7/bits/stl_vector.h:76:28: required from ‘struct std::_Vector_base<const std::vector<Foo>, std::allocator<const std::vector<Foo> > >’
/usr/include/c++/4.7/bits/stl_vector.h:208:11: required from ‘class std::vector<const std::vector<Foo> >’
test.cpp:11:25: required from here
/usr/include/c++/4.7/ext/new_allocator.h:83:7: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = const std::vector<Foo>; __gnu_cxx::new_allocator<_Tp>::const_pointer = const std::vector<Foo>*; __gnu_cxx::new_allocator<_Tp>::const_reference = const std::vector<Foo>&]’ cannot be overloaded
/usr/include/c++/4.7/ext/new_allocator.h:79:7: error: with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = const std::vector<Foo>; __gnu_cxx::new_allocator<_Tp>::pointer = const std::vector<Foo>*; __gnu_cxx::new_allocator<_Tp>::reference = const std::vector<Foo>&]’
the same code compiles fine with VS2012, but fails badly under g++ and I've not the slightest idea how to interpret the error message.
I'm surprised you don't get problems elsewhere, as this vector<const vector<Foo> > is not a possible data type.
The value_type of a std::vector must at minimum be copy constructible (C++03) or movable (C++11). If it is const, you cannot assign or move it.
The Allocator requirements used for standard containers doesn't allow for const objects to be held by the containers. C++11 17.6.3.5 "Allocator requirements" outlines the requirements for standard allocators using a set of tables which start with Table 27. In Table 27, the first definition is for the 'descriptive variables' T, U, and C which are defined as "any non-const, non-reference object type". The descriptive variables X and Y, used for the Allocator class defintions, are:
X - an Allocator class for type T
Y - the corresponding Allocator class for type U
In summary, standard Allocators are defined in terms of parameterization by non-const types.
There's an old GCC/libstdc++ bug for this (resolved as invalid) going back to 2004 (C++2003 has a similar limitation against storing const objects in containers, but it's simpler to show because the 2003 standard more straightforwardly says that the types of objects stored in containers must be 'Assignable').
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16875