Problems with overriding and template function

Hi all, I'm currently working on a C++ project for a 3rd year Physics course module. The project is a family tree, in which I have an abstract base class and two derived classes.

In the main I use this code to create one of the derived classes:

// Create a pointer to an (abstract base class) ftree variable with up to 50 records
ftree *a[50];

cout << "How many records would you like to input? ";
cin >> rec;

// Determine which type of record will be produced
for ( int i = 0; i < rec; ++i) {
while ( rtype != 'h' && rtype != 'l' ) {
cout << "\nRecord Type ('h'/'l'): ";
cin >> rtype;
}
// Create human / plang type record
if( rtype == 'h' ) { a[i] = new human; }
else { a[i] = new plang; }
}


My problems arise thus:

1. How can I create an accessor in the BASE class for a function in a derived class of a particular type?

e.g. In the derived class: string getDev() { return dev; }

I've tried using a virtual function in the base class to access this function but it doesnt work.

Ive tried: Virtual string getDev{};


2. The following function allows the user to search the data that has been input and return the record(s) containing the corresponding data.

The function takes arguments: findit(field to search, array to be searched, field accessor)

The function is templated so that it can operate on any type of data

template <class T1, class T2>
void findit(T1 x, T1 y, T2 z) {

if x = y { z->showInfo(); }

} // End of findit() function


Then in the main I've got:

char temp[50] = 'John';

for (int i = 0; i < rec; ++i) {
findit<string,ftree>( temp, a[i]->getName(), a[i] );
}

This compares the string 'John' with the name element of my a[i] array (as created above) but it fails and says:

error C2770: invalid explicit template argument(s) for 'void findit(T1,T1,T2)

If i change it to:

findit<string,human>( temp, a[i]->getName(), a[i] );

it says:

error C2664: 'findit' : cannot convert parameter 3 from 'ftree *' to 'human'


So I'm totally stumped!


Sorry thats a BIG post, this project is due tomorrow so any advice whatsoever would be wonderful.

Thank you very much in advance,
Gareth.
Please post actual code. It's hard to figure out what's wrong with just a paraphrase.
Here's what I can gather.

1) Need to see code, not sure what your problem is.

2) You should call findit like this:
findit( temp, a[i]->getName(), a[i] );

You do not need to specify the template types (findit<>) because the compiler can
deduce them from your actual parameter list. The only times you need to specify
the types are:
1) When calling templated constructors, argument deduction does not take place;
2) When your function's return type is templated;
3) When the compiler cannot deduce the type of the actual parameter.

1. You should spend some time over at the C++ FAQ-Lite:
http://www.parashift.com/c++-faq-lite/

Specifically, your abstract base class should define accessors like this:
1
2
3
4
5
class Abstract {
  public:
    virtual std::string getDev() const = 0;
    virtual ~Abstract() = 0;
  };

They should be overridden in the derived classes the usual looking way:
1
2
3
4
5
class Derived: public Abstract {
  public:
    std::string getDev() const { return std::string( "Hello" ); }
    ~Derived() { ... }
  };

In this example the method appears inline but it doesn't have to be. Also notice that the destructor is also virtual.


2. I usually find that pasting my error messages into Google produce some good results, or at least it tells me what I did wrong.

I think your 'findit' function is actually a class method? In any case, you might want to read up on "Partial Specialization". Sorry.
http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlcpp8l.doc/language/ref/partial_specialization.htm

Hope this helps.

[edit] man, too slow...
Last edited on
Thanks for your responses, unfortunately I still can't get either bit to work, I've been at it non stop since 9am so I really am trying! Okay, here's the whole code, hopefully sufficiently annoted for all to grasp! I've excluded the dob class files which are inherited by the ftree class cus all that works fine and I need to keep within the word limit!

// This is the abstract base class for family tree records.
// This class contains general information that would apply to any type of family tree.
// Contains accessors to the data in the inherited classes.

#ifndef FTREE_H
#define FTREE_H

// The following two bits of code eliminate the strcpy errors in the newer versions of Visual C++ Express Edition
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE

#include <string>
#include "dob.h"
using namespace std;

