C ++ uses a string to call a member function of an object
I have a superclass Entry
and subclasses MusicAlbum
, Book
and Film
. Instances of these subclasses are stored according to the element name, i.e. Book1
... The name and type of all these instances are stored in a vector cat_vector
, which is a vector of class objects libCatalogue
, which simply stores the name and type:
class libCatalogue{
std::string name;
std::string type;
public:
libCatalogue(std::string name, std::string type);
std::string getname();
std::string gettype();
};
libCatalogue::libCatalogue(std::string name, std::string type) :name(name), type(type) {};
std::vector <libCatalogue> cat_vector;
The entries in the vector are made in the constructor, for example.
MusicAlbum::MusicAlbum(std::string a, std::string b, std::string borrower)
: name(a), artist(b), Entry(borrower){
cat_vector.push_back(libCatalogue(name, "MusicAlbum"));
Each subclass has a member function called printdetails()
. I want to use a loop for every record in cat_vector
and print the details of the record, but the following doesn't work:
int no = 1;
for (auto it = begin(cat_vector); it != end(cat_vector); ++it)
{
std::string name_ = it->getname();
std::string type_ = it->gettype();
std::cout << "Entry no. " << no << std::endl;
std::cout << "Name: " << name_ << std::endl;
std::cout << "Type: " << type_ << std::endl << std::endl;
if (type_ == "MusicAlbum"){
name_.printdetails(); //print using MusicAlbum member function
}
//etc...
no++;
I know because it name_
is a string and not an object of any of the classes I want to call, but I haven't been able to find a way to transform it yet. Is there a way to tell the compiler what name_
is the object of one of the subclasses?
source to share
C ++ is a statically typed compiled language . You cannot create variables on the fly. Fortunately, for cases like this, the work around is to use a lookup table. Typically this is achieved with a map where the key will be a string and the value will be the function you want to bind and call the specific string.
I know this is because name_ is a string and not an object of any of the classes I want to name, but I haven't been able to find a way to convert it so far. Is there a way to tell the compiler that name_ is referring to an object of one of the subclasses?
when you qualify a member, the name of the member is defined in relation to the type of the variable, not in relation to the content. So the call name_.printdetails()
means that you are trying to call a member function on an instance of type std :: string, but std :: string does not have a member function named printdetails.
A simple example to expand on the above idea
struct Spam
{
enum { NO_OF_FUNCTIONS = 4 };
Spam()
{
lookup_callback["Foo1"] = std::bind(&Spam::foo1, this);
lookup_callback["Foo2"] = std::bind(&Spam::foo2, this);
lookup_callback["Foo3"] = std::bind(&Spam::foo3, this);
lookup_callback["Foo4"] = std::bind(&Spam::foo4, this);
}
void foo1() { std::cout << "Foo1" << std::endl; }
void foo2() { std::cout << "Foo2" << std::endl; }
void foo3() { std::cout << "Foo3" << std::endl; }
void foo4() { std::cout << "Foo4" << std::endl; }
void call(std::string name)
{
if (lookup_callback.count(name) > 0)
{
lookup_callback[name]();
}
else
{
std::cerr << "Invalid Function Call" << std::endl;
}
}
std::map<std::string, std::function<void(void)>> lookup_callback;
};
// Driver program to test above functions
int main()
{
std::string name;
Spam spam;
for (std::cin >> name; name != "quit"; std::cin >> name)
{
spam.call(name);
}
}
source to share
If you pass an instance Entry
around, otherwise you won't have problems because you will be able to call:
it->entry->print_details();
If you don't want to get LibCatalogue
familiar with instances Entry
, you can create a new class called Printable
or something similar. This class will hold Entry
and LibCatalogue
. All the data required for printing will be specified in the "Printable" class. This way you could call both:
it->printable->print_details(); entry->printable->print_details();
source to share
To expand on @ abhijit's answer, I often use static tables if the content is small and usage is low.
// Typedef for a the function pointer
typedef void (*Function_Pointer)(void);
struct key_function_entry
{
const char * key_text;
Function_Pointer function;
};
void Process_Foo1_Request(void);
void Process_Bach_Request(void);
void Process_Eat_Request(void);
static const key_function_entry delegation_table[] =
{
{"foo1", Process_Foo1_Request},
{"Bah", Process_Bah_Request},
{"eat", Process_Eat_Request},
};
static const unsigned int delegation_entries =
sizeof(delegation_table) / sizeof(delegation_table[0]);
void Process_Request(const std::string& request)
{
for (unsigned int i = 0U; i < delegation_entries; ++i)
{
if (request == delegation_table[i].key_text)
{
delegation_table[i].function(); // Execute the associated function.
break;
}
}
}
The advantage is that the table is static (one instance) and constant, so it can be put into persistent memory. The table does not need to be created at runtime (for example std::map
). The code refers to a compile-time table. (This is an embedded system, saving memory, or putting stuff in persistent memory.)
For a small number of records, linear search may be faster than a std::map
.
For large records or very large number of hits, it is preferable std::map
.
source to share