template <class Archive> inline void serialize(Archive& ar, foo::bar baz) {
...
}
in the same binary? I can isolate the two use cases into separate translation units, but if the same template function is instantiated multiple times, wouldn't the linker in the end just pick one and discard the other, assuming that they are all equivalent, even if here they are actually not? So it seems like doing this may work, or not, depending on what gets inlined where...
I tried using https://uscilab.github.io/cereal/archive_specialization.html together with a custom derived class like
class MyJSONInputArchive : public cereal::JSONInputArchive {public:MyJSONInputArchive(std::istream& stream): cereal::JSONInputArchive(stream) {}};
and the using sfinae or directly specializing the templates like
template <> inline void serialize(MyJSONInputArchive& ar, foo::bar baz) {
...
}
but that doesn't seem to work for existing common types like std::map<int, foo::bar>, because in the implementation for serialization of std::map for JSONInputArchive, the static archive type is JSONInputArchive even if the dynamic type is MyJSONInputArchive and therefore it doesn't pick up the specialization. So the only solution I can see here, is to provide also a specialization for std::map for MyJSONInputArchive.
Long story short: Is there a way to support different serialization for custom types within the same binary, depending on the Archive type by some simple derivation of the existing archive types, that doesn't require to reimplement serialization for any other common types except for the ones that I need to be different?
Best,
Nikolaus
In one project A, I'd like to include headers from project B that implement the serialization of some datatypes such than I can load such files in project A.Now the issue is, that both projects define serialization for some thirdparty library type, lets say foo::bar (in reality, its Eigen::Matrix<...>), but unfortunately the serialization format is slightly different. It would be the simplest, if I could make them compatible, but for legacy reasons I cannot change it in either project.
Hi Nikolaus,I can not think of a good solution following your archive-specialization approach.
archive(cereal::make_nvp("foo", wrap_eigen(foo)));
Do you share my assessment that having different definitions of "serialize" with the same signature but different definition in different translation units that get linked into the same binary is problematic?
By serialization-only wrapper you mean that the datastructures are unchanged, but the project A serialization functions do something like
archive(cereal::make_nvp("foo", wrap_eigen(foo)));for a member `foo` that is of type Eigen::Matrix?
[...] using e.g. stl types like std::map<int, Eigen::Matrix<...>> would require me to also implement wrap_eigen() for stl containers, right? Or am I missing a simpler way for that case?
Do you share my assessment that having different definitions of "serialize" with the same signature but different definition in different translation units that get linked into the same binary is problematic?Yes, my intuition tells me that such a situation is problematic. However, I am not an expert in this: It seems to me that the one-definition rule (ODR) applies to inline functions with external linkage only. I am not sure if it is legal to declare and define *static* inline functions in two different cpp files. Alternatively, I wonder if anonymous namespaces in the cpp files can help.In conclusion: I can not give you a definitive answer here. All I want to share is the idea of a workaround (wrapper around Eigen::Matrix in namespace of project A).
By serialization-only wrapper you mean that the datastructures are unchanged, but the project A serialization functions do something like
archive(cereal::make_nvp("foo", wrap_eigen(foo)));for a member `foo` that is of type Eigen::Matrix?Yes, that is one possibility I had in mind, and that is what I described as a serialization-only wrapper. You found the disadvantage of that:[...] using e.g. stl types like std::map<int, Eigen::Matrix<...>> would require me to also implement wrap_eigen() for stl containers, right? Or am I missing a simpler way for that case?Yes, you are right. It took me a while to understand that you have two small wrappers in mind: One for Eigen::Matrix and one for std::map<int, Eigen::Matrix>. I can imagine that it is a useful solution if there are only a couple of different containers around Eigen::Matrix that shall be serialized.
I also considered adding an overload/"better match" for std::map serialization (if the mapped_type matches Eigen::Matrix) in project A, but inside the ::cereal namespace. However, that feels fragile because the default std::map serialization is part of another project. It also asks for more trouble of the original kind by adding stuff to the cereal namespace.
If there are more and more use cases of Eigen::Matrix serialization inside different containers then at some point the ProjectA::EigenMatrixWrapper (publicly derived from Eigen::Matrix) may become simpler. The latter solves the std::map issue for free because it becomes std::map<int, ProjectA::EigenMatrixWrapper>.
I believe this is all I can contribute here. I am interested to follow your progress, so feel free to share your findings.
On Thursday, August 1, 2019 at 1:37:26 PM UTC+2, Julius Rapp wrote:Do you share my assessment that having different definitions of "serialize" with the same signature but different definition in different translation units that get linked into the same binary is problematic?Yes, my intuition tells me that such a situation is problematic. However, I am not an expert in this: It seems to me that the one-definition rule (ODR) applies to inline functions with external linkage only. I am not sure if it is legal to declare and define *static* inline functions in two different cpp files. Alternatively, I wonder if anonymous namespaces in the cpp files can help.In conclusion: I can not give you a definitive answer here. All I want to share is the idea of a workaround (wrapper around Eigen::Matrix in namespace of project A).Ah, I think you might be right about external linkage. I think two identical functions with internal linkage should be allowed, so if I can make both definitions of the the serializers have internal linkage (using anonymous namespace or static), and strictly separate their usage to distinct translation units, I think it should be ok. I just haven't tried if "cereal::serialize" can be made static (or put into anonymous namespace inside namespace cereal) and still participate in the template specialization in the same way. But maybe it just works. I'll give that a try. That would overall maybe be the easiest solution (for cases like mine, where you can edit change source code of both projects, but don't want to change the serialization format in either project).