class ftree : public dob {

friend void userhelp();
template <class T1, class T2>
friend void myfindit(T1 x, T1 y, T2 z);

protected:
char name[50];
char parent[50];
char child[50];
dob date;
int recnum;

public:
ftree(void) {name[0] = parent[0] = child[0] = 0;} // default constructor

void setRecNum(int r) { recnum = r; }
void setDOB(dob d) { date = d;}
void setName(char *n) { strcpy(name, n); }
void setParent(char *p) { strcpy(parent, p); }
void setChild(char *c) { strcpy(child, c); }

// Create pure virtual functions that must be overwritten in the inherited class(es)
virtual void showInfo(void) = 0; // pure virtual function

// Accessor functions for private variables in this class
int getRecNum() { return recnum; }
string getName() { return name; }
string getParent() { return parent; }
string getChild() { return child; }
dob getDOB() { return date; }

// Human Class

// Assignment functions
virtual void setGender(char*) {};
virtual void setPartner(char*) {};

// Accessors THESE DONT WORK
//virtual string getGender() {};
//virtual char[1] getPartner(char*) {};

// Plang Class

// Assignment functions
virtual void setDev(char*) {};
virtual void setLev(char*) {};
virtual void setYDev(int) {};

// Accessors THESE DONT WORK
//virtual string getDev() = 0;
//virtual string getLev() {};
//virtual int getYDev() {};

}; // End of ftree class

#endif

**************************************************************
/* This header file defines the human family tree class which is inherited
from the abstract family tree class. For example, considering the Java language,
a parent could be C/C++ and a child (daughter) could be JSP.
This class is derived from the ftree abstract base class. */

#ifndef HUMANFTREE_H
#define HUMANFTREE_H

#include "ftree.h"
#include "dob.h"
#include <string>

class human : public ftree {
private:
// Declate variables
char gender[1];
char partner[50];

public:
// Set variables
void setGender(char *g) { strcpy(gender, g); }
void setPartner(char *p) { strcpy(partner, p); }

// Accessor functions
string getGender() { return gender; }
string getPartner() { return partner; }

void showInfo(void); // defined in human.cpp
};

#endif
***********************************************************
// This is the function file for human family tree class

#include "human.h"
#include <iostream>
using namespace std;

void human::showInfo(void) {

cout << "\nPersonal Information:" << endl;
cout << "\tRecord Number: " << recnum << endl;
cout << "\tName: " << name << endl;

if ( gender[1] == 'm' ) { cout << "\tGender: Male" << endl; }
else { cout << "\tGender: Female" << endl; }

cout << "\tDate of Birth: " << date << endl;
cout << "\tRelatives: " << endl;
cout << "\t\tParent: " << parent <<endl;
cout << "\t\tPartner: " << partner << endl;
cout << "\t\tChild: " << child << endl;
}
************************************************************
/* This header file defines the programming language family tree class which is
inherited from the abstract family tree class. For example, considering the
Java language, a parent could be C/C++ and a child (daughter) could be JSP.
This class is derived from the ftree abstract base class. */

#ifndef PLANG_FTREE_H
#define PLANG_FTREE_H

#include "ftree.h"
#include "dob.h"
#include <string>
using namespace std;

class plang : public ftree {

private:

char dev[20]; // Developed by
char lev[1]; // Language level (high/low)
int ydev; // Year developed

public:

// Set variables
void setDev(char *d) { strcpy(dev, d); }
void setLev(char *l) { strcpy(lev, l); }
void setYDev(int y) { ydev = y; }

// Accessors
string getDev() { return dev; }
string getLev() { return lev; }
int getYDev() { return ydev; }

void showInfo(void); // Defined in plang.cpp
};
#endif

***********************************************************
/* This code overrides the showInfo() function in the 'plang.h' header file.
The showInfo() function displays the information about a programming language
input by the user. */

#include "plang.h"
#include <iostream>
using namespace std;

void plang::showInfo(void) {


cout << "Language Information: " << endl;
cout << "\tRecord Number: " << recnum << endl;
cout << "\tLanguage: " << name << endl;

// Output words "High" or "Low" to describe corresponding language level
if ( lev[1] == 'h' ) { cout << "\tLevel: High" << endl; }
else { cout << "\tLevel: Low" << endl; }

cout << "\tDeveloped by: " << dev << endl;
cout << "\tYear Developed: " << ydev << endl;

cout << "\tRelated Languages: " << endl;
cout << "\t\tParent: " << parent << endl;
cout << "\t\tDaughter: " << child << endl;
}
***********************************************************

// This is the main code file where the data is input by and displayed to the user.

#include "ftree.h" // Include the general family tree class
#include "dob.h" // Include the date of birth class (for dd/mm/yy format)
#include "human.h" // Include the human family tree class
#include "plang.h" // Include the c-evolution family tree class
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;

