Templated Classes question (what to do with char*)

R

Robert.Holic

Hi All (first time caller, long time listener),


I've stumbled across a problem that I have yet to figure out,
although Im sure I'll kick myself when I figure it out. Here it is:

I need to create a templated class that accepts any type. Easy
enough I though, like this:

template<typename Type>
class Foo
{
Type data;
Foo(Type d){ data = d);
~Foo(void){}
void SetData(Type d){ data = d; }
}

But what if the type is char*? Is there a way I can tell it is and
therefore strcpy into data instead of straight copy of the pointer so
that when the inputted string into SetData loses scope I can retain
it? Hopefully that makes sense. Thanks!
 
K

Kai-Uwe Bux

Hi All (first time caller, long time listener),


I've stumbled across a problem that I have yet to figure out,
although Im sure I'll kick myself when I figure it out. Here it is:

I need to create a templated class that accepts any type. Easy
enough I though, like this:

template<typename Type>
class Foo
{
Type data;
Foo(Type d){ data = d);

I would prefer initialization:

Foo ( Type d )
: data ( d )
{}
~Foo(void){}
void SetData(Type d){ data = d; }

Setter and Getter functions are usually a smell.

missing ";"

But what if the type is char*? Is there a way I can tell it is and
therefore strcpy into data instead of straight copy of the pointer so
that when the inputted string into SetData loses scope I can retain
it? Hopefully that makes sense.

Sure does: You can use a partial specialization:

template <>
class Foo< char* > {

char* data;

Foo ( char* str ) :
: data ( new char [ strlen( str ) + 1 ] )
//FIXME: maybe off by one
{
strcpy( I forgot the argument order )
// FIXME: I don't know whether strcpy takes care of terminating 0.
}

...

};

or something like that.

Note however, that

a) you would need to do this for signed char and unsigned char, too.
b) The default implementation is very likely to do the wrong thing for any
pointer type.


You might want to have a look into enable_if and disable_if from boost to
see whether you can prevent Foo<T*> to be instantiated for any T but
char/signed char/unsigned char.



Best

Kai-Uwe Bux
 
R

Robert.Holic

Hi All (first time caller, long time listener),
I've stumbled across a problem that I have yet to figure out,
although Im sure I'll kick myself when I figure it out. Here it is:
I need to create a templated class that accepts any type. Easy
enough I though, like this:
template<typename Type>
class Foo
{
Type data;
Foo(Type d){ data = d);

I would prefer initialization:

Foo ( Type d )
: data ( d )
{}
~Foo(void){}
void SetData(Type d){ data = d; }

Setter and Getter functions are usually a smell.

missing ";"
But what if the type is char*? Is there a way I can tell it is and
therefore strcpy into data instead of straight copy of the pointer so
that when the inputted string into SetData loses scope I can retain
it? Hopefully that makes sense.

Sure does: You can use a partial specialization:

template <>
class Foo< char* > {

char* data;

Foo ( char* str ) :
: data ( new char [ strlen( str ) + 1 ] )
//FIXME: maybe off by one
{
strcpy( I forgot the argument order )
// FIXME: I don't know whether strcpy takes care of terminating 0.
}

...

};

or something like that.

Note however, that

a) you would need to do this for signed char and unsigned char, too.
b) The default implementation is very likely to do the wrong thing for any
pointer type.

You might want to have a look into enable_if and disable_if from boost to
see whether you can prevent Foo<T*> to be instantiated for any T but
char/signed char/unsigned char.

Best

Kai-Uwe Bux

I dont understand, by doing the partial specialization anytime I made
a class Foo<char*> it would use that specialization and any other time
it would use Foo<Type>. Is that what you mean? So basically hard code
the template function for types that are pointers?
 
J

Jerry Coffin

Hi All (first time caller, long time listener),


I've stumbled across a problem that I have yet to figure out,
although Im sure I'll kick myself when I figure it out. Here it is:

I need to create a templated class that accepts any type. Easy
enough I though, like this:

template<typename Type>
class Foo
{
Type data;
Foo(Type d){ data = d);
~Foo(void){}
void SetData(Type d){ data = d; }
}

But what if the type is char*? Is there a way I can tell it is and
therefore strcpy into data instead of straight copy of the pointer so
that when the inputted string into SetData loses scope I can retain
it? Hopefully that makes sense. Thanks!

