template functions distinguishing pointers

I'm setting up generic save and load functions for serialization. Where we are not dealing with basic types, the second and third templates below should be instantiated, which call member functions of the object in question.

The problem is that pointers end up calling the second rather than the third function. Eg: When the first function is call with vector<X*> it will, on serializing its members, call the first function which uses '.' rather than '->'.

What should I do?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <class X>
void Custom_Save(const std::vector<X>& In,std::basic_ofstream<TCHAR>& File)
{
	int l=In.size();
	Custom_Save(l,File);
	for (int n=0;n<l;++n)
	{
		Custom_Save(In[n],File);
	}
}

...

template <class T>
void Custom_Save(const T& In,std::basic_ofstream<TCHAR>& File)
{
	In.Save(File);
}
template <class T>
void Custom_Save(const T* In,std::basic_ofstream<TCHAR>& File)
{
	In->Save(File);
}
I believe all you need to do is swap the order of the second and third functions.
Nope - didn't help.
I don't know if this is the best way to do it, but it certainly is an option:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

template <class T>
struct IsPtr { static const bool VAL=false; };

template <class T>
struct IsPtr<T*> { static const bool VAL=true; };

int main()
{
    cout << IsPtr<int>::VAL << endl;
    cout << IsPtr<char*>::VAL << endl;

    if (IsPtr<double>::VAL)
        cout << "I'm optimized out!" << endl;
    else
        cout << "lalalalalalalalala" << endl;

    cout << "\n(hit enter to quit...)";
    cin.get();

    return 0;
}

You can use it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template <class X>
void Custom_Save(const std::vector<X>& In,std::basic_ofstream<TCHAR>& File)
{
    int l=In.size();
    Custom_Save_Ref(l,File);
    for (int n=0;n<l;++n)
    {
        if (IsPtr<X>::VAL)
            Custom_Save_Ptr(In[n],File);
        else
            Custom_Save_Ref(In[n],File));
    }
}

//...

template <class T>
void Custom_Save_Ref(const T& In,std::basic_ofstream<TCHAR>& File)
{
    In.Save(File);
}

template <class T>
void Custom_Save_Ptr(const T* In,std::basic_ofstream<TCHAR>& File)
{
    In->Save(File);
}

No checks are made when you run your program.
They are optimized out, since IsPtr<X>::VAL is known during compilation.
Last edited on
Thanks m4ster r0shi,

Great! This is the sort of thing I was thinking would be the answer, though I had not managed to get it up and running yet.

It would be nice if we could avoid the if... else... through some sort of traits template, though admitedly it would be more aesthetic than essential. My use of traits templates is non-existent, but if someone thinks there is a more effiecient solution, their help would be appreciated! This is something I want to learn more about...

EA
There is. First, there is boost::type_traits which gives you the is_pointer trait.
(http://www.boost.org/doc/libs/1_45_0/libs/type_traits/doc/html/boost_typetraits/reference/is_pointer.html)

Building on it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template< typename T, typename boost::is_pointer<T>::type >
struct custom_saver;

template< typename T >
struct custom_saver< T, boost::true_type >
{
    static void save( const T*, std::basic_ofstream<TCHAR>& ) {}
};

template< typename T >
struct custom_saver< T, boost::false_type >
{
    static void save( const T&, std::basic_ofstream<TCHAR>& ) {}
};

template< typename T >
void custom_save( const T& t )
{
    custom_saver<T>::save( t );
}

Also, note that the code I posted above won't work...

This:

1
2
3
4
5
6
//...
        if (IsPtr<X>::VAL)
            Custom_Save_Ptr(In[n],File);
        else
            Custom_Save_Ref(In[n],File));
//... 

won't compile.

