tags:

views:

507

answers:

6

I want a class which takes two parameters in its constructor. The first can be either an int, double or float, so , and the second is always a string literal "my string", so I guess const char * const.

Can anyone give me some compilable code which declares a simple class template as described and declares an object of that class?

Thanks

+6  A: 

Sorry, C++ does not currently support the use of string literals (or real literals) as template parameters.

But re-reading your question, is that what you are asking? You cannot say:

foo <"bar"> x;

but you can say

template <typename T>
struct foo {
   foo( T t ) {}
};

foo <const char *> f( "bar" );
anon
Do C++0x variadic templates support integral arguments? You could probably do something nasty with representing strings as lists of chars.
Pete Kirkham
maybe you might hack around with 4 char long literals (supported by all compilers?) and variadic templates -- it would make a nice answer on SO but it would look ugly in production code :)
Gregory Pakosz
Neil, that is looking very good, but since I am dumb, can you correct this code? I tried to adapt yours to take two params, which ought to be simple enough ...<pre>template<typename E, typename S> class Event {public: Event(E eventId, S eventName); // constructorprivate: E _eventId; char _eventName[MAX_EVENT_NAME_LENGTH + 1];};</pre>and try to instantiate with<pre> enum enum1 {eventA, eventB}; Event<enum1, const char *> testEventA(eventA, "A");</pre>but I get compiler errors - see next comment, running out of space
Mawg
sh*t!! How to format comments, if PRE doesn't work?- trying to instantiate ‘template<class E, class S> class - initializer expression list treated as compound expression - invalid conversion from ‘const char*’ to ‘int’ test_fsm.cpp - invalid type in declaration before ‘(’ token test_fsm.cpp - template argument for ‘template<class E, class S> class Event’ uses local type ‘testFsmClasses::TesEventConstructor()::enum1’
Mawg
@Mawg Edit your original question. ANd never try to use HTML to format the question or your answers. Use those little buttons above the editor.
anon
Hi, Neil, Ideally I would have preferred an example with two params, one being an enum value and the other a string, being the event name.By hacking your code, I go it to work - but needed to use a #define as I can't make it work otherwise - because you can't pass a string literal as a template parameter. Here's what I cam up with: (next comment, not enough space here)
Mawg
template <typename EventId, typename EventName> class Event { public: Event(EventId ventId, EventName eventName ) {} }; enum event{eventA, eventB}; #define DECLARE_EVENT(w, x, y, z) Event<x, const char *> w(y, z ) int main(int argc, char *argv[]) { DECLARE_EVENT(e1, event, eventA,"event_A"); DECLARE_EVENT(e2, event, eventA,"event_B"); return 0; }
Mawg
hmm, I though that four spaces at the start of each line would make it a pre-formatted code block (????)
Mawg
Neil, sorry to be so dumb - but the buttons - are they there for comments? I do see them for answers.and, from what I read - from the buttons - four spaces ought to PRE CODE - sorry to be so dumb...
Mawg
+2  A: 

I want a class which takes two parameters in its constructor. The first can be either an int, double or float, so , and the second is always a string literal "my string"

template<typename T>
class demo
{
   T data;
   std::string s;

   public:

   demo(T d,std::string x="my string"):data(d),s(x) //Your constructor
   {
   }
};

I am not sure but is this something what you want?

Prasoon Saurav
Might want to make some part of that class public, or just use `struct` in examples, which greatly reduces clutter. :)
Roger Pate
@Roger: Thanks, corrected it :)
Prasoon Saurav
That is looking very close! But how can I pass a different "my string" as constructor parameter each time I instantiate?
Mawg
@mawg:You need not _pass_ "my string". Just try this:demo<int> d(1); // second parameter is "my string" by defaultdemo<double> d1(1.4); // second parameter is again "my string" by default
Prasoon Saurav
@mawg: Read about default argument constructors here: http://people.cs.vt.edu/~kafura/cs2704/default.html
Prasoon Saurav
+1  A: 

EDIT: ok the title of your question seems to be misleading

"I want a class which takes two parameters in its constructor. The first can be either an int, double or float, so , and the second is always a string literal "my string", so I guess const char * const."

It looks like you're trying to achieve:

template<typename T>
class Foo
{
  public:
  Foo(T t,  const char* s) : first(t), second(s)
  {
    // do something
  }

  private:
  T first;
  const char* second;

};

This would work for any type, for the first parameter: int, float, double, whatever.

Now if you really want to restrict the type of the first parameter to be only int, float or double; you can come up with something more elaborate like

