Re: Define a mapping from class types to strings?
On Nov 22, 5:22 pm, James Kanze <james.ka...@gmail.com> wrote:
On Nov 21, 2:30 pm, alan <almkg...@gmail.com> wrote:
Hello world, I'm wondering if it's possible to implement some sort of
class/object that can performmappingfrom class types to strings?
I will know the classtypeat compile time, like so:
const char *s = string_mapper<thetype>();
Something like typeid(thetype).name() ?
However I do not know the string to be associated with the
typeat compile time,
In which case, you probably want to use std::string, instead of
char const*. This means some sort of map, with the values read
in at runtime. Possibly an std::map< std::type_info const*,
std::string >, but you still have the problem of initialization.
The real question is where the names are coming from: the format
of the source data, and how thetypeassociated with the name is
specified. External files can't contain std::type_info, only
text. And std::type_info::name() isn't portable (although if
you're only targeting one compiler, and that compiler does
something useful with this function, you may be able to use it).
What you'll probably have to do is define some sort of canonical
name, and map it to the user defined name (using std::map<
std::string, std::string >, for example).
From the user, as a string. The class associated to the string is
determined from an object of that class. An object of that class will
provide a method that will allow querying and setting the string.
and will need a way to set up themapping, to be created
at run time, possibly like so:
void foo(char* some_string_from_runtime){
string_mapper_obj<thetype>.setstring(some_string_from_runtime);
}
If you can do that, then you can use std::type_info as your keytypein the map. Or rather std::type_info const*, as a
type_info isn't copiable. You'll have to provide a comparison
function, of course, based on type_info::before().
I'm rather wondering, however, how you establish the initialmappingwhich allows you to call this function.
Possibly I might want to also mix up the string mappings too,
haha, but I suppose I can do that before they even reach the
string mapper.
Maybe something crazy like this? Would this work? (I'm purposefully
glossing over the memory management concerns)
template<class T>
const char* string_mapper_base(bool set, const char* set_to){
static const char* thestring;
if(set) internal = set_to;
return thestring;}
I think a class template would be simpler, with two different
functions, and a static class member.
template<class T>
inline const char* string_mapper(){ return string_mapper_base(false,
0);}
template<class T>
inline const char* string_mapper_set(const char* set_to){return
string_mapper_base(true, set_to);}
---
What I'm trying to do is set up a class of objects so that they have a
possibly-user-defined name. All objects will then be able to access
that name and report to the user that their name is so-and-so.
Worse, I would like to keep this open in the future so that
new classes of objects (inherited from a basetype) will
automatically have this ability too (so static member data
doesn't work too well, because I'd then have to retype that
code - I think?).
The static member is in a separate template class, so a new
instance will be created for each instantiation of the template.
The capability is independent of the target class, and will even
work for built-in types.
Something like:
template< typename T >
class StringMapper
{
public:
explicit StringMapper( std::string const& name )
{
ourName = name ;
}
operator std::string() const
{
return ourName ;
}
operator char const*() const
{
return ourName.c_str() ;
}
private:
static std::string ourName ;
} ;
template< typename T >
std::string StringMapper< T >::ourName = std::string() ;
The basic problem remains: how to you find themappingto start
with.
The user-defined class call name defaults to an empty string.
In the course of the game, when the user picks up an item (generated
by the game) he or she may use a command to call the item, so that all
other items of the same class (generated and not yet generated) will
have the same call name, even if the item is consumed somehow.
So the mapping is created by the item object itself; any object of a
class can change the mapping of that object to the class. And there
will be several classes of that item, each with this ability.
So basically, the item object itself must provide a method that will
change the class name. However, items are passed around as smart
pointers to an abstract base class; therefore, the base class must
itself provide the ability to change the call name.
Here's a solution I've thought up:
template<class T>
//I will use std::string in final code.
const char* string_mapper_base(bool set, const char* set_to){
static const char* thestring="";
if(set) internal = set_to;
return thestring;
}
template<class T>
inline const char* string_mapper_get(){
return string_mapper_base<T>(false,0);
}
template<class T>
inline const char* string_mapper_set(const char* set_to){
return string_mapper_base<T>(true, set_to);
}
class item{
public:
virtual const char* get_call_name()const=0;
virtual const char* set_call_name(const char* c)=0;
virtual void do_action()=0;
virtual void flip_something()=0;
};
template<class T>
class item_class: public item{
public:
virtual const char* get_call_name() const{
return string_mapper_get<T>();
}
virtual const char* set_call_name(const char* c){
return string_mapper_set<T>(c);
}
}
class knife: item_class<knife>{ //works in g++, but not 100% sure if
this is legal
public:
bool dull;
virtual void do_action(){
if(dull){
std::cout <<"Doing an action with a dull
knife!"<<std::endl;
} else {
std::cout << "Some nasty slicing action!" <<
std::endl;
}
}
virtual void flip_something(){
dull = true;
}
}
int
main(){
item *k1 = new knife; //I will use smart pointers in final
code.
item *k2 = new knife;
k2->flip_something();
k1->do_action();
k2->do_action();
k1->set_call_name("Some sort of knife");
std::cout << k1->get_call_name() << std::endl;
std::cout << k2->get_call_name() << std::endl;
k2->set_call_name("A renamed sort of knife");
std::cout << k1->get_call_name() << std::endl;
std::cout << k2->get_call_name() << std::endl;
delete: k1; delete k2;
k1->do_action();
k2->do_action();
}
I'm aware that std::string is much superior to char*, but this is just
a test program I hacked together to see if the concept, by itself, was
feasible (it would very well work with integers or bools, for that
matter). I'll use std::string in actual code.
Anyway I'll probably have to define mappings not only to strings, but
also to other data types - for example I'll need a bool that will
inform me if the player has already identified a class of items. This
bool can be queried by any item of that class, and will be set if the
player, for example, reads a scroll of identify and selects any item
of that class; again, any individual object of that class can set the
identified flag, and any individual object of that class must be able
to determine if its class is already identified.
Additionally, I *think* it's possible to put the dorky
string_mapper_base() function into the item_class class; the
item_class will simply define a private static string that can be
referred directly by its get_call_name() and set_call_name() methods.
Victor Bazarov wrote:
Maybe something crazy like this? Would this work? (I'm purposefully
glossing over the memory management concerns)
template<class T>
const char* string_mapper_base(bool set, const char* set_to){
static const char* thestring;
if(set) internal = set_to;
return thestring;
}
template<class T>
inline const char* string_mapper(){ return string_mapper_base(false,
0);}
template<class T>
inline const char* string_mapper_set(const char* set_to){return
string_mapper_base(true, set_to);}
I don't think there is a need to call 'set' here. Just create
a template specialisation for every type you know/can think of.
Sorry, but I have two constraints:
1. The string associated with the type must be changeable by the user
at run time.
2. I want to make it easy to add new classes in the future that will
be able to provide this feature without repeating code.
I'll look into the typeid thing; I also need to serialize the data
into a save file, so I'll need a way to map a class name to a unique
string, and a reversed map to associate that unique string to a class
that will generate an item of that class.
Finally, thanks to you both for replies; I was wondering why no one
bothered to reply.