如何避免序列化器和容器序列化器之间的循环模板依赖?
How do I avoid a circular template dependency between serializers and serializers of containers?
我正在为 de/serialization 的 stl 容器编写模板 classes。
假设我想序列化 set<int>
。我有一个基础 serialisation
class 和以下模板:
template<typename T>class serialiser;
template<>class serialiser<int>:public serialisation<int>{
public:
void serialise ( int t );
};
为了序列化 set
,我有:
template<typename T>class Container_serialiser:public serialisation<T>{
public:
void serialise ( T t );
private:
/* Notice that I must declare a serialiser.
*/
serialiser<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser<T>::serialise ( T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
这仅适用于其元素本身不构成容器的容器。
问题
如果我想序列化一个map<int,set<int> >
怎么办?
由于每个元素的类型都是 pair<int,set<int> >
,我需要以下 class:
template<>class serialiser<pair<int,set<int> > >:public serialisation<pair<int,set<int> > >{
public:
void serialise ( const pair<int,set<int> >t );
private:
serialiser<int>t_first_serialiser;
/* Notice that I must declare a Container_serialiser.
*/
Container_serialiser<set<int> >t_second_serialiser;
};
但是,Container_serialiser
要求 serialiser<T>
是一个完整的类型。因此,要等到上面的class之后才能定义。同样,上面的class要求Container_serialiser<T>
是一个完整的类型,不能在它之前定义。
编译器给出如下错误:
prog.cpp: In instantiation of 'class Container_serialiser<std::map<int, std::set<int> > >':
prog.cpp:73:44: required from here
prog.cpp:40:39: error: 'Container_serialiser<T>::value_serialiser' has incomplete type
serialiser<typename T :: value_type>value_serialiser;
^
prog.cpp:25:27: note: declaration of 'class serialiser<std::pair<const int, std::set<int> > >'
template<typename T>class serialiser;
问题代码
#include <iostream>
#include <map>
#include <set>
#include <utility>
using std :: cout;
using std :: map;
using std :: set;
using std :: make_pair;
using std :: pair;
template<typename T>class serialisation{
public:
virtual ~serialisation ( );
protected:
virtual void serialise ( const T t ) = 0;
};
template<typename T>serialisation<T> :: ~serialisation ( ){
}
template<typename T>class serialiser;
template<>class serialiser<int>:public serialisation<int>{
public:
void serialise ( const int t );
};
void serialiser<int>:: serialise ( const int t ){
cout << t << '\n';
}
template<typename T>class Container_serialiser:public serialisation<T>{
public:
void serialise ( const T t );
private:
serialiser<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser<T>:: serialise ( const T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
template<>class serialiser<pair<int,set<int> > >:public serialisation<pair<int,set<int> > >{
public:
void serialise ( const pair<int,set<int> >t );
private:
serialiser<int>t_first_serialiser;
Container_serialiser<set<int> >t_second_serialiser;
};
void serialiser<pair<int,set<int> > >:: serialise ( const pair<int,set<int> >t ){
t_first_serialiser . serialise ( t . first );
t_second_serialiser . serialise ( t . second );
}
int main ( ){
set<int>t;
t . insert ( 2 );
t . insert ( 3 );
Container_serialiser<set<int> >t_serialiser;
t_serialiser . serialise ( t );
map<int,set<int> >u;
u . insert ( make_pair ( 5,t ) );
u . insert ( make_pair ( 7,t ) );
Container_serialiser<map<int,set<int> > >u_serialiser;
u_serialiser . serialise ( u );
}
黑客
我可以通过编写四个模板来完成这项工作:
serialiser0<T>
对于所有不是容器且不包含容器的 T
Container_serialiser0<T>
对于所有 value_type
是 serialiser0<T>
的参数的 T
serialiser1<T>
对于不是容器但包含本身不包含容器的元素的容器的所有 T
Container_serialiser1<T>
对于所有 value_type
是 serialiser1<T>
的参数的 T
这种设计是重复的,令人困惑的,并且必须为每个新级别的容器手动扩展。有更好的模式吗?
不幸的是,由于我正在从事的项目,我必须避免外部依赖性,例如 boost.serialization 即使以牺牲性能为代价并为 C++98 标准编写。
黑客代码
#include <iostream>
#include <map>
#include <set>
#include <utility>
using std :: cout;
using std :: map;
using std :: set;
using std :: make_pair;
using std :: pair;
template<typename T>class serialisation{
public:
virtual ~serialisation ( );
protected:
virtual void serialise ( const T t ) = 0;
};
template<typename T>serialisation<T> :: ~serialisation ( ){
}
template<typename T>class serialiser0;
template<>class serialiser0<int>:public serialisation<int>{
public:
void serialise ( const int t );
};
void serialiser0<int>:: serialise ( const int t ){
cout << t << '\n';
}
template<typename T>class Container_serialiser0:public serialisation<T>{
public:
void serialise ( const T t );
private:
serialiser0<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser0<T>:: serialise ( const T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
template<typename T>class serialiser1;
template<>class serialiser1<pair<const int,set<int> > >:public serialisation<pair<int,set<int> > >{
public:
void serialise ( const pair<int,set<int> >t );
private:
serialiser0<int>t_first_serialiser;
Container_serialiser0<set<int> >t_second_serialiser;
};
void serialiser1<pair<const int,set<int> > >:: serialise ( const pair<int,set<int> >t ){
t_first_serialiser . serialise ( t . first );
t_second_serialiser . serialise ( t . second );
}
/* This is the same as Container_serialiser0!
*/
template<typename T>class Container_serialiser1:public serialisation<T>{
public:
void serialise ( const T t );
private:
serialiser1<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser1<T>:: serialise ( const T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
int main ( ){
set<int>t;
t . insert ( 2 );
t . insert ( 3 );
Container_serialiser0<set<int> >t_serialiser;
t_serialiser . serialise ( t );
map<int,set<int> >u;
u . insert ( make_pair ( 5,t ) );
u . insert ( make_pair ( 7,t ) );
Container_serialiser1<map<int,set<int> > >u_serialiser;
u_serialiser . serialise ( u );
}
(我必须承认我忘记了这个问题。)
感谢@michael-gopshtein 的评论,我最终偶然发现了 partial template specialization,它很好地解决了这个问题。请注意,我必须同时使用 template <typename SerialisedType> class Serialiser
和 template <typename SerialisedType> inline void serialise(const SerialisedType &x)
。函数本身不能部分特化,但 Serialiser::serialise
的大多数特化取决于对 ::serialise
.
的一般调用
要序列化一个std::map<int, std::set<int>>
,我会这样写:
#include <map>
#include <set>
#include <utility>
// For the sake of having a MWE, I'll just write int-s to stdout.
#include <iostream>
template <typename SerialisedType> class Serialiser;
template <typename SerialisedType>
inline void serialise(const SerialisedType &x) {
Serialiser<SerialisedType>::serialise(x);
}
template <> class Serialiser<int> {
public:
inline static void serialise(const int &x);
};
template <typename Key> class Serialiser<std::set<Key>> {
public:
inline static void serialise(const std::set<Key> &x);
};
template <typename T1, typename T2> class Serialiser<std::pair<T1, T2>> {
public:
inline static void serialise(const std::pair<T1, T2> &x);
};
template <typename Key, typename T>
class Serialiser<std::map<Key, T>> {
public:
inline static void serialise(const std::map<Key, T> &x);
};
void Serialiser<int>::serialise(const int &x) {
std::cout << x << "\n";
}
template <typename Key>
void Serialiser<std::set<Key>>::serialise(const std::set<Key> &x) {
{
const typename std::set<Key>::const_iterator x_cend = x.cend();
for (typename std::set<Key>::const_iterator x_iterator = x.cbegin();
x_iterator != x_cend; ++x_iterator)
::serialise(*x_iterator);
}
}
template <typename T1, typename T2>
void Serialiser<std::pair<T1, T2>>::serialise(const std::pair<T1, T2> &x) {
::serialise(x.first);
::serialise(x.second);
}
template <typename Key, typename T>
void Serialiser<std::map<Key, T>>::serialise(const std::map<Key, T> &x) {
{
const typename std::map<Key, T>::const_iterator x_cend = x.cend();
for (typename std::map<Key, T>::const_iterator x_iterator = x.cbegin();
x_iterator != x_cend; ++x_iterator)
::serialise(*x_iterator);
}
}
int main() {
std::map<int, std::set<int>> u {
{5, {2, 3}},
{7, {2, 3}}
};
serialise(u);
}
输出:
5
2
3
7
2
3
我正在为 de/serialization 的 stl 容器编写模板 classes。
假设我想序列化 set<int>
。我有一个基础 serialisation
class 和以下模板:
template<typename T>class serialiser;
template<>class serialiser<int>:public serialisation<int>{
public:
void serialise ( int t );
};
为了序列化 set
,我有:
template<typename T>class Container_serialiser:public serialisation<T>{
public:
void serialise ( T t );
private:
/* Notice that I must declare a serialiser.
*/
serialiser<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser<T>::serialise ( T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
这仅适用于其元素本身不构成容器的容器。
问题
如果我想序列化一个map<int,set<int> >
怎么办?
由于每个元素的类型都是 pair<int,set<int> >
,我需要以下 class:
template<>class serialiser<pair<int,set<int> > >:public serialisation<pair<int,set<int> > >{
public:
void serialise ( const pair<int,set<int> >t );
private:
serialiser<int>t_first_serialiser;
/* Notice that I must declare a Container_serialiser.
*/
Container_serialiser<set<int> >t_second_serialiser;
};
但是,Container_serialiser
要求 serialiser<T>
是一个完整的类型。因此,要等到上面的class之后才能定义。同样,上面的class要求Container_serialiser<T>
是一个完整的类型,不能在它之前定义。
编译器给出如下错误:
prog.cpp: In instantiation of 'class Container_serialiser<std::map<int, std::set<int> > >':
prog.cpp:73:44: required from here
prog.cpp:40:39: error: 'Container_serialiser<T>::value_serialiser' has incomplete type
serialiser<typename T :: value_type>value_serialiser;
^
prog.cpp:25:27: note: declaration of 'class serialiser<std::pair<const int, std::set<int> > >'
template<typename T>class serialiser;
问题代码
#include <iostream>
#include <map>
#include <set>
#include <utility>
using std :: cout;
using std :: map;
using std :: set;
using std :: make_pair;
using std :: pair;
template<typename T>class serialisation{
public:
virtual ~serialisation ( );
protected:
virtual void serialise ( const T t ) = 0;
};
template<typename T>serialisation<T> :: ~serialisation ( ){
}
template<typename T>class serialiser;
template<>class serialiser<int>:public serialisation<int>{
public:
void serialise ( const int t );
};
void serialiser<int>:: serialise ( const int t ){
cout << t << '\n';
}
template<typename T>class Container_serialiser:public serialisation<T>{
public:
void serialise ( const T t );
private:
serialiser<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser<T>:: serialise ( const T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
template<>class serialiser<pair<int,set<int> > >:public serialisation<pair<int,set<int> > >{
public:
void serialise ( const pair<int,set<int> >t );
private:
serialiser<int>t_first_serialiser;
Container_serialiser<set<int> >t_second_serialiser;
};
void serialiser<pair<int,set<int> > >:: serialise ( const pair<int,set<int> >t ){
t_first_serialiser . serialise ( t . first );
t_second_serialiser . serialise ( t . second );
}
int main ( ){
set<int>t;
t . insert ( 2 );
t . insert ( 3 );
Container_serialiser<set<int> >t_serialiser;
t_serialiser . serialise ( t );
map<int,set<int> >u;
u . insert ( make_pair ( 5,t ) );
u . insert ( make_pair ( 7,t ) );
Container_serialiser<map<int,set<int> > >u_serialiser;
u_serialiser . serialise ( u );
}
黑客
我可以通过编写四个模板来完成这项工作:
serialiser0<T>
对于所有不是容器且不包含容器的 TContainer_serialiser0<T>
对于所有value_type
是serialiser0<T>
的参数的 T
serialiser1<T>
对于不是容器但包含本身不包含容器的元素的容器的所有 TContainer_serialiser1<T>
对于所有value_type
是serialiser1<T>
的参数的 T
这种设计是重复的,令人困惑的,并且必须为每个新级别的容器手动扩展。有更好的模式吗?
不幸的是,由于我正在从事的项目,我必须避免外部依赖性,例如 boost.serialization 即使以牺牲性能为代价并为 C++98 标准编写。
黑客代码
#include <iostream>
#include <map>
#include <set>
#include <utility>
using std :: cout;
using std :: map;
using std :: set;
using std :: make_pair;
using std :: pair;
template<typename T>class serialisation{
public:
virtual ~serialisation ( );
protected:
virtual void serialise ( const T t ) = 0;
};
template<typename T>serialisation<T> :: ~serialisation ( ){
}
template<typename T>class serialiser0;
template<>class serialiser0<int>:public serialisation<int>{
public:
void serialise ( const int t );
};
void serialiser0<int>:: serialise ( const int t ){
cout << t << '\n';
}
template<typename T>class Container_serialiser0:public serialisation<T>{
public:
void serialise ( const T t );
private:
serialiser0<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser0<T>:: serialise ( const T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
template<typename T>class serialiser1;
template<>class serialiser1<pair<const int,set<int> > >:public serialisation<pair<int,set<int> > >{
public:
void serialise ( const pair<int,set<int> >t );
private:
serialiser0<int>t_first_serialiser;
Container_serialiser0<set<int> >t_second_serialiser;
};
void serialiser1<pair<const int,set<int> > >:: serialise ( const pair<int,set<int> >t ){
t_first_serialiser . serialise ( t . first );
t_second_serialiser . serialise ( t . second );
}
/* This is the same as Container_serialiser0!
*/
template<typename T>class Container_serialiser1:public serialisation<T>{
public:
void serialise ( const T t );
private:
serialiser1<typename T :: value_type>value_serialiser;
};
template<typename T>void Container_serialiser1<T>:: serialise ( const T t ){
for(typename T :: const_iterator t_iterator = t . begin ( );t_iterator != t . end ( );++ t_iterator){
value_serialiser . serialise ( * t_iterator );
}
}
int main ( ){
set<int>t;
t . insert ( 2 );
t . insert ( 3 );
Container_serialiser0<set<int> >t_serialiser;
t_serialiser . serialise ( t );
map<int,set<int> >u;
u . insert ( make_pair ( 5,t ) );
u . insert ( make_pair ( 7,t ) );
Container_serialiser1<map<int,set<int> > >u_serialiser;
u_serialiser . serialise ( u );
}
(我必须承认我忘记了这个问题。)
感谢@michael-gopshtein 的评论,我最终偶然发现了 partial template specialization,它很好地解决了这个问题。请注意,我必须同时使用 template <typename SerialisedType> class Serialiser
和 template <typename SerialisedType> inline void serialise(const SerialisedType &x)
。函数本身不能部分特化,但 Serialiser::serialise
的大多数特化取决于对 ::serialise
.
要序列化一个std::map<int, std::set<int>>
,我会这样写:
#include <map>
#include <set>
#include <utility>
// For the sake of having a MWE, I'll just write int-s to stdout.
#include <iostream>
template <typename SerialisedType> class Serialiser;
template <typename SerialisedType>
inline void serialise(const SerialisedType &x) {
Serialiser<SerialisedType>::serialise(x);
}
template <> class Serialiser<int> {
public:
inline static void serialise(const int &x);
};
template <typename Key> class Serialiser<std::set<Key>> {
public:
inline static void serialise(const std::set<Key> &x);
};
template <typename T1, typename T2> class Serialiser<std::pair<T1, T2>> {
public:
inline static void serialise(const std::pair<T1, T2> &x);
};
template <typename Key, typename T>
class Serialiser<std::map<Key, T>> {
public:
inline static void serialise(const std::map<Key, T> &x);
};
void Serialiser<int>::serialise(const int &x) {
std::cout << x << "\n";
}
template <typename Key>
void Serialiser<std::set<Key>>::serialise(const std::set<Key> &x) {
{
const typename std::set<Key>::const_iterator x_cend = x.cend();
for (typename std::set<Key>::const_iterator x_iterator = x.cbegin();
x_iterator != x_cend; ++x_iterator)
::serialise(*x_iterator);
}
}
template <typename T1, typename T2>
void Serialiser<std::pair<T1, T2>>::serialise(const std::pair<T1, T2> &x) {
::serialise(x.first);
::serialise(x.second);
}
template <typename Key, typename T>
void Serialiser<std::map<Key, T>>::serialise(const std::map<Key, T> &x) {
{
const typename std::map<Key, T>::const_iterator x_cend = x.cend();
for (typename std::map<Key, T>::const_iterator x_iterator = x.cbegin();
x_iterator != x_cend; ++x_iterator)
::serialise(*x_iterator);
}
}
int main() {
std::map<int, std::set<int>> u {
{5, {2, 3}},
{7, {2, 3}}
};
serialise(u);
}
输出:
5
2
3
7
2
3