Re: [Boost-users] Boost Serialization: extended_type_info_no_rtti not working...

Hello I get the exception for the derived class - not the base class. Yes, I have done everything correctly for both the base and derived class. After reading your mail, I have several questions: You recommend to include the archives headers once in the executable. This seems strange; I need to include them in many .cpp files (everytime I want to *call* serialization code). This is why I've included serialization code in the .cpp files and not the headers, in order to avoid multiple definitions. Of course, if you include the archive files only once in your executable, I understand that you can put all serialization code in the headers. But being able to include archive headers only once will require me to do quite a bit of code reorganization I think (every function that needs to call serialization has to be put in the same file... even if they are member functions belonging to different classes etc... I do not understand how you can achieve this without a big mess). If, like I did, you include serialization code only in the .cpp files, you avoid multiple definitions problems, since the code is only instantiated for each class during the compile of the .cpp. Of course, the disadvantage is that you must include the archive headers in each class that you want to serialize, before the export.hpp. And the second problem is that at run time it does not work (the problem I am reporting). This is strange. I found yesterday a way to avoid my problem. If I put all the serialization code in the *same* .cpp file, and compile that file, everything works fine. This means that the derived class is properly registered only if during the compilation of the same unit, the code corresponding to the base class is also instantiated. If you instantiate "separately" the serialization code for the base class and the derived class, it does not work. This is probably a bug, but only you can tell since you know the implementation (and with your way of organizing your code, you probably never ran into this problem). In fact, I think I will maybe keep this workaround (one special .cpp file with all the serialization code for all my classes) since it has several advantages (one is that I am independent of any archive implementation, as I only need to change the headers in that only .cpp file ). Thank you, I am waiting for your reply on these remarks. Jean-Noël For which class do you get this exception? But I have done the following: in all my classes, in the implementation unit (.cpp) I have included explicitely the headers needed for the archives I use: #include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> Then I have something like that in each class: #include <boost/serialization/base_object.hpp> #include <boost/serialization/type_info_implementation.hpp> #include <boost/serialization/extended_type_info_no_rtti.hpp> #include <boost/serialization/export.hpp> namespace boost { namespace serialization { template<class Archive> void serialize(Archive & ar, PrimitiveMemberVariableSpecification & m, const unsigned int version) { ar & boost::serialization::base_object<MemberVariableSpecification>(m); ar & m.unused_primitive_type; } } // namespace serialization } // namespace boost BOOST_CLASS_TYPE_INFO( PrimitiveMemberVariableSpecification, extended_type_info_no_rtti<PrimitiveMemberVariableSpecification> ) BOOST_CLASS_EXPORT(PrimitiveMemberVariableSpecification) (Here the name of the class is PrimitiveMemberVariableSpecification. Is the above being done for the base class as well? I also want to report a bug in the documentation: the doc says that you should write: <skip> correct - good call. Normally the BOOST_CLASS_EXPORT should *register* all my classes. Why doesn't it work? I have of course also written a correct get_key() virtual function for all my classes. If I just change extended_type_info_no_rtti to extended_type_info_typeid, everything works fine, but I do want extended_type_info_no_rtti. This suggests that all derived classes are being exported. Also, keeping everything in the headers files seems to be a nightmare, although that is what Robert Ramsey recommends. I get tons of multiple definitions errors at link time if I do that (something like: multiple definition of `boost::archive::detail::guid_initializer<MemberVariableSpecification>::instance' with gcc - MinGW 3.4.2) This would surprise me. It suggests that boost/archive/...archive.hpp files are being included in several places. In order to avoid the bothersome warnings above and speed build time. I try to organize my code so that non-inline code is instantiated in only one place. This is not strictly a requirement but generally I've found that things work better this way. And I'm not just referring to serialization code but C++ template code in general. In theory, it shouldn't matter, but in practice things work better this way. So what works well for me is: Include serialization code in the headers corresponding to each type. That way all if the types are imported into different executables they are all all consistent with each other. include boost/archive/... headers only once in each executable. This will eliminate multiple definition errors. This might take a little bit of code re-organization and should not be strictly necessary - but it has worked well for me. Generally I also recommend that any boost/archive/...hpp headers be included before any boost/serialization/...hpp headers. This is not strictly necessary - though it was at one time - but I'm in the habit of doing it. If I find I can't do it, I'm wondering if I "accidently" included a boost/archive/....hpp in another header - which can lead to multiple definition warnngs. Also if I do this an import the module into another executable, I might be instantiating code that isn't called. By sticking to the "rules of thumb" above, I generally avoid some problems and or surprises. Note that there are couple of cases where header order does make a difference. a) boost/serialization/export.hpp has to follow all the boost/archive/...hpp files for each combination of type/archive export to be instantiated. b) boost/serialization/base_object.hpp has to follow all the boost/archive/*.hpp files. This is an oversight which will be corrected in the next version. I have looked at the documentation for two days, read absolutely everything and still do not understand. Maybe I should look at the code ??? LOL - if you like the documentation - you'll love the code ! But, trapping the exception will tell you which class isn't being "registered" which will be helpful information. Robert Ramey

"RIVASSEAU Jean Noel" <JN.RIVASSEAU@oberthurcs.com> wrote in message news:87F60F7FA02FF749AFB02BD9FCAA6B04AC5EE9@naserv31.nanterre.oberthurcs.com... Hello I get the exception for the derived class - not the base class. Yes, I have done everything correctly for both the base and derived class. After reading your mail, I have several questions: You recommend to include the archives headers once in the executable. This seems strange; I need to include them in many .cpp files (everytime I want to *call* serialization code). This is why I've included serialization code in the .cpp files and not the headers, in order to avoid multiple definitions. Of course, if you include the archive files only once in your executable, I understand that you can put all serialization code in the headers. But being able to include archive headers only once will require me to do quite a bit of code reorganization I think (every function that needs to call serialization has to be put in the same file. even if they are member functions belonging to different classes etc. *** I don't think this is correct. My custom is to make a separate *.cpp file for each class. ******** There is an example in the package called "demo_polymorphic" which includes serialization invocation in differerent *.cpp files for each class to be serialized. This illustrates the considerations which one has to address to be sure that the the required code gets instantiated once and only once. It might be helpful to consider this example. In theory, compiler/linker combinations will discard multiple instantiations but in many cases this will result in undesirable side effects like long compile times, voluminous warnings that should be ignored, etc. For me it has worked best to organize my code to avoid these side effects. I've found it easy to do. Of course, I can't speak for everyone else here. Note that it is helpful to keep in mind the distinction between the headers in the family boost/serialization/... and those in boost/archive/... boost/serialization/.. headers are templates only which specify how types are to be serialized to any archive. boost/archive/... instantiate code to serialize data to/from a specific archive type. So its my practice to include a specific boost/archive/... in only one *.cpp file to avoid the annoying side effects of having multiple definitions in the program. ***** If, like I did, you include serialization code only in the .cpp files, you avoid multiple definitions problems, since the code is only instantiated for each class during the compile of the .cpp. Of course, the disadvantage is that you must include the archive headers in each class that you want to serialize, before the export.hpp. And the second problem is that at run time it does not work (the problem I am reporting). This is strange. *** I cannot reproduce or explain this. I found yesterday a way to avoid my problem. If I put all the serialization code in the *same* .cpp file, and compile that file, everything works fine. This means that the derived class is properly registered only if during the compilation of the same unit, the code corresponding to the base class is also instantiated. If you instantiate "separately" the serialization code for the base class and the derived class, it does not work. This is probably a bug, but only you can tell since you know the implementation (and with your way of organizing your code, you probably never ran into this problem). In fact, I think I will maybe keep this workaround (one special .cpp file with all the serialization code for all my classes) since it has several advantages (one is that I am independent of any archive implementation, as I only need to change the headers in that only .cpp file ). **** to summarize: What works for me is: for each class a) put header code in a separate *.hpp module b) don't include serialization defintions the the class header - only the declarations. That is don't use serialize(...){...} but rather serialize(...); c) In a separate *.cpp file for each class (my_class) i) #include boost/archive/... for each type of archive you want to support ii)#include "my_class.hpp". "my_class.hpp" might contain boost/serialization/export or anything else iii) specify definitions for serialize functions: template<class Archive> void my_class::serialize(Archive & ar, const unsigned int file_version){ ar & ....; // serialize class members } iv) for each archive class I explicitly instantiate the serialize functions like so template my_class::serialize<boost::binary_oarchive> ; ... for each archive. d) Now I have serialization functions instantiated for my_class for any archive I might use. Of course this is bothersome because I have code bloat if my linker isn't smart enough. So I then organize my project as a library and all of the serialize instantiations are added to the library. e) Now, my "real" project just links with the library of all my types. This "two step" process may seem somewhat tedious and maybe it is. But it has a few advantages: a) Once the serialization functions are done they don't have to be continually re-compiled every time you make a small change in your project. b) All code is instantiated once and only once. c) Code that is instantiated but not actually used doesn't show up in your executable as code bloat. This is really a feature of using (non - inlined) templated code in general rather than a feature of the serialization library per se. I'm not sure what else to say about this. ********* A totally different subject is the usage of extended_type_info_no_rtti. After the first review of the serialization system, I was left with a very long list of features that it was "missing". At the bottom of the list was the ability to use an extended type system other than the built-in rtti. This was low priortity in my view. As I got to the end of the list of features and design changes from the first review, Iwas very pleased with the new system. I figured - hell, the other changes worked out well, may this last one (extended type info) will work out well too!? It was a pain in the neck and it resulted in being able to assign a typeinfo system on a class by class basis. It also resulted in factoring the type info management out of the serialization library thereby making the serialization library itself "smaller" and clarifying the concepts involved. I made my test and declared victory. I made the decision that the first type_info system encountered in the header would be the default one used. If none was specified, the rtti based on would be used. (Header order again) Of course now I have some doubts about this decision but at the time it seemed better than including some macro definition somewhere. So, if you're going to use the no-rtti system as the default, then this should probably be the first header specified. But...., as far as I know, no one ever used this functionality. And some questions have been raised in my mind about it. The test does work but I'm not sure its really complete. It does test the case where different typeinfo systems are being used by different classes to be sure that they interoperate correctly. It doesn't test the case where default is the non-rtti version. So you may be breaking new territory here. Robert Ramey
participants (2)
-
RIVASSEAU Jean Noel
-
Robert Ramey