I am new in c++, I am trying to make a very simple CRUD program.
In this example, a client bought many things in a store, and this store has the information about the things that this client bought. I called it Inventory.
The store, wants to print a Report for every client. In the code below, the main just has one Inventory, just for sample.
The problem is: When I want to print the Report, I have to get the data from the client, but without lose encapsulation. I mean, I want that no class can modify the content of the inventory.
What I am trying to do is convert the map into a vector (I need something to sort the data) and pass this vector (dinamically allocated).
I am allocating this vector at the class Inventory but who is deleting
is the class Report, this is not the correctly way to do things, but I do not know how to pass this information without doing like this.
Anyway, the report class can get the pointer to a Book, and use its set function or point to other Book. Again, I do not know how to do it correctly.
Can someone give me a tip what I have to do in this case ?
Thanks.
Sorry for the long code.
Main:
int main(void)
{
Inventory i;
Report r(i);
i.addBook("Foo Bar I");
i.addBook("Foo Bar II");
r.generateReport();
return 0;
}
Class Report in .h:
class Report
{
private:
Inventory* i;
public:
Report(Inventory& i);
void generateReport();
};
Class Report in cpp:
Report::Report(Inventory& i)
{
this->i = &i;
}
void Report::generateReport()
{
ofstream out ("Report.txt");
out << "Books: " << endl;
vector<pair<int, Book *>> * b = i->getBooks();
for(pair<int, Book *> p : *b)
{
out << p.first << ": " << p.second.getName() << endl;
}
out << endl;
delete b;
out.close();
}
Class Inventory in .h:
class Inventory
{
private:
map<int, Book *> books;
public:
void addBook(int code, const string& name);
vector<pair<int, Book *>> * getBooks();
};
Class Inventory in .cpp:
void Inventory::addBook(int code, const string& name)
{
books.insert(pair<int, Book *>(code, new Book(name)));
}
vector<pair<int, Book *>> * Inventory::getBooks()
{
return new vector<pair<int, Book *>>(books.begin(), books.end());
}
Your inventory class should have this interface:
class Inventory
{
private:
map<int, Book *> books;
public:
using const_iterator = std::map<int, Book*>::const_iterator;
void addBook(int code, const string& name);
const_iterator begin() const { return books.begin(); }
const_iterator end() const { return books.end(); }
};
no reason to create a copy of the map into a vector! that is inefficient and not reason for doing it.
Rewrite the generateReport to work with this interface as follows:
for(const auto &p : *i)
{
out << p.first << ": " << p.second.getName() << endl;
}
out << endl;
With your approach, the trick to transform the map into a vector introductes only an additional dependency: the report has to know about the internals of the inventory.
I'd rather recommend to go the way of the vistitor design pattern: the goal is to separate the algorithm (producing the report, the visitor) from the datastructure you explore (the inventory and its items).
class Item { ...}; // Your object structure
class Book : public Item { ... };
class Inventory { ...};
class Visitor { ... }; // Algorithms that shall be executed on structure
class Report : public Visitor {...};
This could also simplify the implementation of your inventory, as you'd no longer need to foresee different containers for different kind of items (provided they all inherit from some common element base class).
Theres is plenty of litterature on this pattern. I recommend you Design patterns, elements of reusable object oriented software from Gamma & al: its the orginal textbook, and guess what, the demo code shows an inventory with a pricing visitor ;-)
Here a naive on-line example based on your problem, to illustrate how it could work.
Related
Hi I need some clarification on some code since google did not help nor did any of my C++ books. Ok I have a base class,which I derived 2 other classes, but I did not post the code for them here since they don't have anything to do with my question:
ifndef BASEBALLPLAYER_H
#define BASEBALLPLAYER_H
#include <iostream>
using namespace std;
class BaseBallPlayer{ //abstract class meaning no instance can be made for it, which is why we access it via derived classes
//data members
protected:
string name;
int height;
int weight;
public:
BaseBallPlayer();
BaseBallPlayer(string nam, int wight, int hight);
string get_Name();
virtual void print_player()=0; //
virtual void load_player(ifstream & read) = 0; //
virtual ~BaseBallPlayer();
};
Then an ArrayList.h file:
#ifndef ARRAY_LIST_H
#define ARRAY_LIST_H
#include <string>
using namespace std;
const static int MAX_INIT = 99;
template <class elemType>
class ArrayList {
private:
int n_element;
elemType * data;
public:
~ArrayList();
ArrayList(int n = MAX_INIT);
ArrayList(const ArrayList<elemType> &);
const ArrayList<elemType> & operator=(const ArrayList<elemType> &);
void MakeEmpty();
bool IsFull() const;
int LengthIs() const;
void RetrieveItem(elemType &, bool&);
void InsertItem(elemType);
void DeleteItem(elemType);
void ResetList();
bool IsLastItem();
void GetNextItem(elemType &);
};
Now my question lies in this new class, its a standalone class, that I made in the PlayerDatabase.h file:
#ifndef PLAYERDATABASE_H
#define PLAYERDATABASE_H
#include <iostream>
#include "BaseBallPlayer.h"
#include "ArrayList.h"
using namespace std;
class PlayerDatabase{
private:
ArrayList<BaseBallPlayer *> teamArrayList; // I do NOT Understand what this means?
public:
};
#endif
I DO NOT understand what the private member in my PlayerDatabse.h file means or entails? And what can I do with it? My instructor told us to usethat template with a pointer but I didn't have a chance to ask what it does/mean?
So I'm not exactly sure what you end goal is here, but let me take a quick guess at what you might be trying to do:
So you mentioned you have 2 subclasses of BaseBallPlayer, let's say they are MajorLeaguePlayer and MinorLeagePlayer This private Array list teamArrayList can be used to hold any combinations of your base class (BaseBallPlayer -- in your case MajorLeaguePlayer and MinorLeaguePlayer).
You usually want to leave this teamArrayList private so that it will only be safely modified by your public methods. So for example you might want to have a constructor for PlayerDatabase that takes an argument of ArrayList<BaseBallPlayer *>
PlayerDatabase(ArrayList<BaseBallPlayer *> players) {
// set the private teamArray list in the constructor
teamArrayList = players;
}
or you might want to sort the teamArray list based off of one of the properties in the base class BaseBallPlayer
// or another one sorting for weight or name
void sortPlayerListByHeight() {
// sort the private team array list
teamArrayList = ... // sort by height
}
Maybe you want to get the number of players in the list, or find the first or last player in the list. In general it's good practice to use public methods to access/modify private data members (i.e. teamArrayList)
int numberOfPlayersInList() {
return teamArrayList.LengthIs();
}
BaseBallPlayer getFirstPlayerInList() {
return teamArrayList.RetrieveItem.... // get the first item in the list
}
Then from another class you could construct some of your subclass objects and construct your player database:
ArrayList<BaseBallPlayer *> playerList = new ArrayList<>();
playerList.add(new MinorLeagePlayer("minorName", 180,70);
playerList.add(new MajorLeaguePlayer("majorName", 200, 72);
PlayerDatabase db = new PlayerDatabase(playerList);
// now you can do some operations on the PlayerDatabase knowing that the list will contain all objects that can use methods from the Base Class (`BaseBallPlayer`)
db.getFirstPlayerInList().print_player(); // method from your base class implemented in your subclasses
int numberOfPlayers = db.numberOfPlayersInList();
// do something with the number of players in the list
This is pseudo code, but hopefully it will help you get the idea.
Edit related to our comments:
ArrayList<BaseBallPlayer *> *playerList = new ArrayList<BaseBallPlayer *>();
MinorLeaguePlayer *aPlayer = new MinorLeaguePlayer();
playerList->InsertItem(aPlayer);
I've included a couple of simple cout statements in the ArrayList constructor, the MinorLeaguePlayer constructor, and the InsertItem method to output some information when they are called (I'm assuming you already have implementations of these methods):
ArrayList constructor: n_element = n;
cout << "Constructing the arrayList, n_element = " << n_element << endl;
MinorLeaguePlayer constructor: cout << "Constructing a minor league player" << endl;
InsertItem method: cout << "Inserting an item into the ArrayList" << endl;
Here's the output after building and running the above code:
Constructing the arrayList, n_element = 99
Constructing a minor league player
Inserting an item into the ArrayList
Edit 2 related to further comments:
Methods which take a reference to the elemType
Example:
// this will basically just going to "hold" the BaseBallPlayer fetched
// from GetNextItem
BaseBallPlayer *bPlayer;
playerList->GetNextItem(bPlayer);
cout << "Weight after fetching from list = " << bPlayer->weight << endl;
The implementation of GetNextItem is going to look something like this:
void ArrayList<elemType>::GetNextItem(elemType& player) {
// implementation to get the next item in the array list
player = //... set the "NextItem" to the BaseBallPlayer reference we passed into this method
}
Edit 3: print_player() polymorphism:
MinorLeaguePlayer *minPlayer = new MinorLeaguePlayer();
MinorLeaguePlayer *min2Player = new MinorLeaguePlayer();
MajorLeaguePlayer *majPlayer = new MajorLeaguePlayer();
MajorLeaguePlayer *maj2Player = new MajorLeaguePlayer();
playerList->InsertItem(minPlayer);
playerList->InsertItem(min2Player);
playerList->InsertItem(majPlayer);
playerList->InsertItem(maj2Player);
BaseBallPlayer *fetchedPlayer;
for (int i = 0; i < 4; i++) {
playerList->GetNextItem(fetchedPlayer);
fetchedPlayer->print_player();
}
Notice that you have to get the reference to the fetchedPlayer prior to calling the print_player() method on it
playerList->GetNextItem(fetchedPlayer);
fetchedPlayer->print_player();
This is because of the way your GetNextItem method is structured to place the reference to the nextItem in the passed in BaseBallPlayer.
Here's another example where the method would return the BaseBallPlayer instead of setting it by reference:
method stub (in ArrayList):
elemType getNextItemReturn();
for loop:
for (int i = 0; i < 4; i++) {
playerList->getNextItemReturn()->print_player();
}
Here's a link to a quick discussion asking which is more efficient:
Which is more efficient: Return a value vs. Pass by reference?
And the output with the example cout statements:
Constructing the arrayList, n_element = 99
Inserting an item into the ArrayList
Inserting an item into the ArrayList
Inserting an item into the ArrayList
Inserting an item into the ArrayList
Printing From MINOR League Player
Player Name: MinorLeagueName Weight = 22
Printing From MINOR League Player
Player Name: MinorLeagueName Weight = 22
Printing From MAJOR League Player
Player Name: MajorLeaguePlayer Weight = 33
Printing From MAJOR League Player
Player Name: MajorLeaguePlayer Weight = 33
The mock implementations of print_player():
void MinorLeaguePlayer::print_player() {
cout << "Printing From MINOR League Player" << endl;
cout << "Player Name: " << name << " Weight = " << weight << endl;
}
void MajorLeaguePlayer::print_player() {
cout << "Printing From MAJOR League Player" << endl;
cout << "Player Name: " << name << " Weight = " << weight << endl;
}
teamArrayList is a list of pointers to BaseBallPlayer objects. By using pointers, it can by polymorphic -- the pointers can point to any class derived from BaseBallPlayer.
This is the common way to use an abstract base class. You can then use it to call the methods in the BaseBallPlayer class, and the implementations in the appropriate derived classes will be used.
I want to store functions with similar signature in a collection to do something like this:
f(vector<Order>& orders, vector<Function>& functions) {
foreach(process_orders in functions) process_orders(orders);
}
I thought of function pointers:
void GiveCoolOrdersToBob(Order);
void GiveStupidOrdersToJohn(Order);
typedef void (*Function)(Order);
vector<Function> functions;
functions.push_back(&GiveStupidOrdersToJohn);
functions.push_back(&GiveCoolOrdersToBob);
Or polymorphic function objects:
struct IOrderFunction {
virtual void operator()(Order) = 0;
}
struct GiveCoolOrdersToBob : IOrderFunction {
...
}
struct GiveStupidOrdersToJohn : IOrderFunction {
...
}
vector<IOrderFunction*> functions;
functions.push_back(new GiveStupidOrdersToJohn());
functions.push_back(new GiveCoolOrdersToBob());
Premise:
The design you propose will work, but using regular function pointers will limit you considerably in the kind of callbacks you can register, and although more powerful, the approach based on inheritance from a fixed interface is more verbose and requires more work for a client to define callbacks.
In this answer I will first show some examples of how to use std::function for this purpose. The examples will pretty much speak for themselves, showing how and why using std::function brings advantages as opposed to the kind of solutions you outlined.
However, a naive approach based on std::function will also have limitations of its own, which I am going to list. This is why I eventually suggest you to have a look at Boost.Signals2: it is a pretty powerful and easy-to-use library. I will address Boost.Signals2 at the end of this answer. Hopefully, understanding a simple design based on std::function first will make it easier for you to grasp the more complex aspects of signals and slots later on.
Solution based on std::function<>
Let's introduce a couple of simple classes and prepare the ground for some concrete examples. Here, an order is something which has an id and contains several items. Each item is described by a type (for simplicity, here it can be either a book a dvd), and a name:
#include <vector>
#include <memory>
#include <string>
struct item // A very simple data structure for modeling order items
{
enum type { book, dvd };
item(type t, std::string const& s) : itemType(t), name(s) { }
type itemType; // The type of the item
std::string name; // The name of the item
};
struct order // An order has an ID and contains a certain number of items
{
order(int id) : id(id) { }
int get_id() const { return id; }
std::vector<item> const& get_items() const { return items; }
void add_item(item::type t, std::string const& n)
{ items.emplace_back(t, n); }
private:
int id;
std::vector<item> items;
};
The heart of the solution I am going to outline is the following class order_repository, and its internal usage of std::function to hold callbacks registered by clients.
Callbacks can be registered through the register_callback() function, and (quite intuitively) unregistered through the unregister_callback() function by providing the cookie returned by registered_callback() upon registration:
The function than has a place_order() function for placing orders, and a process_order() function that triggers the processing of all orders. This will cause all of the registered handlers to be invoked sequentially. Each handler receives a reference to the same vector of placed orders:
#include <functional>
using order_ptr = std::shared_ptr<order>; // Just a useful type alias
class order_repository // Collects orders and registers processing callbacks
{
public:
typedef std::function<void(std::vector<order_ptr>&)> order_callback;
template<typename F>
size_t register_callback(F&& f)
{ return callbacks.push_back(std::forward<F>(f)); }
void place_order(order_ptr o)
{ orders.push_back(o); }
void process_all_orders()
{ for (auto const& cb : callbacks) { cb(orders); } }
private:
std::vector<order_callback> callbacks;
std::vector<order_ptr> orders;
};
The strength of this solution comes from the use of std::function to realize type erasure and allow encapsulating any kind of callable object.
The following helper function, which we will use to generate and place some orders, completes the set up (it simply creates four orders and adds a few items to each order):
void generate_and_place_orders(order_repository& r)
{
order_ptr o = std::make_shared<order>(42);
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
o = std::make_shared<order>(1729);
o->add_item(item::book, "TC++PL, 4th Edition");
o->add_item(item::book, "C++ Concurrency in Action");
r.place_order(o);
o = std::make_shared<order>(24);
o->add_item(item::dvd, "2001: A Space Odyssey");
r.place_order(o);
o = std::make_shared<order>(9271);
o->add_item(item::dvd, "The Big Lebowski");
o->add_item(item::book, "C++ Concurrency in Action");
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
}
Now let's see what kinds of callback we can provide. For starter, let's have a regular callback function that prints all of the orders:
void print_all_orders(std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id() << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
std::cout << "=========================\n\n";
}
And a simple program that uses it:
int main()
{
order_repository r;
generate_and_place_orders(r);
// Register a regular function as a callback...
r.register_callback(print_all_orders);
// Process the order! (Will invoke all the registered callbacks)
r.process_all_orders();
}
Here is the live example showing the output of this program.
Quite reasonably, you are not limited to registering regular functions only: any callable object can be registered as a callback, including a functor holding some state information. Let's rewrite the above function as a functor which can either print the same detailed list of orders as function print_all_orders() above, or a shorter summary that does not include order items:
struct print_all_orders
{
print_all_orders(bool detailed) : printDetails(detailed) { }
void operator () (std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id();
if (printDetails)
{
std::cout << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
else { std::cout << std::endl; }
}
std::cout << "=========================\n\n";
}
private:
bool printDetails;
};
Here is how this could be used in a small test program:
int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
// Register one particular instance of our functor...
r.register_callback(print_all_orders(false));
// Register another instance of the same functor...
r.register_callback(print_all_orders(true));
r.process_all_orders();
}
And here is the corresponding output shown in this live example.
Thanks to the flexibility offered by std::function, we can also register the result of std::bind() as a callback. To demonstrate this with an example, let's introduce a further class person:
#include <iostream>
struct person
{
person(std::string n) : name(n) { }
void receive_order(order_ptr spOrder)
{ std::cout << name << " received order " << spOrder->get_id() << std::endl; }
private:
std::string name;
};
Class person has a member function receive_order(). Invoking receive_order() on a certain person object models the fact that a particular order has been delivered to that person.
We could use the class definition above to register a callback function that dispatches all the orders to one person (which can be determined at run-time!):
void give_all_orders_to(std::vector<order_ptr>& orders, person& p)
{
std::cout << "Dispatching orders:\n=========================\n";
for (auto const& o : orders) { p.receive_order(o); }
orders.clear();
std::cout << "=========================\n\n";
}
At this point we could write the following program, that register two callbacks: the same function for printing orders we have used before, and the above function for dispatching orders to a certain instance of Person. Here is how we do it:
int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
person alice("alice");
r.register_callback(print_all_orders);
// Register the result of binding a function's argument...
r.register_callback(std::bind(give_all_orders_to, _1, std::ref(alice)));
r.process_all_orders();
}
The output of this program is shown in this live example.
And of course one could use lambdas as callbacks. The following program builds on the previous ones to demonstrate the usage of a lambda callback that dispatches small orders to one person, and large orders to another person:
int main()
{
order_repository r;
generate_and_place_orders(r);
person alice("alice");
person bob("bob");
r.register_callback(print_all_orders);
r.register_callback([&] (std::vector<order_ptr>& orders)
{
for (auto const& o : orders)
{
if (o->get_items().size() < 2) { bob.receive_order(o); }
else { alice.receive_order(o); }
}
orders.clear();
});
r.process_all_orders();
}
Once again, this live example shows the corresponding output.
Beyond std::function<> (Boost.Signals2)
The above design is relatively simple, quite flexible, and easy to use. However, there are many things it does not allow to do:
it does not allow to easily freeze and resume the dispatching of events to a particular callback;
it does not encapsulate sets of related callbacks into an event
class;
it does not allow grouping callbacks and ordering them;
it does not allow callbacks to return values;
it does not allow combining those return values.
All these feature, together with many others, are provided by full-fledged libraries such as Boost.Signals2, which you may want to have a look at. Being familiar with the above design, it will be easier for you to understand how it works.
For instance, this is how you define a signal and register two simple callbacks, and call them both by invoking the signal's call operator (from the linked documentation page):
struct Hello
{
void operator()() const
{
std::cout << "Hello";
}
};
struct World
{
void operator()() const
{
std::cout << ", World!" << std::endl;
}
};
int main()
{
boost::signals2::signal<void ()> sig;
sig.connect(Hello());
sig.connect(World());
sig();
}
As usual, here is a live example for the above program.
You might want to look into std::function, your vector would then look like this:
std::vector< std::function< void( Order ) > > functions;
But be aware that std::function has a small overhead. For the instances, drop the new:
function.push_back(GiveStupidOrdersToJohn());
Boost.Signal solves exactly your problem. You should have a look into that. Unless you have special requirements. In particular boost.signal and boost.function and/or std::function
use type erasure techniques. Thus, you are able to have a vector of callable things with a specified signature. It does not matter if your entities are plain C-functions (as you have in your example) or function-objects or member-functions in general. You can mix all of them.
Is there any practical way to get objects to work with maps? (I don't really know much about maps so sorry if this is a bad question). I'm interested in using a map for an inventory system that will contain item objects. An item object has a name, description, and money value. The key would be the item's name, and the linking variable would be the the quantity of items I have for that particular item.
And if a map doesn't work, does anyone have a good alternative to this type of system? I need something keeping track of the quantity of each type of item I have.
The C++ standard library template map is just a storage container so it can definitely be used with objects. The map will take your object as its templated argument parameter.
A map would work well for your inventory system. Use something like:
#include <pair>
#include <map>
#include <string>
#include <iostream>
class Item {
public:
Item(void) {}
~Item(void) {}
Item(std::string new_name) {
my_name=new_name;
}
void setName(std::string new_name) {
my_name= new_name;
}
std::string getName(void) {
return my_name;
}
private:
std::string my_name;
};
class Item_Manager {
public:
Item_Manager(void) {}
~Item_Manager(void) {}
void addItem(Item * my_item, int num_items) {
my_item_counts.insert( std::pair<std::string,int>(Item.getName(),num_items) );
}
int getNumItems(std::string my_item_name) {
return my_item_counters[my_item_name];
}
private:
std::map<std::string, int> my_item_counts;
};
main () {
Item * test_item = new Item("chips");
Item * test_item2 = new Item("gum");
Item_Manager * my_manager = new Item_Manager();
my_manager->addItem(test_item, 5);
my_manager->addItem(test_item2,10);
std::cout << "I have " << my_manager->getNumItems(test_item->getName())
<< " " << test_item->getName() << " and "
<< my_manager->getNumItems(test_item2->getName())
<< " " << test_item2->getName() << std::endl;
delete test_item;
delete test_item2;
delete my_manager;
}
Here's a reference on the stdlib map and its functions:
http://www.cplusplus.com/reference/stl/map/
Look at the function pages for examples of how to iterate through/index a map, etc.
If you're talking about std::map, it's a template which can work with any type of object, as long as a way to compare objects for ordering is provided. Since your key (the name) is a string, it will work right out of the box with std::map
struct Item
{
std::string description;
int value;
};
int main()
{
// associate item names with an item/quantity pair
std::map<std::string, std::pair<Item, int> > itemmap;
}
I need something keeping track of the quantity of each type of item I have.
How about std::vector<std::pair<Item, int> >?
a bit of a vague question but I am looking for pointers as to how can I generate String diff vectors in C++. The scenario is such that given a paragraph I want to store the various differences(Edit, cut copy paste etc.) it goes through in a draft mode to review Audit history.
Any hints in this regard will be really appreciated.
An idea using C++ polymorphism:
class Action
{
public:
virtual void revert(std::string& base) = 0;
};
class InsertAction : public Action
{
private:
int pos, len;
public:
InsertAction(int pos, std::string& base, const std::string& in) : len(in.size()), pos(pos)
{
base.insert(pos, in);
}
virtual void revert(std::string& base)
{
base.erase(pos,len);
}
};
int main()
{
std::string text("hello !");
std::cout << text << std::endl;
Action* action = new InsertAction(5, text, " world");
std::cout << text << std::endl;
action->revert(text);
std::cout << text << std::endl;
delete action;
}
You can then add and pop Actions from a LIFO queue as you want. It's a simple example, you could also try to link it more to a string instead of always passing at as a param, but that's up to your own design. I know it's not 'real' diffing, but I think this solution to be closer coupled to the problem then really storing general string differences.
I am updating an old piece of C++ code and am stuck on a design issue and need advice on the best course of action. The code handles geometric data. Currently, the code defines many global constants to handle element types:
#define TETRAHEDRON 0
#define HEXAHEDRON 1
Each constant has information associated with it that remains constant and which is currently handled by a class, in our case Topology.
int Topology::nodesPerElement(int topType)
{
switch(topType) {
case TETRAHEDRON:
return 4;
break;
case HEXAHEDRON:
return 8;
break;
}
}
The Topology class has many of these functions that simply switch on the global constant to figure out associated information. There are a lot of element types and many bugs are introduced by switch statements that don't consider all element types. If an element type is added all of these methods need to be fixed. I need a better way of doing this that keeps the associated information with the type.
Enumerations are an improvement over this design, but it doesn't solve the problem of associating data with the enumeration.
For simplicity, I would like to avoid needing to instantiate classes for each type, as each will contain only static data that doesn't change.
What I really need is a "static class" that holds this information and performs like the pseudocode below:
class Tetrahedron : public TopType {
static const int nodesPerElement = 4;
static const std::string name = "Tet";
etc...
}
Each method in Topology becomes trivial:
int Topology::nodesPerElement(TopType topType)
{
return topType.nodesPerElement;
}
Is there a way to do this in C++? I've thought about just getting rid of the enumerations and having separate child Topology classes for each TopologyType, but the feedback I get from others is that it's too complicated of a solution. I hope that my question is clear enough.
Create a base class that contains all of the properties that your objects should support, and a private constructor to set those properties. You don't need derived classes, then: you can use static public objects to create the objects that you want with the desired properties.
class TopologyObject
{
private:
int numberVertices;
int numberFaces;
// etc.
public:
int getVertices() { return numberVertices; };
int getFaces() { return numberFaces; };
protected:
TopologyObject(int vertices, int faces) :
numberVertices(vertices),
numberFaces(faces)
{};
public:
static TopologyObject Tetrahedron = new TopologyObject(4, 4);
// etc.
}
You can access the Tetrahedron with all of its properties via TopologyObject::Tetrahedron.
If you decide that you need more complex variable behavior based on the type of object, then you really do need derived classes and virtual methods for the overrideable behavior.
Unless your Topology types have different runtime behaviors (like drawing themselves), then I agree with your peers that sub-classing is overkill. Reporting static properties like nodesPerElement and name is hardly a runtime behavior.
Unless you are not telling us the whole story about Topology, it seems that what you need is a simple property map. Use std::map to associate a topology type code with a structure of topology properties. This refactoring resembles Replace Subclass with Fields.
Here's some code that may serve as inspiration:
#include <cassert>
#include <iostream>
#include <map>
#include <string>
struct Topology
{
enum Code {tetrahedron, hexahedron};
int nodesPerElement;
std::string name;
};
namespace // Anonymous namespace
{
// Lookup table associating topology code with properties
const struct {Topology::Code code; Topology topo;} topoTable_[] =
{
{Topology::tetrahedron, {4, "Tetrahedron"}},
{Topology::hexahedron, {6, "Hexahedron"}}
};
};
class TopologyMap // Singleton
{
public:
static TopologyMap lookup(Topology::Code code)
{
return Topology(instance().doLookup(code));
}
private:
typedef std::map<Topology::Code, Topology> Map;
Map map_;
TopologyMap()
{
// Initialize map with constant property table
size_t tableSize = sizeof(topoTable_) / sizeof(topoTable_[0]);
for (size_t row=0; row<tableSize; ++row)
{
map_[topoTable_[row].code] = topoTable_[row].topo;
}
}
static TopologyMap& instance()
{
static TopologyMap instance;
return instance;
}
const Topology& doLookup(Topology::Code code) const
{
Map::const_iterator match = map_.find(code);
assert(match != map_.end());
return match->second;
}
};
class Shape
{
public:
Shape(Topology::Code topoCode)
: topo_(TopologyMap::lookup(topoCode)) {}
const Topology& topology() const {return topo_;}
// etc...
private:
Topology topo_;
};
int main()
{
Shape shape1(Topology::tetrahedron);
Shape shape2(Topology::hexahedron);
std::cout << "shape1 is a " << shape1.topology().name << " with " <<
shape1.topology().nodesPerElement << " nodes per element.\n";
std::cout << "shape2 is a " << shape2.topology().name << " with " <<
shape2.topology().nodesPerElement << " nodes per element.\n";
};
Output:
shape1 is a Tetrahedron with 4 nodes per element.
shape2 is a Hexahedron with 6 nodes per element.
If the topology code is zero-based and continuous, then you may use simple array indexing instead of a map. However, array indexing will be more error-prone if someone messes around with the topology code enum. Here is the same example that uses array indexing:
#include <cassert>
#include <iostream>
#include <map>
#include <string>
struct Topology
{
enum Code {tetrahedron, hexahedron, CODE_COUNT};
int nodesPerElement;
std::string name;
};
namespace // Anonymous namespace
{
// Lookup table associating topology code with properties
const Topology topoTable_[] =
{
{4, "Tetrahedron"},
{6, "Hexahedron"}
};
};
class TopologyMap // Singleton
{
public:
static Topology lookup(Topology::Code code)
{
assert(code < Topology::CODE_COUNT);
return topoTable_[code];
}
private:
TopologyMap() {} // Non-instantiable
};
class Shape
{
public:
Shape(Topology::Code topoCode)
: topo_(TopologyMap::lookup(topoCode)) {}
const Topology& topology() const {return topo_;}
// etc...
private:
Topology topo_;
};
int main()
{
Shape shape1(Topology::tetrahedron);
Shape shape2(Topology::hexahedron);
std::cout << "shape1 is a " << shape1.topology().name << " with " <<
shape1.topology().nodesPerElement << " nodes per element.\n";
std::cout << "shape2 is a " << shape2.topology().name << " with " <<
shape2.topology().nodesPerElement << " nodes per element.\n";
};
Note that because the details of storing and retrieving Topology was encapsulated in TopologyMap, I didn't have to rewrite any code in Shape and main.
You can have classes with nothing but static member variables. And that's a nice way to encapsulate attribute data.
If you'd rather not do that, traits might get you what you want.
I'm not sure who advised you to avoid derived classes for each Toplogy type. To my eye, this problem is screaming for derived classes.
Unless you would need a very large number of such classes.
Personally I think the best way to store this information would be to create a general Shape class. Then, instead of coding all those static variables put them in a file/database and load your shape information from the data store when you start your program.
Couldn't you use a record to do this if your goal is to avoid class instantiation?
Really though, you should class the poop out of this.
If topType is contiguous and starting a 0, you could just maintain an array of structs and index into that, instead of trying to have classes and subclasses. This way the only code change you would need is to
add the struct: Easy
add an array of structs: Easy
change each method to index into array and return proper field of struct: Tedious, but you have to do this anyway.
It your TopologyType can just be modelled as an instance of a struct (i.e no methods on it etc), Classes + Derived classes is overkill, IMO.
Since (apparently) all the relevant data is available at compile time, one possibility would be to use an enumeration along with templates and specialization to do the job:
enum { tetrahedron, hexahedron };
template <int type>
struct nodes_per_element { int operator()() const {
throw std::invalid_argument("Attempt to use unknown shape");
};
template <>
struct nodes_per_element<tetrahedron> { int operator()() const { return 4; } };
template <>
struct nodes_per_element<hexahedron> { int operator()() const { return 8; } };
You'd use this like: int x = nodes_per_element<hexahedron>()(); If you try to use it for a value for which there's no specialization, that will invoke the un-specialized template, which will throw an exception, halting the program and (normally) displaying a message saying you attempted to use an unknown shape. Of course, you can customize how that's displayed (if at all).
This should quickly show where you have problems due to values that haven't been defined.
The other obvious possibility would be to just define a struct for each shape you're going to use, and create an array of those structs, using the name of the shape as an index into the data, and the name of the specific data you want will be the member of the struct. For just the nodes per element you've given, that would look like:
struct shape_data {
int nodes_per_element;
std::string name;
};
shape_data data[] = {
{4, "Tetrahedron"},
{8, "Hexahedron" }
};
Retrieving data would be something like:
shape_data &s = data[hexahedron];
std::cout << "A " << s.name << " has " << s.nodes_per_element << "nodes per element.\n";
Having look at the previous answers, I've decided to add my own.
To me there are 2 things that I would require of such a design:
the ability to define a new item without recompiling the whole program
the ability to look up an item based on a property (like the number of faces)
This can be quite easy to do, so here is my little bit of code:
class Solid
{
typedef std::vector<Solid> solids_type;
public:
Solid(std::string name, size_t faces, size_t nodes):
mName(name), mFaces(faces), mNodes(nodes)
{
}
///
/// Properties
///
const std::string& getName() const { return mName; }
size_t getFaces() const { return mFaces; }
size_t getNodes() const { return mNodes; }
///
/// Collection Handling
///
static bool Add(Solid solid); // only add if it's not already there.
///
/// struct Predicate: std::unary_function<Solid,bool>
///
template <class Predicate>
static const Solid* Get(Predicate pred)
{
solids_type::const_iterator it =
std::find_if(Solids().begin(), Solids().end(), pred);
return it == Solids().end()) ? 0 : &(*it);
} // Get
///
/// Some Predicates
///
class ByName: std::unary_function<Solid,bool>
{
public:
ByName(std::string name): mName(name) {}
bool operator()(const Solid& s) const { return s.getName() == mName; }
private:
std::string mName;
};
class ByFaces; /// ...
class ByNodes; /// ...
private:
/// Properties
std::string mName;
size_t mFaces;
size_t mNodes;
/// Collection
static solids_type& Solids()
{
static solids_type MSolids;
return MSolids;
}
}; // class Solid
And thus, now we can have:
// in tetrahedron.cpp
namespace
{
bool gTetrahedron = Solid::Add(Solid("Tetrahedron", 4, 4));
}
// in main.cpp
int main(int argc, char* argv[])
{
const Solid* myTetra = Solid::Get(Solid::ByFaces(4));
assert(myTetra->getName() == "Tetrahedron");
assert(myTetra->getFaces() == 4);
assert(myTetra->getNodes() == 4);
return 0;
} // main
And now we have met our goals:
Adding one new solid does not cause any recompilation
We can lookup solid based on their properties
We could also imagine:
being able to iterate through all the registered solids
having them sorted by number of faces, or whatever
defining a little macro for the registration
This is precisely what virtual functions are for. The classical way to do it would be:
class Topology
{
public:
virtual int nodesPerElement() const = 0;
// etc
};
class Tetrahedrom : public Topology
{
public:
virtual nodesPerElement() const { return 4; }
// etc
}
// etc
But if you really have an aversion to re-implementing the accessor methods (as opposed to just defining variables) you could do the following with templates (although it's really no less verbose):
class Topology
{
public:
virtual int nodesPerElement() const = 0;
// etc
};
template<typename T>
class ConcreteTopology : public Topology
{
public:
virtual int nodesPerElement() const { return T::nodesPerElement; }
// etc
};
struct Tetrahedron_Data {
int nodesPerElement = 4;
// etc
};
typedef ConcreteTypology<Tetraheadron_Data> Tetrahedron;
// etc