Sure -- you can explicitly specialize your template over type char *,
and have the explicit specialization do the right thing. You'd start
with the code above (after fixing typos, such as in the ctor), and then
add an explicit specialization:

template<>
class Foo<char *> {
// ...
};

Making this code exception safe WILL be decidedly non-trivial. Unless
you're quite experienced with exception safety, I'd recommend reading
items 8-13 of _Exceptional C++_ before you implement this code.
 
J

Jerry Coffin

[ ... ]
Sure does: You can use a partial specialization:

template <>
class Foo< char* > {

Just a minor note on terminology: this is an explicit specialization. A
partial specialization is...partial. For example:

template <class T, class U>
class Foo {
// ...
};

template <class T>
class Foo<char> {
// ...
};

In this case, the specialization is only partial -- one parameter has a
specified type, but the other is still generic. You can only partially
specialize a template that has at least two template parameters.
 
R

Robert.Holic

[ ... ]
Sure does: You can use a partial specialization:
template <>
class Foo< char* > {

Just a minor note on terminology: this is an explicit specialization. A
partial specialization is...partial. For example:

template <class T, class U>
class Foo {
// ...

};

template <class T>
class Foo<char> {
// ...

};

In this case, the specialization is only partial -- one parameter has a
specified type, but the other is still generic. You can only partially
specialize a template that has at least two template parameters.

--
Later,
Jerry.

The universe is a figment of its own imagination.

Thank you very much. You guys were extremely helpful and clear. Cheers
 
D

Daniel T.

I've stumbled across a problem that I have yet to figure out,
although Im sure I'll kick myself when I figure it out. Here it is:

I need to create a templated class that accepts any type. Easy
enough I though, like this:

template<typename Type>
class Foo
{
Type data;
Foo(Type d){ data = d);
~Foo(void){}
void SetData(Type d){ data = d; }
}

But what if the type is char*?

Don't do that. Use std::string instead.
Is there a way I can tell it is and therefore strcpy into data
instead of straight copy of the pointer so that when the inputted
string into SetData loses scope I can retain it?

Yes there is a way to do that, but it opens up the question, what about
pointers to other things?

template< typename Type >
class Foo
{
Type data;
public:
Foo( Type d ) {
data = d;
}
~Foo() { }

void SetData( Type d ) {
data = d;
}
};

template<>
class Foo < char* >
{
char* data;
public:
Foo( const char* d ): data( 0 ) {
SetData( d );
}
Foo( const Foo& o ): data( 0 ) {
SetData( o.data );
}
~Foo() {
delete [] data;
}
Foo& operator=( const Foo& o ) {
SetData( o.data );
return *this;
}
void SetData( const char* d ) {
char* tmp = data;
data = new char[ strlen( d ) ];
strcpy( data, d );
delete [] tmp;
}
};
 
D

Daniel T.

"Daniel T. said:
template<>
class Foo < char* >
{
char* data;
public:
Foo( const char* d ): data( 0 ) {
SetData( d );
}
Foo( const Foo& o ): data( 0 ) {
SetData( o.data );
}
~Foo() {
delete [] data;
}
Foo& operator=( const Foo& o ) {
SetData( o.data );
return *this;
}
void SetData( const char* d ) {
char* tmp = data;
data = new char[ strlen( d ) ];
strcpy( data, d );
delete [] tmp;
}
};

I was caught by the off-by-one error in SetData. It should be "data =
new char[ strlen( d ) + 1 ];" Yet another reason to use std::string.
 
A

Alan Johnson

Hi All (first time caller, long time listener),
I've stumbled across a problem that I have yet to figure out,
although Im sure I'll kick myself when I figure it out. Here it is:
I need to create a templated class that accepts any type. Easy
enough I though, like this:
template<typename Type>
class Foo
{
Type data;
Foo(Type d){ data = d);
I would prefer initialization:

Foo ( Type d )
: data ( d )
{}
~Foo(void){}
void SetData(Type d){ data = d; }
Setter and Getter functions are usually a smell.
missing ";"
But what if the type is char*? Is there a way I can tell it is and
therefore strcpy into data instead of straight copy of the pointer so
that when the inputted string into SetData loses scope I can retain
it? Hopefully that makes sense.
Sure does: You can use a partial specialization:

template <>
class Foo< char* > {

char* data;

Foo ( char* str ) :
: data ( new char [ strlen( str ) + 1 ] )
//FIXME: maybe off by one
{
strcpy( I forgot the argument order )
// FIXME: I don't know whether strcpy takes care of terminating 0.
}

...

};

or something like that.

Note however, that

a) you would need to do this for signed char and unsigned char, too.
b) The default implementation is very likely to do the wrong thing for any
pointer type.

You might want to have a look into enable_if and disable_if from boost to
see whether you can prevent Foo<T*> to be instantiated for any T but
char/signed char/unsigned char.

Best

Kai-Uwe Bux

I dont understand, by doing the partial specialization anytime I made
a class Foo<char*> it would use that specialization and any other time
it would use Foo<Type>. Is that what you mean? So basically hard code
the template function for types that are pointers?

If you have a general solution for any pointer type, you can use partial
specialization to get just pointer types:

template <typename Type>
class Foo
{
// implementation goes here.
} ;

template <typename Type>
class Foo<Type *>
{
// implementation for pointer types goes here.
} ;

// Not necessary if the above would work for char *.
template <>
class Foo<char *>
{
// implementation for char * goes here.
} ;
 
R

Robert.Holic

If you have a general solution for any pointer type, you can use partial
specialization to get just pointer types:

template <typename Type>
class Foo
{
// implementation goes here.

} ;

template <typename Type>
class Foo<Type *>
{
// implementation for pointer types goes here.

} ;

// Not necessary if the above would work for char *.
template <>
class Foo<char *>
{
// implementation for char * goes here.

} ;

--
Alan Johnson- Hide quoted text -

- Show quoted text -

// Not necessary if the above would work for char *.

It still seems to me that you would need to specify the char* aside
from the pointer implementation . Maybe thats what you mean and a
typo, not sure.
 
A

Alan Johnson

It still seems to me that you would need to specify the char* aside
from the pointer implementation . Maybe thats what you mean and a
typo, not sure.

The comment was just meant to imply that C++ wouldn't require you to
have the explicit specialization. I've no idea what specific problem
you are trying to solve. It may be that your solution for a Type * (for
some generic Type) would also work for a a char *, in which case you
would not need an explicit specialization for char *. If that is not
the case, then you do, of course, need the explicit specialization for
char *.
 
G

Grizlyk

(e-mail address removed) :
I need to create a templated class that accepts any type. Easy
enough I though, like this:

But what if the type is char*? Is there a way I can tell it is and
therefore strcpy into data instead of straight copy of the pointer so
that when the inputted string into SetData loses scope I can retain
it? Hopefully that makes sense. Thanks!

Any type will work with your templated class. But as i can understand, you
want to manage any object pointer point to - copy if pointer copying, delete
if pointer deleted etc. C++ type "pointer" does not allow it. There are
special classes, that can do it - common wrappers as auto_ptr or special for
"char*" classes as cstring. Use the classes as parameters instead of POD
pointers.
 
M

Michal Nazarewicz

I need to create a templated class that accepts any type. Easy
enough I though, like this:

template<typename Type>
class Foo
{
Type data;
Foo(Type d){ data = d);
~Foo(void){}
void SetData(Type d){ data = d; }
}

But what if the type is char*? Is there a way I can tell it is and
therefore strcpy into data instead of straight copy of the pointer so
that when the inputted string into SetData loses scope I can retain
it?

You can use another class as template's parameter which specifies
copying functions and specializations, ie.:

#v+
template<class T>
class foo_copier {
static void init(T &dest, const T&src) {
dest = src;
}

static void copy(T &dest, const T&src) {
dest = src;
}

static void done(T &dest) {
/* nothing */
}
};

template<>
class foo_copier<char *> {
static void init(T &dest, const T&src) {
dest = 0;
copy(dest, src);
}

static void copy(T &dest, const T&src) {
delete[] dest;
size_t len = strlen(src) + 1;
dest = new char[len];
memcpy(dest, src, len);
}

static void done(T &dest) {
delete[] dest;
}
};
#v-

and then your class would look like:

#v+
template<class T, class copier = foo_copier<T> >
class Foo {
T data;
public:
Foo(const T &d) {
copier::init(data, d);
}

~Foo() {
copier::done(data);
}

void setData(const T &d) {
copier::copy(data, d);
}
};
#v-

This way users could define their own copier classes with their own
chosen behaviour. Note however, that some compilers do not support
default template arguments (ie. the part " = foo_copier<T> ").
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top