Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > trait technique?

Reply
Thread Tools

trait technique?

 
 
Patrick Guio
Guest
Posts: n/a
 
      12-18-2004

Dear all,

I am not sure this is really a trait technique but I have some trouble
with the following. I would like to create a small object that carry
information about a value type (the template) and a convert member
function from string to the value type.

To do this I have defined

template <class _Tp>
struct type
{ };

and defined the specialisation I want like

template<>
struct type<char>
{
static string name() { return string("character"); }
static char convert(const string &value)
{
return *value.c_str();
}
};

template<>
struct type<int>
{
static string name() { return string("integer"); }
static int convert(const string &value)
{
return static_cast<int>(strtol(value.c_str(), 0, 10));
}
};

and so on....

Now I can define a test function

template<class T>
void test(const string & s)
{
cout << "s=" << s << "\ttype=" << type<T>::name() << " converted " <<
type<T>::convert(s) << endl;
}

and calls like

test<char>("c");
test<int>("2004");

works nicely.

Now I would like to extend to class type containers like vector

I tried

template <class T>
struct type< vector<T> >
{
static string name() { return string("vector<" + type<T>::name() + ">");
}
static vector<T> convert(const string &value)
{
// To implement for comma-separated T values
return vector<T>(0);
}
};


test< vector<int> >("1,2,3,4");

But my compiler is complaining. Any idea how to do that?


Sincerely,
Patrick

 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      12-18-2004
Patrick Guio wrote:
> I am not sure this is really a trait technique but I have some trouble
> with the following. I would like to create a small object that carry
> information about a value type (the template) and a convert member
> function from string to the value type.
>
> To do this I have defined
>
> template <class _Tp>
> struct type
> { };
>
> and defined the specialisation I want like
>
> template<>
> struct type<char>
> {
> static string name() { return string("character"); }
> static char convert(const string &value)
> {
> return *value.c_str();
> }
> };
>
> template<>
> struct type<int>
> {
> static string name() { return string("integer"); }
> static int convert(const string &value)
> {
> return static_cast<int>(strtol(value.c_str(), 0, 10));
> }
> };
>
> and so on....
>
> Now I can define a test function
>
> template<class T>
> void test(const string & s)
> {
> cout << "s=" << s << "\ttype=" << type<T>::name() << " converted " <<
> type<T>::convert(s) << endl;
> }
>
> and calls like
>
> test<char>("c");
> test<int>("2004");
>
> works nicely.
>
> Now I would like to extend to class type containers like vector
>
> I tried
>
> template <class T>
> struct type< vector<T> >
> {
> static string name() { return string("vector<" + type<T>::name() +
> ">"); }
> static vector<T> convert(const string &value)
> {
> // To implement for comma-separated T values
> return vector<T>(0);
> }
> };
>
>
> test< vector<int> >("1,2,3,4");
>
> But my compiler is complaining. Any idea how to do that?


What is your compiler complaining about?

#include <vector>

template<class T> struct A { int a; };
template<class T> struct A<std::vector<T> > { char b; };

template<class T> A<T> bar(T t);

int main() {
std::vector<int> v;
bar(v).a; // error: A<vector<int> > has no member 'a'
bar(v).b; // OK
}

Victor
 
Reply With Quote
 
 
 
 
Patrick Guio
Guest
Posts: n/a
 
      12-18-2004
On Sat, 18 Dec 2004, Victor Bazarov wrote:


> What is your compiler complaining about?
>
> #include <vector>
>
> template<class T> struct A { int a; };
> template<class T> struct A<std::vector<T> > { char b; };
>
> template<class T> A<T> bar(T t);
>
> int main() {
> std::vector<int> v;
> bar(v).a; // error: A<vector<int> > has no member 'a'
> bar(v).b; // OK
> }
>
> Victor
>


Oops, my fault, it actually works...
But I have another question: is there a way to have a container such as
map or vector that would contain pointers of different specialisations of
the template (a kind of polymorphic pointer for template class)?

Considering again the trait example

template <class _Tp>
struct type : typeClass
{
static string name();
static _Tp convert(string str);
};

template<>
struct type<bool>
{
// implementation
....
};

template<>
struct type<float>
{
// implementation
....
};

etc..

How would the second type map<int,?> myMap looks to allow the following

myMap[0] = type<bool>();
myMap[1] = type<float>();