template<typename T>
struct RestrictType;

template<>
struct RestrictType<int>
{
  typedef int Type;
};

template<>
struct RestrictType<float>
{
  typedef float Type;
};

template<>
struct RestrictType<double>
{
  typedef double Type;
};

template<typename T>
class Foo
{
  typedef typename RestrictType<T>::Type FirstType;

  public:
  Foo(FirstType t,  const char* s) : first(t), second(s)
  {
    // do something
  }

  private:
  FirstType first;
  const char* second;

};

int main()
{
  Foo<int> f1(0, "can");
  Foo<float> f2(1, "i");
  Foo<double> f3(1, "have");
  //Foo<char> f4(0, "a pony?");
}

If you remove the comment on the last line, you'll effectively get a compiler error.


String literals are not allowed by C++2003

ISO/IEC 14882-2003 §14.1:

14.1 Template parameters

A non-type template-parameter shall have one of the following (optionallycv-qualified) types:

— integral or enumeration type,

— pointer to object or pointer to function,

— reference to object or reference to function,

— pointer to member.

ISO/IEC 14882-2003 §14.3.2:

14.3.2 Template non-type arguments

A template-argument for a non-type, non-template template-parameter shall be one of:

— an integral constant-expression of integral or enumeration type; or

— the name of a non-type template-parameter; or

— the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference; or

— a pointer to member expressed as described in 5.3.1.

[Note:A string literal (2.13.4) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.

[Example:

template<class T, char* p> class X { 
  //... 
  X(); 
  X(const char* q) { /* ... */ } 
}; 

X<int,"Studebaker"> x1; //error: string literal as template-argument 
char p[] = "Vivisectionist"; 
X<int,p> x2; //OK 

—end example] —end note]

And it looks like it's not going to change in the upcoming C++0X, see the current draft 14.4.2 Template non-type arguments.

Gregory Pakosz
Gregory, I have read similar explanation elsewhere, but not so clear.Your code looks like I can understand it, and it should work - but it gives error under Linux with G++ (in fact, I have compile errors with two good looking suggestions above - can I really be so dumb?)
Mawg
see my edited answer
Gregory Pakosz
+3  A: 

Further from Neil's answer: one way to using strings with templates as you want is to define a traits class and define the string as a trait of the type.

#include <iostream>

template <class T>
struct MyTypeTraits
{
   static const char* name;
};

template <class T>
const char* MyTypeTraits<T>::name = "Hello";

template <>
struct MyTypeTraits<int>
{
   static const char* name;
};

const char* MyTypeTraits<int>::name = "Hello int";

template <class T>
class MyTemplateClass
{
    public:
     void print() {
         std::cout << "My name is: " << MyTypeTraits<T>::name << std::endl;
     }
};

int main()
{
     MyTemplateClass<int>().print();
     MyTemplateClass<char>().print();
}

prints

My name is: Hello int
My name is: Hello
Amit Kumar
This looks interesting. Can it be massages to pass the string as a parameter and show and example of declaring an object?
Mawg
@mawg As per your example under Niel's answer (really you should update your question, saying "Update: This is what I want"), you want to differentiate between classes based (only) on a string template parameter. This is impossible (see Niel's answer). But if you want to differentiate between classes based on EventId, then you can use the EventName as a field in the trait class as per my answer.
Amit Kumar
@mawg Also see my another newly added answer.
Amit Kumar
A: 

a string literal "my string", so I guess const char * const

Actually, string literals with n visible characters are of type const char[n+1].

#include <iostream>
#include <typeinfo>

template<class T>
void test(const T& t)
{
    std::cout << typeid(t).name() << std::endl;
}

int main()
{
    test("hello world"); // prints A12_c on my compiler
}
Fred
+1  A: 

Based on your comments under Niel's answer, another possibility is the following:

#include <iostream>

static const char* eventNames[] = { "event_A", "event_B" };

enum EventId {
        event_A = 0,
        event_B
};

template <int EventId>
class Event
{
public:
   Event() {
     name_ = eventNames[EventId];
   }
   void print() {
        std::cout << name_ << std::endl;
   }
private:
   const char* name_;
};

int main()
{
        Event<event_A>().print();
        Event<event_B>().print();
}

prints

event_A
event_B
Amit Kumar
Yes, that works, and is worth considering. What worries me, though, is that it is possible to make a mistake and get them out of alignment. It would be 'better' to pass both the enum value and the string as a pair (but, of course, you can still make mistakes there too)
Mawg