Use a string as a template parameter? HOW?

Started by
8 comments, last by Matei 19 years, 9 months ago
Hi! This is what i want to do:


//The class...
template <char *parameter>
class Test
{
public:
    void print() {cout << parameter << endl;};
};


...And in the main file:

Test<"GET THIS!"> instance;
instance.print(); //Should print "GET THIS!"
..But the stupid compiler just says: "Invalid expression as template parameter". I can imagine why. I guess that a hard-coded string may not have it's type as a char*, ut it's absolutelly castable! What can i do to pass a string to a template parameter WITHOUT having to create a global variable with the suitable type to make it work? Basically, what i want to do is:


AnyClass<"MyString"> anyInstance;

--Electron"The truth can be changed simply by the way you accept it.""'General failure trying to read from file' - who is General Failure, and why is he reading my file??"
Advertisement
Does it work if you use const char* instead of just char*?


If not, might as well fallback to std::string.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
No, at this moment it doesn't matter wether i put char* or const char*
Of course i should use const char* for better usage span, but right now it doesn't work in any way.

And using a string class is not good either. First of all, a template class cannot handle a struct or union as parameter, so if i want to use string, i am forced to use a *pointer* to a string.
--Electron"The truth can be changed simply by the way you accept it.""'General failure trying to read from file' - who is General Failure, and why is he reading my file??"
If you use a pointer as non-type template parameter, it must point to an object with external linkage. String literals have internal linkage (i.e. they are not visible from other compilation units), thus you cannot use them as non-type template parameters.

However, here's what you can do:

template <const char *parameter>class Test{public:    void print() {cout << parameter << endl;};};extern const char param[] = "GET THIS!";Test<param> instance;instance.print();
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Nontype template parameters are restricted to be constant integral values, or pointers to objects with external linkage. One of the reasons for this is that in C++ two instances of a template is considered the same type if and only if they have the same arguments.
In the case where the argument is a pointer, the value is the address pointed to. However, two identical string literals appearing in different source locations are not required to have the same address.
Thus Test<"X"> may or may not be of the same type as another declaration of Test<"X">, depending on wether the declarations were in different source locations or not.
So, two declarations of Test<"X"> may be of different and incompatible types!

- Neophyte

EDIT: Minor type, and noticing that Fruny seems to have got there first...
I don't think your implimentation of a template makes any sense. templates are used to change type a funtion uses, not the instance of that type. To do what I think your trying to do would be more along the lines of this:

//The class...
template
class Test
{
public:
Test(type T) {variable = T}
void print() {cout instance("GET THIS!");
instance.print(); //Should print "GET THIS!"
this seems to be deleting all the lines in the middle of my reply. whatever. Either way your using templates in an inappropriate way. templates are for when you aren't sure of the type for the function. For what your trying to do it would make more sense to make a consructor that'll takes in a string.
Quote:Original post by Anonymous Poster
templates are for when you aren't sure of the type for the function.

Not entirely true.
template <int size, typename T>class Vector{    T data[size];...};
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
Thanks Fruny. I guess that i have to live with that solution. As i see it, ther is no workaround, especially after what Neophyte mentioned.

And to Anonymous poster: I know it might look weird, but believe me it's fully legit. To easen you grief, i'm gonna explain what i'm trying to do:

I am building a singleton system, but a slight more advanced singleton than the usual type singleton
template <class T>class singleton{private:     static T instance;public:     T &operator() () {return(instance);};}



What i am aiming for is a singleton system that creates a singleton for a specific class AND a specific parameter!
My goal is to be able to use the system like this:

singleton<int> a;singleton<int> b; //same as asingleton<int,OneParameter<int,int,5> > c; //uniquesingleton<int,OneParameter<int,int,6> > d; //uniquesingleton<int,OneParameter<int,int,5> > e; //same as ccout << a() << endl; //No idea what it will print. (Probably a compiler error =)cout << b() << endl; //No idea, but the same as a. cout << c() << endl; //5cout << d() << endl; //6cout << e() << endl; //5c() = 99;cout << e() << endl; //Note that i call e, not c. But it will still print 99.


where OneParameter =

template <class T, class PT, PT parameter>class OneParameter{     public:         static T *instanciate()         {             return(new T(parameter));         }};


Of course, there are also classes like NoParameter (which is defult if no creator class is defined) and TwoParameters, etc

...and The MAIN goal is (well was) to use it like

void createAsteroid(){   Entity *newentity;   //The ONE and ONLY instance of the asteroid model!   //It's there for availability during the entire execution,   //and it's ONLY LOADED ONCE!   singleton<MD2_Model, OneParameter<MD2_Model,char*,"data/asteroid.md2"> > asteroid;    newentity = new Entity();   //Set some properties   newentity.position.set(0,0,0);   newentity.model = &asteroid(); //point to the asteroid model   //..and this is the world handler sigleton. Only one, and    //VERY easy to reach.   singleton<WorldHandler> worldhandler;   worldhandler().addUnit(newentity);};

--Electron"The truth can be changed simply by the way you accept it.""'General failure trying to read from file' - who is General Failure, and why is he reading my file??"
For that particular purpose, it's probably better to make a map-like class which loads an object if it's not found. My template knowledge is a bit rusty, but something like this should work:
template<class Id, class Value, class LoadFunc>class OneTimeLoader {private: map<Id, Value> loaded;public: Value operator[](const Id& id) {    if(loaded.find(id)==loaded.end()) {      loaded[id] = LoadFunc(id);    }    return loaded[id];  }}// Then you can use it as follows:Texture* loadTexture(string filename) {  // parse some file format  return new Texture(...);}OneTimeLoader<string, Texture*, loadTexture> textures;setTexture(textures["xyz.png"]);drawPrimitive(...);

This topic is closed to new replies.

Advertisement