I'm trying to model a one-to-many relationship with ODB. I'm basically trying to recreate the example in https://www.codesynthesis.com/products/odb/doc/manual.xhtml#6.2.2
I have to use std::weak_ptr
for one side of the relationship, to avoid circular ownership issues. However, my very simple example code does not compile, as ODB doesn't seem to play nicely with std::weak_ptr.
In my example, every Bar
has exactly one Foo
, while every Foo
has multiple Bar
s. This is my code:
#include <odb/core.hxx>
#include <string>
#include <memory>
#include <vector>
// Forward
class Foo;
#pragma db object
class Bar {
public:
// A Bar has exactly *one* Foo
#pragma db not_null
std::shared_ptr<Foo> cfg;
private:
#pragma db id auto
unsigned long id_;
friend class odb::access;
};
#pragma db object
class Foo {
public:
// A Foo has multiple Bars
// Using std::weak_ptr here instead of std::shared_ptr to avoid circular
// ownership
#pragma db value_not_null inverse(cfg)
std::vector<std::weak_ptr<Bar>> entries;
private:
#pragma db id auto
unsigned long id_;
friend class odb::access;
};
int main() {}
I generate the database code with:
odb --std c++11 --database sqlite --generate-query --generate-schema --at-once main.hpp
And I compile like this:
g++ --std=c++11 main.hpp main-odb.cxx
(I know that would crash at linking - I'm just trying to get it to compile.)
My compiler (GCC 7) tells me:
main-odb.cxx: In static member function ‘static void odb::access::object_traits_impl<Foo, (odb::database_id)1u>::entries_traits::init(odb::access::object_traits_impl<Foo, (odb::database_id)1u>::entries_traits::value_type&, const odb::access::object_traits_impl<Foo, (odb::database_id)1u>::entries_traits::data_image_type&, odb::database*)’:
main-odb.cxx:794:43: error: no matching function for call to ‘std::weak_ptr<Bar>::weak_ptr(odb::object_traits<Bar>::pointer_type)’
obj_traits::object_type > (id));
^
In file included from /usr/include/c++/5/memory:82:0,
from main.hpp:3,
from main-odb.hxx:16,
from main-odb.cxx:7:
/usr/include/c++/5/bits/shared_ptr.h:492:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(std::weak_ptr<_Tp1>&&)
weak_ptr(weak_ptr<_Tp1>&& __r) noexcept
^
/usr/include/c++/5/bits/shared_ptr.h:492:2: note: template argument deduction/substitution failed:
main-odb.cxx:794:43: note: mismatched types ‘std::weak_ptr<_Tp>’ and ‘odb::object_traits<Bar>::pointer_type {aka Bar*}’
obj_traits::object_type > (id));
There are three more candidates which I skipped. The important part: ODB tries somewhere to create a std::weak_ptr<Bar>
from a Bar *
, which is obviously not possible. It would have to create it from a std::shared_ptr<Bar>
. However, the ODB documentation explicitly says one should (and in fact must) use std::weak_ptr
in these cases.
What am I doing wrong?
Okay, I figured out a solution (not sure if it's the best solution):
You can force ODB to use std::shared_ptr<Bar>
instead of Bar *
everywhere. You do so by defining your class as:
#pragma db object pointer(std::shared_ptr)
class Bar {
...
This way, when a std::weak_ptr<Bar>
is being created, it is created from a std::shared_ptr<Bar>
, which works. You can also specify the pointer type to be used at a namespace or global scope, see https://www.codesynthesis.com/products/odb/doc/manual.xhtml#3.3