Thread: RTTI - Printing template arg that could be array

  1. #1
    Magically delicious LuckY's Avatar
    Join Date
    Oct 2001
    Posts
    856

    RTTI - Printing template arg that could be array

    I can't be the first person to ask this, so I'm figuring there has got to be some solution. I have a template class that will sometimes be passed an array and other times not. Taking care of assigning for either case is the easy part: memcpy(). The hard part is trying to generically print (via ostream) for both cases.
    E.g.,
    Code:
    template <typename T>
    class Foo {
      T data;// type_info::name() could be "int", "char[14]", etc
    public:
      // ...
      friend std::ostream& operator<<(std::ostream &out, const Foo &foo) {
        if (its_an_array)// ignore this; I determine this in the constructor
          for (int i = 0; i < count; ++i)// print each array element individually
            out << data[i];// ==> error when T is not an array type <==
        else
          out << data;// print an individual variable
        return out;
      }
    };

  2. #2
    Registered User Kybo_Ren's Avatar
    Join Date
    Sep 2004
    Posts
    136
    I'm afraid that's the best way to do this.
    The only method I can think of is to search for * or [ in the name(), and output based upon the characteristics, which is presumably what you're doing.

  3. #3
    Magically delicious LuckY's Avatar
    Join Date
    Oct 2001
    Posts
    856
    Exactly. If there is a "*" I just use 'data' as a pointer and copy the passed address into it. If there is a "[" T could be something like "int[20]" so I copy all the elements into 'data.' The problem is that even though the line 'out << data[i];' will never executed if the template was not instantiated with an array, it is still compiled (unsuccessfully). It would be nice if there was a compile-time way to bypass (not compile) the code based on the typeid.

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    You'll want to look into partial template specialization so that you can "specialize" for array vs. non-array templated types.

    gg

  5. #5
    Magically delicious LuckY's Avatar
    Join Date
    Oct 2001
    Posts
    856
    Thanks for the tip, Codeplug. I had been trying partial, explicit, and any other type of specialization you can shake a stick at but what you said (you can "specialize" for array vs. non-array templated types) is a bit simplified and not exactly accurate. You can specialize for a pointer (vs. non-pointer) but I need to discern between fixed-size array (int arr[3];) and everything else (int var; int *ptr;). You can specialize for a const and for a pointer, but not for a fixed-size array. If there's a way around this caveat, please do tell (I'm hoping you've got something else up your sleeve seeing as how deep your knowledge of advanced C++ is). Here's an example of what I mean:
    Code:
    #include <iostream>
    using namespace std;
    
    class FooBase {
    };
    
    template <typename T>
    class Foo : public FooBase {
    public:
      Foo(const T &val) { cout << "<T> : Foo<" << typeid(T).name() << ">" << endl; }
    };
    
    template <typename T>
    class Foo<const T*> : public FooBase {
    public:
      Foo(const T *&val) { cout << "<T*> : Foo<" << typeid(T).name() << ">" << endl; }
    };
    
    class Shell {
      FooBase *_content;
        
    public:
      template <typename T>
      Shell(const T &value)
      : _content(new Foo<T>(value))
      {
        cout << "Shell(T) : " << typeid(T).name() << endl << endl;
      }
      
      template <typename T>
      Shell(const T *&value)
      : _content(new Foo<const T*>(value))
      {
        cout << "Shell(T*) : " << typeid(T).name() << endl << endl;
      }
      
      ~Shell() { delete _content; }
    };
    
    int main() {
      int arr[] = { 100, 200, 300 };
      const int *ptr = arr;
    
      Shell s(arr); // instantiates unspecialized Foo<int[3]>
      Shell sp(ptr);// instantiates specialized Foo<int*>
    
      return 0;
    }
    Oh yes, here's the output:
    <T> : Foo<int[3]>
    Shell(T) : int[3]

    <T*> : Foo<int>
    Shell(T*) : int
    Last edited by LuckY; 02-18-2005 at 10:14 AM. Reason: Quoted Output

  6. #6
    Magically delicious LuckY's Avatar
    Join Date
    Oct 2001
    Posts
    856
    Okay, I've figured it out (*makes fist and swings elbow backward*). Perhaps this is what you were suggesting Codeplug, but instead of simply printing as in my first example, I have a free function template with a partial specialization. The specialization is for an array and prints accordingly. Here's how my example would work:
    Code:
    // _only_ when Foo::data is a fixed-size array can this be called (count > 0)
    template <typename T>    
    std::ostream& array_output(std::ostream &out, const T *rhs, unsigned int count) {
      for (unsigned int i = 0; i < count; ++i) {
        out << rhs[i];
      return out;
    }
    
    // this is called for any other type (count == 0)
    template <typename T>    
    std::ostream& array_output(std::ostream &out, const T rhs, unsigned int count) {
      return out << rhs;
    }
    
    template <typename T>
    class Foo {
      T data;// type_info::name() could be "int", "char[14]", etc
      unsigned int elements;
    public:
      // ...
      friend std::ostream& operator<<(std::ostream &out, const Foo &foo) {
        if (its_a_char_star_or_pointer)// ignore this; I determine this in the constructor
          out << data;// print an individual variable or pointer
        else
          // DON'T do this! out << data[i];// ==> error when T is not an array type <==
          array_output(out, data, elements);//do this
          
        return out;
      }
    };
    [edit]
    "(oh, I get it; it's supposed to be like "corned beef!")"
    I mistakenly replied to Cornedbee instead of Codeplug even though he hadn't even posted in this thread. Oops.
    [/edit]
    Last edited by LuckY; 02-18-2005 at 01:06 PM.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Looks workable.

    And yes, CornedBee did indeed evolve from corned beef. My original online name was CornedBeef, but there was one place where that didn't fit, and I found CornedBee to be much cooler and unique anyway.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> ...but I need to discern between fixed-size array...
    Looks like you've solved your problem, but you may still be interrested in <boost/type_traits.hpp>. I particularly enjoy digging into how a lot of boost actually works using MSVC 6.0

    gg

  9. #9
    Magically delicious LuckY's Avatar
    Join Date
    Oct 2001
    Posts
    856
    (How embarassing. I thought I was talking to Cornedbee and not Codeplug for some reason. I fixed my previous post.)

    I have at most just scanned through a few libraries on boost.org. I haven't ever used it and still don't plan to. There's no real reason for that except that I don't want to depend on anything that isn't standard. In any case, thanks for the assistance. Now I can do this successfully:
    Code:
      int intarr[] = { 100, 200, 300 };
    
      char *str = new char[100];
      strcpy(str, "This is a pointer to a character array");  
      char str2[] = "This is a fixed-length character array";  
      std::string stlstr = "This is an STL string";
      
      Morph arr[COUNT] = { 'X', 1, "Var 3", 4444, 3.14159, UINT_MAX, wchar_t(65535), str, str2, stlstr, intarr };
      for (int i = 0; i < COUNT; ++i)
       cout << "arr[" << i << "] = " << arr[i] << endl;
        
      delete [] str;
    Of course there are still tons of things I'd like the class to do, but that's what time is for.

    Btw, the output of that looks like this:
    arr[0] = X
    arr[1] = 1
    arr[2] = Var 3
    arr[3] = 4444
    arr[4] = 3.14159
    arr[5] = 4294967295
    arr[6] = 65535
    arr[7] = This is a pointer to a character array
    arr[8] = This is a fixed-length character array
    arr[9] = This is an STL string
    arr[10] = { 100, 200, 300 }
    Last edited by LuckY; 02-18-2005 at 01:16 PM.

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    LuckY, I believe you are making a great mistake in not using Boost just because "it's not standard". If you ever need to read XML files, will you write your own parser? Or will you rather depend on some non-standard parser library (libxml2, Xerces-C, ...) that still works on just about every platform?
    Boost is the next best thing to the standard. Their libraries are well-tested, cross-platform and liberally licensed.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help printing a multi array
    By cjohnman in forum C Programming
    Replies: 4
    Last Post: 05-05-2008, 01:35 PM
  2. Need Help Please
    By YouShallNotPass in forum C++ Programming
    Replies: 3
    Last Post: 08-22-2006, 11:22 AM
  3. Class Template Trouble
    By pliang in forum C++ Programming
    Replies: 4
    Last Post: 04-21-2005, 04:15 AM
  4. printing to an array from file
    By problematic in forum C Programming
    Replies: 6
    Last Post: 05-10-2002, 05:21 AM
  5. Printing an Array to the screen
    By simhap in forum C++ Programming
    Replies: 6
    Last Post: 11-01-2001, 11:16 AM