Sincerely,

Patrick

 
Reply With Quote
 
Jonathan Mcdougall
Guest
Posts: n/a
 
      12-19-2004
Patrick Guio wrote:
> Oops, my fault, it actually works... But I have another question: is
> there a way to have a container such as map or vector that would contain
> pointers of different specialisations of the template (a kind of
> polymorphic pointer for template class)?


A container cannot contain different types. A vector of int will always
store ints. Since a class template is not a type (it is a family of
types), you cannot use it as an element type for a container.

template <class T> struct S;

std::vector< S > v; // illegal
std::vector< S* > v; // illegal

std::vector< S<int> > v; // ok
std::vector< S<int>* > v; // ok

> Considering again the trait example
>
> template <class _Tp>
> struct type : typeClass
> {
> static string name();
> static _Tp convert(string str);
> };
>
> template<>
> struct type<bool>
> {
> // implementation
> ....
> };
>
> template<>
> struct type<float>
> {
> // implementation
> ....
> };
>
> etc..
>
> How would the second type map<int,?> myMap looks to allow the following
>
> myMap[0] = type<bool>();
> myMap[1] = type<float>();


Unfortunately, I cannot think of a solution. By using a common base
class for all these, you won't be able to have your convert() function
since its return type is a template argument (except by returning
Java-like pointers to an Object class, part of your framework, of which
would derive Integer and Bool classes). The thing is, C++ is very rigid
on types and fiddling with them can be difficult sometimes.

You could have

class Converter
{
public:
virtual std::string name() = 0;
};

class IntConverter : public Converter
{
public:
virtual std::string name() { ... }
};

class BoolConverter : public Converter
{
public:
virtual std::string name() { ... }
};

int main()
{
std::vector< Converter* > v;

v.push_back(new IntConverter);
v.push_back(new BoolConverter);

// don't forget to delete them
}

Perhaps libraries such as boost have something for you, but I don't have
other ideas.


Jonathan
 
Reply With Quote
 
Nate Barney
Guest
Posts: n/a
 
      12-19-2004
Jonathan Mcdougall wrote:
> Patrick Guio wrote:
>
>> Oops, my fault, it actually works... But I have another question: is
>> there a way to have a container such as map or vector that would
>> contain pointers of different specialisations of the template (a kind
>> of polymorphic pointer for template class)?

>
>
> A container cannot contain different types. A vector of int will always
> store ints. Since a class template is not a type (it is a family of
> types), you cannot use it as an element type for a container.
>
> template <class T> struct S;
>
> std::vector< S > v; // illegal
> std::vector< S* > v; // illegal
>
> std::vector< S<int> > v; // ok
> std::vector< S<int>* > v; // ok


This may or not be useful to the OP, but you can do this:

template <class T> struct S;

template <class T2>
struct S2
{
std::vector<S<T2> > v;
std::vector<S<T2>*> v2;
};
 
Reply With Quote
 
Tom Widmer
Guest
Posts: n/a
 
      12-20-2004
Patrick Guio wrote:
> But I have another question: is
> there a way to have a container such as map or vector that would contain
> pointers of different specialisations of the template (a kind of
> polymorphic pointer for template class)?


No - you're mixing compile time with runtime. How would you use the
type<T> object without knowing what T is?

> Considering again the trait example
>
> template <class _Tp>
> struct type : typeClass
> {
> static string name();
> static _Tp convert(string str);
> };
>
> template<>
> struct type<bool>
> {
> // implementation
> ...
> };
>
> template<>
> struct type<float>
> {
> // implementation
> ...
> };
>
> etc..
>
> How would the second type map<int,?> myMap looks to allow the following
>
> myMap[0] = type<bool>();
> myMap[1] = type<float>();


map<int, typeClass> myMap;
myMap[0] = new type<bool>();
myMap[1] = new type<float>();

This assumes that typeClass looks like this:

class typeClass
{
public:
virtual ~typeClass(){}
virtual string name() const = 0;
//other type independent functions.
};

But what exactly are you doing?

Tom
 
Reply With Quote
 
Patrick Guio
Guest
Posts: n/a
 
      12-21-2004
On Mon, 20 Dec 2004, Tom Widmer wrote:

Hi Tom,

