   template question
  A while back, I opened this thread concerning templates and a more efficient way to design my program.  Unfortunately, it has been so long that the thread has been retired and I must start a new one.  I am interested in using the solution posted in the original thread (there is only one reply, so it's not hard to miss), but I'm having some trouble.  The solution was to create two classes and have the template class derived from the base class.  My question is, what exactly needs to go in the definition for the base class (C2DArray) and what for the derived class (T2DArray)?  Here is the abridged contents of my T2DArray class, which I am using now:template <class T>class T2DArray{    private:   unsigned int Height;   unsigned int Width;   T **Data;    public:   T2DArray(unsigned int Height, unsigned int Width);   ~T2DArray();   void Fill(T value);   template <class A, class V>   void BoundRange(const V& lowBound, const V& highBound, T2DArray<A> &output)   {  // scale the data in this object to be between lowBound and highBound,  // then store the result in output, such that the original data in  // this object is not actually changed   }};template <class T>T2DArray<T>::T2DArray(unsigned int height, unsigned int width){    Data = new T*[height];    for (unsigned int i = 0; i < height; i++)   Data = new T[width];    Height = height;    Width = width;}template <class T>T2DArray<T>::~T2DArray(){    if (!Data)   return;    for (unsigned int i = 0; i < Height; i++)    {   if (Data)   {  delete[] Data;  Data = NULL;   }    }    if (Data)    {   delete[] Data;   Data = NULL;    }    Height = 0;    Width = 0;}template <class T>void T2DArray<T>::Fill(T value){    for (unsigned int i = 0; i < Height; i++)   for (unsigned int j = 0; j < Width; j++)  Data[j] = value;}The original posted solution was:class C2DArray {}template<typename T> class T2DArray : public C2DArray {}class FooBar {  C2DArray * array;  void AsBytes( ) {    array = new T2DArray<BYTE>( width, height );  }}Being the inexperienced programmer that I am, perhaps somebody could help me determine exactly what the definitions for C2DArray and T2DArray should be.  I have tried a few haphazardly myself but I'm getting nowhere.Thanks.   
  Why use a base class for the array class? Looking at your previous question, it seems as if a base class for the file class would be better:class FileObjBase{public:   virtual void foo() = 0;   virtual void bar() = 0;};template <typename T>class FileObj : public FileObjBase{   std::vector<T> array;public:   void foo() {}   void bar() {}};Please elaborate a little on your design, if I've misunderstood you =);  Thanks for the feedback, Matsen.  I have thought about the design you mentioned as well.  That is, use the template with the file object instead of the array that holds the data.  However, the problem is that at the time the file object is to be created, the data type of the file is not yet known.  The program works by creating a file object, opening a file, and then determining the data format of the file and reading in the data.FileObj *file1 = new FileObj();FileObj *file2 = new FileObj();file1->Open("C:\\float_file.dat");file2->Open("C:\\int_file.dat");file1->GetDataType();    // returns TYPE_FLOATfile2->GetDataType();    // returns TYPE_INTAs stated before, the FileObj is currently designed like:class FileObj{    T2DArray<int> intData;    T2DArray<float> floatData;    DATA_TYPE DataType;  public:    void CreateArray()    {   // data type has been determined before this point   switch (DataType)   {  case TYPE_INT:  intData.Create(height, width);  break;  case TYPE_FLOAT:  floatData.Create(height, width);  break;   }    }   void CalcDataStatistics()    {   // data type has been determined before this point   switch (DataType)   {  case TYPE_INT:  intData.CalcStatistics();  break;  case TYPE_FLOAT:  floatData.CalcStatistics();  break;   }    }}The problem is that I'm tired of writing switch statements for functions just to determine which T2DArray object needs to be used.  I would like a completely general T2DArray object that can accept any data type, so that my code would be the following:class FileObj{    T2DArray * data;    DATA_TYPE DataType;  public:    void CreateArray()    {   // data type has been determined before this point   switch (DataType)   {  case TYPE_INT:  data = new T2DArray<int>(height, width);  break;  case TYPE_FLOAT:  data = new T2DArray<float>(height, width);  break;   }    }   void CalcDataStatistics()    {   data.CalcStatistics();    }}The switch statement would still be needed upon construction of the array, but that would be it.  I hope that makes more sense.  Let me know!Thanks.   ;Quote:The switch statement would still be needed upon construction of the array, but that would be it. I hope that makes more sense. Let me know!You're on the right track. I think you should separate the construction of the concrete file classes from the class itself. For example, you could create a separate function which creates a concrete file for you. The rest of your code will only deal with the base class, FileObjBase.FileObjBase* CreateFileObj(const std::string& fileName){  HANDLE fileHandle;  // open the file and fetch the type  switch (theFileType)  {  case TYPE_INT: return new FileObj<int>(fileHandle);  case TYPE_FLOAT: return new FileObj<float>(fileHandle);  case TYPE_MYTYPE: return new FileObjMyType(fileHandle);  case TYPE_WEIRDTYPE: return new FileObjWeirdType(fileHandle);  }  return 0; // or throw an exception for unknown type}I can't see any reason for why your solution would not work, basically you're doing the same thing but with the array class. But, I think writing your own special array class when std::vector would do the work for you, with a different design, feels more appealing. Also, as your code shows, it looks like you're stuck with switch statements all over. By separating construction from the class, you will only need one switch statement and need only to create subclasses for any type you wish to support.Hope it helps   ;Quote:Original post by StonieJA while back, I opened this thread concerning templates and a more efficient way to design my program.  Unfortunately, it has been so long that the thread has been retired and I must start a new one.  I am interested in using the solution posted in the original thread (there is only one reply, so it's not hard to miss), but I'm having some trouble.  The solution was to create two classes and have the template class derived from the base class.  My question is, what exactly needs to go in the definition for the base class (C2DArray) and what for the derived class (T2DArray)?  ...Being the inexperienced programmer that I am, perhaps somebody could help me determine exactly what the definitions for C2DArray and T2DArray should be.  I have tried a few haphazardly myself but I'm getting nowhere.Looks like you're trying to implement the "design to an interface" paradigm. It's a good pattern.The idea is to provide an interface with the base class... that is, just the virtual function definitions... including pure virtual functions, if you prefer... and then derive a template from the base and implement those functions in the template.If you're doing it that way, you may have to provide template specializations for some of the template types, like TClass<BYTE> and TClass<unsigned long>.If you don't provide all of the code for the functions in the header where the template is defined, you will use template specializations somewhere else... like a source file....It's a good design paradigm that allows a person not to have to know the template types until they are instantiated, so a pointer to the base class can be used to store the objects. Just make sure you expose all of the necessary function types (as virtual functions) in the base, and then implement them either directly in the derived template class header, or as template specializations elsewhere.HTH.Chad;Quote:Original post by VergLooks like you're trying to implement the "design to an interface" paradigm. It's a good pattern.The idea is to provide an interface with the base class... that is, just the virtual function definitions... including pure virtual functions, if you prefer... and then derive a template from the base and implement those functions in the template.Thanks for the reply!  If I were to take this approach, how would I handle the Fill method?  If I have a T2DArray<int> object, the Fill( ) method must take an int parameter, and likewise for float and any other data type. The prototype of Fill is:template<class T>void T2DArray<T>::Fill(T value);But in order to access Fill via the interface, wouldn't the interface have to be aware of the Fill( ) method? If so, how would this be done, since the interface is not aware of the variable data type "T"?class I2DArray{public:   virtual void Fill(T value) = 0;   <== ERROR?!};template <class T>class T2DArray : public I2DArray{private:unsigned int Height;unsigned int Width;T **Data;public:T2DArray(unsigned int Height, unsigned int Width) {}~T2DArray();   virtual void Fill(T value) {}};What about other functions I use in T2DArray, like BoundRange, which accepts multiple data types?  How would this be declared in the interface class?template <class A, class V>void T2DArray<T>::BoundRange(const V& lowBound, const V& highBound, T2DArray<A> &output);Does this have anything to do with the issue you mentioned about template specialization?  "If you're doing it that way, you may have to provide template specializations for some of the template types, like TClass<BYTE> and TClass<unsigned long>."Thanks for the help!   ;Quote:Original post by StonieJQuote:Original post by VergLooks like you're trying to implement the "design to an interface" paradigm. It's a good pattern.The idea is to provide an interface with the base class... that is, just the virtual function definitions... including pure virtual functions, if you prefer... and then derive a template from the base and implement those functions in the template.Thanks for the reply!  If I were to take this approach, how would I handle the Fill method?  If I have a T2DArray<int> object, the Fill( ) method must take an int parameter, and likewise for float and any other data type. The prototype of Fill is:template<class T>void T2DArray<T>::Fill(T value);But in order to access Fill via the interface, wouldn't the interface have to be aware of the Fill( ) method? If so, how would this be done, since the interface is not aware of the variable data type "T"?class I2DArray{public:   virtual void Fill(T value) = 0;   <== ERROR?!};What you'd have to do (then) is declare template functions on the base class.class I2DArray{public:   template<typename T>   void Fill(T value);};Then write the implementations as specializations in your sourcecode:EG:template<>void I2DArray::Fill(BYTE *val){}template<>void I2DArray::Fill(unsigned long val){}If you need access to the Derived type class, you'd have to declare it like this:class I2DArray{public:   template<typename DerivedType, typename T>   void Fill(DerivedType &thisobject, T value);template<>void I2DArray::Fill(T2DArray<BYTE> &thisobject, BYTE *val){// if you want to access private members of T2DArray<>, make sure and declare// the base I2DArray a friend// of course, to use this, you must pass a reference to the Derived class// to this function... if that's not necessary, the previous code would be fine}C   ;  There is a way to circumvent the strict typing on virtual function parameters. Unfortunatly, you'll lose type safety completely.class Base{protected:virtual void impl_fill(void*) = 0;public:virtual ~Base() {}template <typename T>void fill(T& value){imple_fill(static_cast<void*>(&value));}};template <typename T>class Derived : public Base{protected:virtual void impl_fill(void* ptr){T* value = static_cast<T*>(ptr);//use value.}};//usageBase* one = new Derived<int>();one->fill(1); //okayone->fill(3.14f); //Silent and deadly.A better solution probably calls for a bit of a redesign. What is this fill function being used for? Is there any way it can do it's job with a more generic parameter? Is it being called with data from the file? Could you give the template the file, and let it extract the data itself? You are already constructing the array template with the filehandle. It makes sense for that template to exract the data from the file as well.   ;  Here, it doesn't solve any of your problems but it just might help. Since you seem to have a limited number of types these files can hold, you can always go without templating it. :)enum DATA_TYPE{UNKNOWN,INT,ETC};class File{public:virtual ~File() {}virtual void calculate() = 0;virtual DATA_TYPE datatype() = 0;virtual void fill(int) { throw wrong_file_type_exception; }virtual void fill(float) { throw wrong_file_type_exception; }};class int_File : public File{private:std::vector<int> data;public:t_File(filehandle f) { /*read from f into data...*/ }virtual void calculate() {}virtual DATA_TYPE datatype() { return INT; }virtual void fill(int i) {}//don't override File::file(float)...};namespace Factory{File* create(filehandle f){DATA_TYPE dt = determine_type_of_file(f);switch (dt){case INT : return new int_File(f);case ETC:default : return 0;}}}filehandle fh = open_file(filename);File* file = Factory::create(fh);if (file) file->calculate();if (file && file->datatype() == INT) std::cout << "The file is of type INT";if (file) file->fill(1); //fineif (file) file->fill(3.14); //throwsThis doesn't scale very well to large numbers of types, and you have to add every new type to the base class. You only need the one type in the derived classes though.   ;  Now that I think about it, you CAN template it! You still need to include the fill overload in the base class.enum DATA_TYPE{UNKNOWN,INT,ETC};class File{public:virtual ~File() {}virtual void calculate() = 0;virtual DATA_TYPE datatype() = 0;virtual void fill(int) { throw wrong_file_type_exception; }virtual void fill(float) { throw wrong_file_type_exception; }};namespace ResolveType{template <typename T>DATA_TYPE resolve() { return UNKNOWN; }template <>DATA_TYPE resolve<int>() { return INT; }}template <typename T>class t_File : public File{private:std::vector<T> data;public:t_File(filehandle f) { /*read from f into data...*/ }virtual void calculate() {}virtual DATA_TYPE datatype() { return ResolveType::resolve<T>(); }virtual void fill(T) {}};namespace Factory{File* create(filehandle f){DATA_TYPE dt = determine_type_of_file(f);switch (dt){case INT : return new t_File<int>(f);case ETC:default : return 0;}}}filehandle fh = open_file(filename);File* file = Factory::create(fh);if (file) file->calculate();if (file && file->datatype() == INT) std::cout << "The file is of type INT";//assuming file is of type int...if (file) file->fill(1); //fineif (file) file->fill(3.14); //throws