int main()
{

char rtype = 'z'; // Record type
char temp[50]; // temporary input variable to store text
int rec = -1;
int year = -1; // Variable to store year of birth/development

ftree *a[50]; // Create a pointer to an ftree variable with up to 50 records

cout << "How many records would you like to input? ";
cin >> rec;

// If user does not wish to input any records terminate program
if ( rec == 0 ) { return -1; }

for ( int i = 0; i < rec; ++i) {

// Determine which type of record will be produced
while ( rtype != 'h' && rtype != 'l' ) {
cout << "\nRecord Type ('h'/'l'): ";
cin >> rtype;
}
// Create human / plang type record
if( rtype == 'h' ) { a[i] = new human; }
else { a[i] = new plang; }

// Allocate record number to record
a[i]->setRecNum(i);

cout << "\nEnter the following information:" << endl;

// Name
cout << "\nName: ";
cin >> temp;
a[i]->setName(temp);


// If human family tree member
if( rtype == 'h' ) {

// Gender
while ( rtype != 'm' && rtype != 'f' ) {
cout << "Gender(m/f): ";
cin >> rtype;
}
rtype = temp[0];
a[i]->setGender(temp);

// Date of Birth
int day = -1;
int month = -1;

while ( day < 1 || day > 31 ) {
cout << "Day of Birth (1-31): ";
cin >> day;
}
while ( month < 1 || month > 12 ) {
cout << "Month of Birth: (1-12): ";
cin >> month;
}
while ( year < 1900 || year > 2008 ) {
cout << "Year of Birth (e.g. 2000): ";
cin >> year;
}
dob dateofbirth(day,month,year); // Create dob object
a[i]->setDOB(dateofbirth);

// Parent
cout << "Parent: ";
cin >> temp;
a[i]->setParent(temp);

// Partner
cout << "Partner: ";
cin >> temp;
a[i]->setPartner(temp);

// Child
cout << "Child: ";
cin >> temp;
a[i]->setChild(temp);

}


// If c-evolution family tree member
if(rtype == 'l') {

char ltype = 'z';
// Language level
while ( ltype != 'h' && ltype != 'l' ) {
cout << "Language level ('h'/'l'): ";
cin >> ltype;
}
ltype = temp[0];
a[i]->setLev(temp);

// Developed by
cout << "Developed by: ";
cin >> temp;
a[i]->setDev(temp);

// Year developed
while ( year < 1900 || year > 2008 ) {
cout << "Year developed (e.g. 1970): ";
cin >> year;
}
a[i]->setYDev(year);

// Parent language
cout << "Parent language: ";
cin >> temp;
a[i]->setParent(temp);

// Daughter language
cout << "Daughter language: ";
cin >> temp;
a[i]->setChild(temp);

}

rtype = 'z'; // Reset record type

}

char q = 'z'; // new character for post-data input display/search questions
// Ask the user if they would like to display all records

while ( q != 'n' ) {

cout << "\nDisplay all records? (y/n) ";
cin >> q;

if ( q == 'y' ) {
for (int i = 0; i < rec; ++i) { a[i]->showInfo(); }

q = 'n'; // Set q to 'n' to terminate the loop
}

}

q = 'z'; // Reset q

// Ask the user if they wish to search the database.
// The findit() function is defined in ftree.h

while ( q != 'n' ) {
cout << "\nWould you like to search the database? (y/n): ";
cin >> q;

if ( q == 'y' ) {
cout << "\nWhat field would you like to search? (Type 'h' for help): ";
cin >> q;

if ( q == 'h' ) { userhelp(); }

if ( q == 'n' ) {
cout << "\nWhat name would you like to search for? ";
cin >> temp;
for (int i = 0; i < rec; ++i) { myfindit<string,ftree>( temp, a[i]->getName(), a[i] ); }
}

if ( q == 'g' ) {
cout << "\nWhich gender would you like to search for (m/f)? ";
cin >> q;
//for (int i; i < rec; ++i ) { findit(temp,a,a[i]->getGender) }
}

} // End of search if()

} // End of search while()

return -1; // Terminate program

} // End of main()


//The following function displays instructions on how to choose which data field to search.

void userhelp() {

cout << "\nGeneral Fields:" << endl;
cout << "\tType 'n' to search for a name" << endl;

cout << "\nHuman Fields:" << endl;
cout << "\tType 'g' to search for a gender" << endl;
cout << "\tType 'd' to search for a date of birth" << endl;
cout << "\tType 'p' to search for a parent" << endl;
cout << "\tType 'pr' to search for a partner" << endl;
cout << "\tType 'c' to search for a child" << endl;

cout << "\nProgramming Language Fields:" << endl;
cout << "\tType 'l' to search for a language level" << endl;
cout << "\tType 'dv' to search for a developer" << endl;
cout << "\tType 'y' to search for a development year" << endl;
cout << "\tType 'pl' to search for a parent language" << endl;
cout << "\tType 'dl' to search for a daughter language" << endl;


} // End of help() function

