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

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.

Related

How to search for const pointer in a collection of non-const pointers?

I have a member variable std::set<T*> m_associates;, i.e., a collection of non-const raw pointers, and simply want to check for existence of another pointer.
To maintain const correctness my function looks like this:
bool MyClass::is_associated(const T* x) const
{
return (m_associates.find(x) != m_associates.end());
}
However, this does not compile, since x is passed as const T* to indicate that the value pointed to by x is not changed by the function, but m_associates contains non-const T*.
If I remove const from the x parameter, it compiles, but violates const correctness...
Adding const to m_associates, i.e., std::set<const T*> m_associates; is not an option either as I need the non-const pointers elsewhere in my class.
How do I solve this? Is this (possibly the only) point where a const_cast should be used? Or do I have to always pass all parameter T pointers as non-const?
Edit:
Full error output, compiler is clang++-8, code is in C++17
error: no matching member function for call to 'find'
return (m_associates.find(x) != m_associates.end());
~~~~~~~~~~~~^~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_set.h:798:7: note: candidate function not viable: 1st argument ('const T *') would lose const qualifier
find(const key_type& __x) const
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_set.h:804:2: note: candidate function template not viable: 'this' argument has type 'const std::set<T *>', but method is not marked const
find(const _Kt& __x)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_set.h:794:7: note: candidate function not viable: 'this' argument has type 'const std::set<T *>', but method is not marked const
find(const key_type& __x)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_set.h:810:2: note: candidate template ignored: substitution failure [with _Kt = const T *]: no matching member function for call to '_M_find_tr'
find(const _Kt& __x) const
^
The reason your current code fails is that the default Compare for std::set<T> is std::less<T>; which forces both arguments to be a T for comparison -- in this case, non-const T* types. Since const T* cannot be converted to T* without casting away the constness, this is causing your compile errors.
If you are using C++14 or above, you can redefine your std::set so that the Compare template type is a transparent comparator (one that deduces the underlying types for comparison), for example std::set<T*, std::less<>>. This will enable the overload of std::set::find that deduces the type and forwards the argument to the comparator, which will enable the above code to work.
You can cast away the const-ness of x with no ill-effects in this case.
Other than perhaps signalling a design flaw, the only thing you need to watch out with casting away const is that the behaviour on attempting to modify an object that was originally declared using const via the non-const pointer is undefined. That's not the case here.

Member function pointer issue with standard library methods

This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.
The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.

Passing a member function pointer to an overloaded class method into a template function [duplicate]

This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.
The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.

Return reference to template vector

I start from simple class declaration where I defined inline template method which returning reference to specyfic type of container.
class JPetParamManager
{
public:
enum ContainerType {kScintillator, kPM, kKB, kTRB, kTOMB};
std::vector<JPetScin> fScintillators;
std::vector<JPetPM> fPMs;
std::vector<JPetKB> fKBs;
std::vector<JPetTRB> fTRBs;
std::vector<JPetTOMB> fTOMBs;
template <typename T>
const std::vector<T>& getContainer(const JPetParamManager::ContainerType &p_containerType) const
{
switch(p_containerType)
{
case kScintillator:
return fScintillators;
case kPM:
return fPMs;
case kKB:
return fKBs;
case kTRB:
return fTRBs;
case kTOMB:
return fTOMBs;
}
}
}
In some another class method I want to return some container from class above:
void JPetAnalysisModuleKB::CreateOutputObjects(const char* outputFilename)
{
std::vector<JPetKB> l_KBs22 = m_manager.getParamManagerInstance().getContainer<JPetKB>(JPetParamManager::ContainerType::kKB);
}
When I want to run this method in main I have error like:
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h: In member function ‘const std::vector<_RealType>& JPetParamManager::getContainer(const JPetParamManager::ContainerType&) const [with T = JPetKB]’:
JPetAnalysisModuleKB.cpp:55:126: instantiated from here
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:81:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetScin>’
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:83:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetPM>’
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:87:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetTRB>’
./../../framework/JPetManager/../JPetParamManager/JPetParamManager.h:89:14: error: invalid initialization of reference of type ‘const std::vector<JPetKB>&’ from expression of type ‘const std::vector<JPetTOMB>’
make: *** [JPetAnalysisModuleKB.o] Błąd 1
Introduction
Even though only one of your switch-labels will match and execute, the statement(s) associated with the others must still be valid.
The compiler is trying to tell you that not all of your returns can be used when returning a std::vector<T> const& (where T is the type passed to your function).
Explanation
The below instantiate getContainer in a way that makes it return a std::vector<PetKB>, but when instantiating the function the compiler will see that the case-label matched by kScintillator has a return of type std::vector<JPetScin>.
m_manager.getParamManagerInstance().getContainer<JPetKB> (JPetParamManager::kScintillator)
Since std::vector<JPetScin> cannot be converted to a std::vector<PetKB> the compiler complains and basically says that your code is ill-formed.
The same applies even if the switch-condition doesn't select a case where the return-type is different; all paths must be able to return, otherwise an application is ill-formed.

shared_ptr initialization

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