Either X is a pointer or it's not. In either case, one of the calls causes a compilation error
(the compiler doesn't know/care that the error causing call will be optimized out later).
To make amends for my mistake, here's a working, boost free version of boost boy's code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <vector>
using namespace std;

template <class T>
struct IsPtr { static const bool VAL=false; };

template <class T>
struct IsPtr<T*> { static const bool VAL=true; };

template <class T, bool>
struct CustomSaver;

template <class T>
struct CustomSaver<T,false>
{
    static void Save(const T & data)
    {
        cout << "saving reference..." << endl;
        data.Save();
    }
};

template <class T>
struct CustomSaver<T*,true>
{
    static void Save(const T * data)
    {
        cout << "saving pointer..." << endl;
        data->Save();
    }
};

template <class T>
void CustomSave(const T & data)
{
    cout << "saving T..." << endl;
}

template <class T>
void CustomSave(const vector<T> & data)
{
    cout << "saving vector..." << endl;

    int size=data.size();
    CustomSave(size);

    for (int n=0; n<size; ++n)
        CustomSaver<T,IsPtr<T>::VAL>::Save(data[n]);
}

struct Storable { void Save() const {} };

int main()
{
    vector<Storable> vs(1);
    vector<Storable*> vps(1);

    CustomSave(vs); cout << endl;
    CustomSave(vps); cout << endl;

    cout << "(hit enter to quit...)";
    cin.get();

    return 0;
}

But notice that it won't work if the contained objects don't have a Save member function.
For example, it won't work for a vector of ints or doubles. I'll see how this can be fixed...
Last edited on
Thanks so much to you both.

I now have the following, which appears to compile. Once I change the load functions as well I will test it, but if I don't follow up then it works :)

A note: The boost code did not compile - from the error messages I think VS alleged boost::true_type was not a compile time constant.

Thanks again!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

template <class T>
struct IsPtr { static const bool VAL=false; };

template <class T>
struct IsPtr<T*> { static const bool VAL=true; };

template <class T, bool>
struct CustomSaver;

template <class T>
struct CustomSaver<T,false>
{
    static void Save(const T & data,std::basic_ofstream<TCHAR>& File)
    {
        data.Save(File);
    }
};

template <class T>
struct CustomSaver<T*,true>
{
    static void Save(const T * data,std::basic_ofstream<TCHAR>& File)
    {
        data->Save(File);
    }
};

template <class T>
void Custom_Save(const T In,std::basic_ofstream<TCHAR>& File)
{
	CustomSaver<T,IsPtr<T>::VAL>::Save(In,File);
}

void Custom_Save (const int In,std::basic_ofstream<TCHAR>& File);
void Custom_Save (const double& In,std::basic_ofstream<TCHAR>& File);
void Custom_Save (const short In,std::basic_ofstream<TCHAR>& File);
void Custom_Save (const TCHAR In,std::basic_ofstream<TCHAR>& File);
void Custom_Save (const std::basic_string<TCHAR>& In,std::basic_ofstream<TCHAR>& File);
void Custom_Save (const bool In,std::basic_ofstream<TCHAR>& File);

template <class X>
void Custom_Save(const std::vector<X>& In,std::basic_ofstream<TCHAR>& File)
{
	int l=In.size();
	Custom_Save(l,File);
	for (int n=0;n<l;++n)
	{
		Custom_Save(In[n],File);
	}
}

template <class X,class Y>
void Custom_Save(const std::map<X,Y>& In,std::basic_ofstream<TCHAR>& File)
{
	int l=In.size();
	Custom_Save(l,File);
	std::map<X,Y>::const_iterator l=In.end();
	for (std::map<X,Y>::const_iterator i=In.begin();i!=l;++i)
	{
		Custom_Save(i->first,File);
		Custom_Save(i->second,File);
	}
}

template <class X>
void Custom_Save(const std::set<X>& In,std::basic_ofstream<TCHAR>& File)
{
	int l=In.size();
	Custom_Save(l,File);
	std::set<X>::const_iterator l=In.end();
	for (std::set<X>::const_iterator i=In.begin();i!=l;++i)
	{
		Custom_Save(*i,File);
	}
}

template <class X>
void Custom_Save(const std::list<X>& In,std::basic_ofstream<TCHAR>& File)
{
	int l=In.size();
	Custom_Save(l,File);
	std::list<X>::const_iterator l=In.end();
	for (std::list<X>::const_iterator i=In.begin();i!=l;++i)
	{
		Custom_Save(*i,File);
	}
}
Topic archived. No new replies allowed.