/* The following function allows the user to search the data that has been input and return
the record containing the corresponding data.

The function takes arguments: findit(field to search, array to be searched, field accessor)

The function is templated so that it can operate on any type of data*/

template <class T1, class T2>
void myfindit(T1 x, T1 y, T2 z) {

if x = y { z->showInfo(); }

} // End of findit() function
There are solutions to your problem #1 that require complete redesigns, but short of that, your simplest solution is probably a downcast.

1
2
3
4
5
6
7
// Given:
ftree* a[ 50 ];

// To call accessor getPartner() on human, you'd need to:
human* h = dynamic_cast<human*>( a[ i ] );
if( h ) 
    cout << "Partner is " << h->getPartner() << endl;


Absolutely wonderful! Thank you so much! That works like a charm, and as for the other problem it took MANY hours but I've figured it out by myself - so rewarding when that finally happens (IF it ever does!).

Thanks so much everyone, I've just got to figure out how to implement a comparison between two dates of birth, I think I'll pose that little question as a separate topic as I doubt many people will get past the horrendous amount of code above!

EDIT - Okay now that its fully implemented its coming up with a debug error when I run the program...

I added this to the code:

for ( int i = 0; i < rec; ++i) {

// Determine which type of record will be produced
while ( rtype != 'h' && rtype != 'l' ) {
cout << "\nRecord Type ('h'/'l'): ";
cin >> rtype;
}
// Create human type record
human* h = dynamic_cast<human*>(a[i]);

// Create plang type record
plang* p = dynamic_cast<plang*>(a[i]);

// Allocate record number to record
h[i].setRecNum(i+1);

p[i].setLev(temp)

etc etc...

Whats the deal? :(
Last edited on
Well, I've just finished figuring out your old code... but if you've scrapped all that I'll just go away.

OK, I'll list your major problem with that template function (and issue #1 also):
You cannot instantiate abstract classes. Hence, with the myfindit(), you need to write it as:
1
2
3
4
5
6
template <class T1, class T2>
void myfindit(T1 x, T1 y, T2 z) {

if (x == y) { z.showInfo(); }

} // End of findit() function 

Notice the proper syntax and the fact that T2 is not a pointer (since you'll be using an abstract ftree... and template functions have to have something solid to work with.
Use it thus:
 
myfindit<string,ftree&>( string(temp), a[i]->getName(), *a[i] );

No instance of ftree need be instantiated; all that is needed is a reference to an existing ftree descendant.

As to your latest problem, if that is exactly how your code appears: keep in mind that a human is still a human even if you call it a plang, and vice-versa. Hence, don't cast a human to a plang, and don't cast a plang to a human. They are not compatible, so keep them separate.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
switch (rtype) {

  case 'h': {
    human *h = dynamic_cast<human*>(a[i]);
    h[i].setRecNum(i+1);
    ...
    }
    break;

  case 'l': {
    plang* p = dynamic_cast<plang*>(a[i]);
    p[i].setLev(temp);
    ...
    }
    break;

  default:
    ...
  }


Finally, please start using code tags.

Good luck.
Hey, sorry, I've taken note about using code tags for future reference!

Thanks for your help - I spent a long time implementing this as my program is now pretty big. It sort of worked but for some reason I couldn't stop it getting errors after inputting more than one record.

Also, the fact that my find function has to be outside the switch while the human/plang instances are inside the switch means that I cant use

myfindit<string,ftree&>( string(temp), h[i]->getName(), *a[i] );

since h nor p no longer exist... this seems far too big a problem to overcome in one fleeting night, so thank you VERY much for your help, but I'm gonna have to just leave it and write about what I ATTEMPTED to achieve in the report.

The bottom line is I still have a find function, but it can only access variables within the base class itself... unless there is a way to access the variables in the inherited classes from the base class as I originally enquired?

Thanks again all for your replies, thats a LOT of code up there, I really appreciate your help!

Gareth.
Heh, sorry for the grief. You are tackling a lot at once.

You can still use myfindit(), since getName() is inherited from ftree.

As for large projects, don't worry about it. I just finished the first draft of a little utility (in Delphi) for playing with the Desktop wallpaper that contains 25 code files (most less than 300 lines, a couple around 1200 lines, and the main program a little over 4400), 6 form definition files, 4 resource files, and a slew of other small files (autorun.inf, config files, project definition files, etc).

As long as you keep things organized and separate things separate, it really isn't too much at all.

:-)
Topic archived. No new replies allowed.