Problem understanding 'boost::intrusive::list_member_hook<>'
I'm in the early stages of trying to understand boost::intrusive::list' so forgive me if I'm just doing something stupid!! Using VS2019 I wrote a small demo app which uses 'boost::intrusive::list_base_hook<>':- #include <boost/intrusive/list.hpp> #include <string> #include <iostream> using namespace std; class animal : public boost::intrusive::list_base_hook<> { public: animal (string n, int l) : name{move(n)}, legs{l} {} string name; int legs; }; using animal_list = boost::intrusive::list<animal>; template<typename T> void print_list (const boost::intrusive::list<T>& elem) { for (const T& a : elem) cout << "A " << a.name << " has " << a.legs << " legs" << '\n'; } int main() { animal a1{"dog", 4}; animal a2{"spider", 6}; animal a3{"budgie", 2}; animal_list animals; animals.push_back(a1); animals.push_back(a2); animals.push_back(a3); print_list (animals); return 0; } Everything runs fine and I see the expected output but I've noticed that some other developers use something called a 'list_member_hook' rather than my 'list_base_hook' - so class animal would look like this:- class animal { public: animal (string n, int l) : name{move(n)}, legs{l} {} string name; int legs; boost::intrusive::list_member_hook<> _animal_hook; }; and then later on in the program there'd be a couple of lines looking something like this:- typedef boost::intrusive::member_hook<animal, boost::intrusive::list_member_hook<>, &animal::_animal_hook> AnimalHookOption; typedef boost::intrusive::list<animal, AnimalHookOption> Animals; But when I try that here, VS2019 throws up this compiler error:- error C2039: 'default_list_hook': is not a member of 'animal' Can anyone explain what I'm doing wrong?? John
But when I try that here, VS2019 throws up this compiler error:-
error C2039: 'default_list_hook': is not a member of 'animal'
Can anyone explain what I'm doing wrong??
John
It's difficult to see what's wrong without a full compilable example but the following snippet works perfectly for me in Visual 2019: #include <boost/intrusive/list.hpp> #include <string> #include <iostream> using namespace std; class animal { public: animal (string n, int l) : name{move(n)}, legs{l} {} string name; int legs; boost::intrusive::list_member_hook<> _animal_hook; }; typedef boost::intrusive::member_hook<animal, boost::intrusive::list_member_hook<>, &animal::_animal_hook> AnimalHookOption; typedef boost::intrusive::list<animal, AnimalHookOption> Animals; void print_list (const Animals& elem) { for (const Animals::value_type& a : elem) cout << "A " << a.name << " has " << a.legs << " legs" << '\n'; } int main() { animal a1{"dog", 4}; animal a2{"spider", 6}; animal a3{"budgie", 2}; Animals animals; animals.push_back(a1); animals.push_back(a2); animals.push_back(a3); print_list (animals); return 0; }
Many thanks lon, Just before your reply I spotted a conflict between my original use of 'animals' and the new usage. Sorry for the noise! John On 07/09/2021 16:33, Ion Gaztañaga via Boost-users wrote:
But when I try that here, VS2019 throws up this compiler error:-
error C2039: 'default_list_hook': is not a member of 'animal'
Can anyone explain what I'm doing wrong??
John
It's difficult to see what's wrong without a full compilable example but the following snippet works perfectly for me in Visual 2019:
#include <boost/intrusive/list.hpp> #include <string> #include <iostream>
using namespace std;
class animal { public: animal (string n, int l) : name{move(n)}, legs{l} {}
string name; int legs; boost::intrusive::list_member_hook<> _animal_hook; };
typedef boost::intrusive::member_hook<animal, boost::intrusive::list_member_hook<>, &animal::_animal_hook> AnimalHookOption;
typedef boost::intrusive::list<animal, AnimalHookOption> Animals;
void print_list (const Animals& elem) { for (const Animals::value_type& a : elem) cout << "A " << a.name << " has " << a.legs << " legs" << '\n'; }
int main() { animal a1{"dog", 4}; animal a2{"spider", 6}; animal a3{"budgie", 2}; Animals animals;
animals.push_back(a1); animals.push_back(a2); animals.push_back(a3); print_list (animals);
return 0; } _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
On 07/09/2021 16:43, John Emmas wrote:
Many thanks lon,
Just before your reply I spotted a conflict between my original use of 'animals' and the new usage. Sorry for the noise!
BTW (hope this isn't more noise...) I realise there are two types of hook available for 'boost::intrusive::list' ( the list_member_hook<> and the list_base_hook<>) - so are there certain situations where one would preferable over the other? Thanks, John
On 08/09/2021 15:56, John Emmas wrote:
I realise there are two types of hook available for 'boost::intrusive::list' ( the list_member_hook<> and the list_base_hook<>) - so are there certain situations where one would preferable over the other?
Overnight I got told (off-list) that when 2 x classes are both declared using the 'list_base_hook<>' option, I'll get a runtime crash if I try to add the same object to both lists. In other words, this will crash at the specified line:- class animal : public boost::intrusive::list_base_hook<> { public: animal (string n, int l) : name{move(n)}, legs{l} {} string name; int legs; }; typedef boost::intrusive::list<animal> Animals; typedef boost::intrusive::list<animal> Dogs; int main() { animal a1{"labrador", 4}; animal a2{"bulldog", 4}; Animals animals; animals.push_back (a2); // <--- 'bulldog' works okay Animals dogs; dogs.push_back (a1); // <--- 'labrador' Works okay dogs.push_back (a2); // <--- 'bulldog' crashes !!! return 0; } and sure enough it does crash there! So I then tried changing the design to use 'boost::intrusive::list_member_hook<>' but that gave me the same runtime crash... so is this just a general limitation with 'boost::intrusive::list'? Thanks again for any advice, John
El 09/09/2021 a las 11:28, John Emmas via Boost-users escribió:
On 08/09/2021 15:56, John Emmas wrote:
I realise there are two types of hook available for 'boost::intrusive::list' ( the list_member_hook<> and the list_base_hook<>) - so are there certain situations where one would preferable over the other?
Overnight I got told (off-list) that when 2 x classes are both declared using the 'list_base_hook<>' option, I'll get a runtime crash if I try to add the same object to both lists. In other words, this will crash at the specified line:-
class animal : public boost::intrusive::list_base_hook<> { [...] };
[...]
animals.push_back (a2); // <--- 'bulldog' works okay [...] dogs.push_back (a2); // <--- 'bulldog' crashes !!!
You're trying to insert a2 into dogs when the element is alredy inserted into animals. You can either remove a2 from animals beforehand: animals.erase(Animals::s_iterator_to(a2)); or, if you mean for the elements to be allowed in both lists at the same time, use tags: #include <boost/intrusive/list.hpp> #include <string> #include <utility> typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct animal_tag>> animals_hook; typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct dog_tag>> dogs_hook; class animal : public animals_hook, public dogs_hook { public: animal (std::string n, int l) : name{std::move(n)}, legs{l} {} std::string name; int legs; }; typedef boost::intrusive::list<animal,boost::intrusive::base_hook<animals_hook>> Animals; typedef boost::intrusive::list<animal,boost::intrusive::base_hook<dogs_hook>> Dogs; int main() { animal a1{"labrador", 4}; animal a2{"bulldog", 4}; Animals animals; animals.push_back (a2); // <--- 'bulldog' works okay Dogs dogs; dogs.push_back (a1); // <--- 'labrador' works okay dogs.push_back (a2); // <--- 'bulldog' works okay return 0; } Joaquín M López Muñoz
Thanks Joaquin - one quick question... do I need to define 'struct animal_tag' and 'struct dog_tag' somewhere? At present, your code compiles and links okay but I still see a runtime crash at the same line ('dogs.push_back (a2);') This time though, it gives me a runtime error message saying:- Assertion failed: !safemode_or_autounlink || node_algorithms::inited(to_insert), file F:\+GTK-SOURCES\gnu-windows\include\boost\intrusive\list.hpp, line 271 Thanks, John On 09/09/2021 12:16, Joaquin M López Muñoz via Boost-users wrote:
if you mean for the elements to be allowed in both lists at the same time, use tags:
#include <boost/intrusive/list.hpp> #include <string> #include <utility>
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct animal_tag>> animals_hook; typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct dog_tag>> dogs_hook;
class animal : public animals_hook, public dogs_hook { public: animal (std::string n, int l) : name{std::move(n)}, legs{l} {}
std::string name; int legs; };
typedef boost::intrusive::list<animal,boost::intrusive::base_hook<animals_hook>> Animals; typedef boost::intrusive::list<animal,boost::intrusive::base_hook<dogs_hook>> Dogs;
int main() { animal a1{"labrador", 4}; animal a2{"bulldog", 4};
Animals animals; animals.push_back (a2); // <--- 'bulldog' works okay
Dogs dogs; dogs.push_back (a1); // <--- 'labrador' works okay dogs.push_back (a2); // <--- 'bulldog' works okay
return 0; }
Joaquín M López Muñoz
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
El 09/09/2021 a las 15:21, John Emmas escribió:
On 09/09/2021 12:16, Joaquin M López Muñoz via Boost-users wrote:
[...] if you mean for the elements to be allowed in both lists at the same time, use tags:
#include <boost/intrusive/list.hpp> #include <string> #include <utility>
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct animal_tag>> animals_hook; typedef boost::intrusive::list_base_hook<boost::intrusive::tag<struct dog_tag>> dogs_hook;
class animal : public animals_hook, public dogs_hook { public: animal (std::string n, int l) : name{std::move(n)}, legs{l} {}
std::string name; int legs; };
typedef boost::intrusive::list<animal,boost::intrusive::base_hook<animals_hook>> Animals; typedef boost::intrusive::list<animal,boost::intrusive::base_hook<dogs_hook>> Dogs;
int main() { animal a1{"labrador", 4}; animal a2{"bulldog", 4};
Animals animals; animals.push_back (a2); // <--- 'bulldog' works okay
Dogs dogs; dogs.push_back (a1); // <--- 'labrador' works okay dogs.push_back (a2); // <--- 'bulldog' works okay
return 0; } [portion above moved from end of post]
Please don't top post. Thank you! https://www.boost.org/community/policy.html
Thanks Joaquin - one quick question... do I need to define 'struct animal_tag' and 'struct dog_tag' somewhere?
No, this is not needed.
At present, your code compiles and links okay but I still see a runtime crash at the same line ('dogs.push_back (a2);')
It seems to work for me: http://coliru.stacked-crooked.com/a/d605f0b3bccfe9fa Please note that dogs in line 29 is defined as: Dogs dogs; whereas in your original code this was: Animals dogs; Maybe this is the problem? Joaquín M López Muñoz
On 09/09/2021 16:48, Joaquin M López Muñoz via Boost-users wrote:
Please note that dogs in line 29 is defined as:
Dogs dogs;
whereas in your original code this was:
Animals dogs;
Maybe this is the problem?
Aaargh, you're right - that was exactly it. Many thanks and sorry for the top-posts, John
Hi Ion and Jaoquin - our own code is obviously more complicated than my examples and currently uses 'list_member_hook<>'. This morning I modified it to use tags and then later (when that wouldn't compile) I tried using 'list_base_hook<>' - but both cases triggered a compile-time assertion here in 'boost/intrusive/detail/generic_hook.hpp':- BOOST_INTRUSIVE_FORCEINLINE void unlink() { /* This line asserts ---> */ BOOST_STATIC_ASSERT(( (int)hooktags::link_mode == (int)auto_unlink )); node_ptr n(this->this_ptr()); if(!node_algorithms::inited(n)){ node_algorithms::unlink(n); node_algorithms::init(n); } } I know you can't see the code but could either of you take a guess at what kinda thing might trigger that assertion? There's a mention of 'link_mode' so could it be because we're using 'boost::intrusive::list' with dynamic linkage maybe (i.e. in a DLL)? My previous examples compiled okay but of course they were very simple examples just running in an exe Thanks for any suggestions, John
El 11/09/2021 a las 12:50, John Emmas via Boost-users escribió:
Hi Ion and Jaoquin - our own code is obviously more complicated than my examples and currently uses 'list_member_hook<>'.
This morning I modified it to use tags and then later (when that wouldn't compile) I tried using 'list_base_hook<>' - but both cases triggered a compile-time assertion here in 'boost/intrusive/detail/generic_hook.hpp':-
BOOST_INTRUSIVE_FORCEINLINE void unlink() { /* This line asserts ---> */ BOOST_STATIC_ASSERT(( (int)hooktags::link_mode == (int)auto_unlink )); node_ptr n(this->this_ptr()); if(!node_algorithms::inited(n)){ node_algorithms::unlink(n); node_algorithms::init(n); } }
This looks like an element has been destroyed before being removed from the list(s) it belongs in. To enable such scenarios you have to set the auto-unlink mode (not set by default): https://www.boost.org/doc/html/intrusive/auto_unlink_hooks.html So, you have two options: * Make sure that elements are removed from lists before being destroyed * Activate auto-unlink mode for your lists Joaquín M López Muñoz
On 11/09/2021 12:11, Joaquin M López Muñoz via Boost-users wrote:
So, you have two options:
* Make sure that elements are removed from lists before being destroyed * Activate auto-unlink mode for your lists
Thanks Jaoquin - after reading that article it looked like I'd need to change all occurrences of:- public boost::intrusive::list_base_hook<> to be this:- public boost::intrusive::list_base_hook< boost::intrusive::link_mode<boost::intrusive::auto_unlink> > but after doing that, all compiles now trigger a new assert in 'boost/intrusive/list.hpp':- //Constant-time size is incompatible with auto-unlink hooks! BOOST_STATIC_ASSERT(!(constant_time_size && ((int)value_traits::link_mode == (int)auto_unlink) )); After lunch I'll try homing in on it a bit closer - but is it possible there's a particular #define needed for this to work? John
Em 11 de set de 2021, à(s) 13:51, John Emmas via Boost-users <boost-users@lists.boost.org> escreveu: On 11/09/2021 12:11, Joaquin M López Muñoz via Boost-users wrote:
So, you have two options:
* Make sure that elements are removed from lists before being destroyed * Activate auto-unlink mode for your lists
Thanks Jaoquin - after reading that article it looked like I'd need to change all occurrences of:-
public boost::intrusive::list_base_hook<>
to be this:-
public boost::intrusive::list_base_hook< boost::intrusive::link_mode<boost::intrusive::auto_unlink> >
but after doing that, all compiles now trigger a new assert in 'boost/intrusive/list.hpp':-
//Constant-time size is incompatible with auto-unlink hooks! BOOST_STATIC_ASSERT(!(constant_time_size && ((int)value_traits::link_mode == (int)auto_unlink) )); The comment above the assert says it all. Take a look at how constant_time_size<false> is used in the docs for auto-unlink hooks.
Joaquín M López Muñoz
On 11/09/2021 13:10, Joaquín M López Muñoz via Boost-users wrote:
Em 11 de set de 2021, à(s) 13:51, John Emmas via Boost-users <boost-users@lists.boost.org> escreveu:
compiles now trigger a new assert in 'boost/intrusive/list.hpp':-
//Constant-time size is incompatible with auto-unlink hooks! BOOST_STATIC_ASSERT(!(constant_time_size && ((int)value_traits::link_mode == (int)auto_unlink) )); The comment above the assert says it all. Take a look at how constant_time_size<false> is used in the docs for auto-unlink hooks.
Thanks again Joaquin - AFAICT the only occurrences of 'constant_time_size' are within libboost itself. In fact for 'class list_impl' it seems to be defined as a constant:- static const bool constant_time_size = ConstantTimeSize; However, I've realised that the problem I reported initially (today) does seem to be connected to DLL linkage somehow. For example, this code in a source file compiles okay:- #include <boost/intrusive/list.hpp> class Point : public boost::intrusive::list_base_hook<> { }; whereas this seemingly minor change:- #include <boost/intrusive/list.hpp> class __declspec(dllexport) Point : public boost::intrusive::list_base_hook<> { }; will immediately invoke this compile-time assertion:- "BOOST_STATIC_ASSERT(( (int)hooktags::link_mode == (int)auto_unlink ));" - i.e. the message I reported earlier... So (presumably ?) 'boost::intrusive::list_base_hook<>' can't be used in a class that'll get exported from a DLL? John
On 11/09/2021 14:59, John Emmas wrote:
this seemingly minor change:-
#include <boost/intrusive/list.hpp> class __declspec(dllexport) Point : public boost::intrusive::list_base_hook<> { };
will immediately invoke this compile-time assertion:- "BOOST_STATIC_ASSERT(( (int)hooktags::link_mode == (int)auto_unlink ));" - i.e. the message I reported earlier...
So (presumably ?) 'boost::intrusive::list_base_hook<>' can't be used in a class that'll get exported from a DLL?
Just wondering if anyone can reproduce this? Today I've tried 3 x different revisions of libboost:- 1.77, 1.74 and 1.71 - but they all give me the same assertion message. So maybe it's an intentional limitation? John
On 12/09/2021 9:26, John Emmas via Boost-users wrote:
On 11/09/2021 14:59, John Emmas wrote:
this seemingly minor change:-
#include <boost/intrusive/list.hpp> class __declspec(dllexport) Point : public boost::intrusive::list_base_hook<> { };
will immediately invoke this compile-time assertion:- "BOOST_STATIC_ASSERT(( (int)hooktags::link_mode == (int)auto_unlink ));" - i.e. the message I reported earlier...
So (presumably ?) 'boost::intrusive::list_base_hook<>' can't be used in a class that'll get exported from a DLL?
Just wondering if anyone can reproduce this? Today I've tried 3 x different revisions of libboost:- 1.77, 1.74 and 1.71 - but they all give me the same assertion message. So maybe it's an intentional limitation?
John
hi, Sorry about being missing these days. When you declare something as declspec the compiler must instantiate the class including all base classes. A base hook has members that have only sense when some options are used so the instantiation fails. To sum up: yes, boost.intrusive is not prepared to be used in a DLL interface. Best, Ion
On 13/09/2021 07:43, Ion Gaztañaga via Boost-users wrote:
Sorry about being missing these days. When you declare something as declspec the compiler must instantiate the class including all base classes. A base hook has members that have only sense when some options are used so the instantiation fails. To sum up: yes, boost.intrusive is not prepared to be used in a DLL interface.
That's okay Ion - I'll feed that back to the main devs here and see if we could build as a static lib, rather than a DLL. Thanks again to you and Joaquin for all your help with this, John
Sorry Joaquin, I'm not trying to drag this out but I wondered if you can clarify something for me... On 09/09/2021 12:16, Joaquin M López Muñoz via Boost-users wrote:
if you mean for the elements to be allowed in both lists at the same time, use tags:
I emailed our main developer overnight - but he seems to think that tags are only needed when building with MSVC. For other compilers (in his case, gcc) he's under the impression that elements can exist in both lists simultaneously by using list_member_hook<> rather than tags. I'd assumed it'd be the same for any compiler... would you mind clarifying it for me please? Thanks again, John
El 14/09/2021 a las 8:54, John Emmas via Boost-users escribió:
Sorry Joaquin, I'm not trying to drag this out but I wondered if you can clarify something for me...
On 09/09/2021 12:16, Joaquin M López Muñoz via Boost-users wrote:
if you mean for the elements to be allowed in both lists at the same time, use tags:
I emailed our main developer overnight - but he seems to think that tags are only needed when building with MSVC. For other compilers (in his case, gcc) he's under the impression that elements can exist in both lists simultaneously by using list_member_hook<> rather than tags.
You can also have multiinsertion by using multiple member hooks instead of multiple (tagged) base hooks: https://godbolt.org/z/GanT6dh1c
I'd assumed it'd be the same for any compiler... would you mind clarifying it for me please? Thanks again,
Well, the example above works. That said, the last paragraph in https://www.boost.org/doc/html/intrusive/usage.html#intrusive.usage.usage_me... reads: "However, member hooks have some implementation limitations: If there is a virtual inheritance relationship between the parent and the member hook, then the distance between the parent and the hook is not a compile-time fixed value so obtaining the address of the parent from the member hook is not possible without reverse engineering compiler produced RTTI. Apart from this, the non-standard pointer to member implementation for classes with complex inheritance relationships in MSVC ABI compatible-compilers is not supported by member hooks since it also depends on compiler-produced RTTI information." So, member hooks won't work (anywhere) if virtual inheritance is used or, in MSVC, in some unspecified "complex inheritance" scenarios. What those scenarios are you should ask Ion, I guess. So far, most of our interchange have referred back to the documentation, which, IMHO, is pretty good. Do your developers have any problem with Boost.Intrusive docs that may point at their improvement? Why don't they try things (like whether MSVC works or not with meber hooks) on their own? Best Joaquín M López Muñoz
participants (4)
-
Ion Gaztañaga
-
Joaquin M López Muñoz
-
Joaquín M López Muñoz
-
John Emmas