>> Considering again the trait example
>>
>> template <class _Tp>
>> struct type : typeClass
>> {
>> static string name();
>> static _Tp convert(string str);
>> };
>>
>> template<>
>> struct type<bool>
>> {
>> // implementation goes here
>> };
>>
>> etc..
>>
>> How would the second type map<int,?> myMap looks to allow the following
>>
>> myMap[0] = type<bool>();
>> myMap[1] = type<float>();

>
> map<int, typeClass> myMap;


Are you sure it is not map<int, typeClass*> ?

> myMap[0] = new type<bool>();
> myMap[1] = new type<float>();
>
> This assumes that typeClass looks like this:
>
> class typeClass
> {
> public:
> virtual ~typeClass(){}
> virtual string name() const = 0;
> //other type independent functions.
> };


I cannot compile as you say.

> But what exactly are you doing?


I am trying to write a trait for a parser class. In principle I would like
to insert an options with a method which looks like

insert(optKey, type<optType>(), "optName", "optDescription", any(optVar))

OptKey is used as the first element of map<int, typeClass*> myMap.
The second data contains a pointer to the trait class for the type
optTYpe. It should contain

* the name of the type (for example real for float, double).
This is to be used for example for a --help option (as name())
* a convert algorithm from string to optType to be used when calling the
menber function parse(optKey, optVar)
* there might be need for another trait to test the domain of validity of
the option value (for example only positive integer)

* "optName" is the option name
* "optDescription" is a short description to be used for a --help option
* any(optVar) stores the current (default) value of the var in a any
container (boost).

Do you understand what I want to do? Any more ideas?

Sincerely,

Patrick

 
Reply With Quote
 
Jonathan Mcdougall
Guest
Posts: n/a
 
      12-21-2004
Patrick Guio wrote:
> On Mon, 20 Dec 2004, Tom Widmer wrote:
> Hi Tom,
>>>Considering again the trait example
>>>
>>>template <class _Tp>
>>>struct type : typeClass
>>>{
>>>static string name();
>>>static _Tp convert(string str);
>>>};
>>>
>>>template<>
>>>struct type<bool>
>>>{
>>>// implementation goes here
>>>};
>>>
>>>etc..
>>>
>>>How would the second type map<int,?> myMap looks to allow the following
>>>
>>>myMap[0] = type<bool>();
>>>myMap[1] = type<float>();

>>
>>map<int, typeClass> myMap;

>
> Are you sure it is not map<int, typeClass*> ?


It should be, or references. With references, you'll need to make sure
the objects exist for the life of the map. Since the safest way is with
heap-allocated objects, you'd be better off with pointers.

>>But what exactly are you doing?

>
> I am trying to write a trait for a parser class. In principle I would like
> to insert an options with a method which looks like
>
> insert(optKey, type<optType>(), "optName", "optDescription", any(optVar))
>
> OptKey is used as the first element of map<int, typeClass*> myMap.
> The second data contains a pointer to the trait class for the type
> optTYpe.


So it should be (asssuming optKey is or can be converted to int)

insert(optKey, new optType_type, ...);

optType_type should be class derived from type. You could use a factory
to make it easier. The map should look like

std::map<int, type*>

What yo need is run-time polymorphism and you can't achieve that with
templates, which requires all types to be known at compile-time.

> It should contain
>
> * the name of the type (for example real for float, double).
> This is to be used for example for a --help option (as name())


That should be easy, since you can use a virtual function in class type
returning a std::string.

> * a convert algorithm from string to optType to be used when calling the
> menber function parse(optKey, optVar)


That will be impossible, though it would be nice. You will need to
design a framework la Java with a base class (such as Object) and to
return objects by pointer/reference, wherever they are allocated (heap,
static...). You can use covariant return types to make your life easier.

> * there might be need for another trait to test the domain of validity of
> the option value (for example only positive integer)


That should be easy also, just use a virtual function.

> * "optName" is the option name
> * "optDescription" is a short description to be used for a --help option
> * any(optVar) stores the current (default) value of the var in a any
> container (boost).


Ok.

> Do you understand what I want to do? Any more ideas?


C++ is not a language with which it is easy to play with types. The
kind of polymorphism you look for can only be implemented with a
hierarchy of classes.

1) Design a hierarchy of classes (Integer, Bool, Float...) all deriving
from Object.

Object has a pure virtual print(std:stream&) or something and a
static key() function which returns an invalid key. Derived classes
should override that static function.

Add an global operator<< which takes a const Object&. Call print on
it.

class Object
{
public:
virtual ~Object();
virtual void print(std:stream &stream) const = 0;
};

class Integer : public Object
{
private:
int value_;

public:
Integer(int value);
virtual void print(std:stream &stream) const
};

....

2) Design a hierarchy of classes (IntegerConverter, BoolConverter,
FloatConverter...) all deriving from Converter. Converter will have
the following pure virtual functions :

. std::string name() const
returns the converter's name (such as "bool" or "integer").

. Object *convert(std::string s) ( Object* can be covariant )
parses the string and returns the corresponding object. return
0 if parsing failed.

. static Key key()
returns a unique number (Key could be an int). this function
may be a problem to maintain, just make sure what you return is
unique to the class.

class Converter
{
public:
typedef int Key;

public:
virtual std::string name() const = 0;
virtual Object *convert(std::string s) = 0;
static Key key() { return 0; }
};

class BoolConverter : public Converter
{
public:
virtual std::string name() const;
virtual Bool *convert(std::string s); // note : covariant

static Key key() { return unique_number; }
};

....

3) Make an Option class containing a Key and a name (such as "help" or
"an_option").

class Option
{
private:
Converter::Key key_;
std::string name_;

public:
Option(Converter::Key key, std::string name);
Converter::Key key() const;
std::string name() const
};

4) Make a Types class containing a map<Key, Converter*> which will have
all the converters (and will not forget to delete them . Types
has something like get(Key k) which returns 0 or the correct
converter.

class Types
{
private:
typedef std::map<Converter::Key, Converter*> Map;
Map map_;

public:
Types();
~Types()

Converter *get(Converter::Key key);
void add(Converter::Key key, Converter *c);
};

5) Make a class Format which gets a reference to the Types object and
contains a vector of Options. Add a parse(std::string s) member
function. More on that one later.

class Format
{
private:
typedef std::vector<Option> Options;

Options options_;
Types &types_;

public:
Format(Types &types);
void add(Option o);

void parse(std::string what);
};

6) Have your main() function create a Types object and fill it with
converters. Then, create a Format object, giving it a reference to
the Types object, and fill it with options. Finally, give a string
to Format to parse it.

int main()
{
Types types;
types.add(BoolConverter::key(), new BoolConverter);
types.add(IntegerConverter::key(), new IntegerConverter);

Format format(types);
format.add(Option(BoolConverter::key(), "opt1"));
format.add(Option(IntegerConverter::key(), "opt2"));
format.add(Option(BoolConverter::key(), "opt3"));

format.parse("1 10 0");
}

Have Format:arse() fill a vector of std::string containing the tokens
of the given string. Iterate over all these tokens and the option list,
get the converter for that option and convert the token. You should get
an Object* or 0 if the conversion failed.

void Format:arse(std::string what)
{
typedef std::vector<std::string> Tokens;
Tokens tokens;

// tokenize 'what' into 'tokens'

Tokens::iterator token_itor = tokens.begin();
Options::iterator option_itor = options_.begin();

for ( ; token_itor != tokens.end() && option_itor !=
options_.end(); ++token_itor, ++option_itor)
{
Option *o = &*option_itor;
std::string t = *token_itor;

Converter *converter = types_.get(o->key());

if ( converter == 0 )
{
// type not found, that's a problem with the keys
continue;
}

Object *value = converter->convert(t);

if ( value != 0 )
{
std::cout << "got " << *value
<< " of type " << converter->name()
<< std::endl;
}
else
{
std::cout << "couldn't parse '" << t
<< "' to a " << converter->name()
<< std::endl;
}

delete value;
}
}

So with a string "1 10 0", you get

got true of type bool
got 10 of type integer
got false of type bool

and a string "1 a 0" you get

got true of type bool
couldn't parse 'a' to a integer
got false of type bool


I actually made the whole code (and it works quite well), so if you need
help with that design, I'll show you a bit more (but not too much

Jonathan
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Number of Template Parameters in Trait Types? Nephi Immortal C++ 2 02-15-2013 04:37 PM
having an alternate function based on some trait .. exactly how to do? abir C++ 2 03-28-2008 03:03 PM
can I have trait based friendship for a class abir C++ 3 02-27-2008 09:28 AM
composition (Dragon "has a" Trait) Thufir Ruby 2 11-13-2007 06:03 AM



Advertisments