QMetaObject finding class name in list of pointers - c++

I have a RegistrationList class that has a function which is supposed to itterate over a list of Registration pointers and then return the total fees of a specific registration type passed to the function as a QString. When I call this function from my gui implimentation class it always returns 0 even though I can see that there are registration pointers in the Registration list class. Any idea what i'm doing wrong? i'm guessing it has something to do with my use of QMetaObject but not sure. The first function below is the RegistrationList function and the second is the slot in my gui class.
my Code:
double RegistrationList::totalFees(QString t) {
double total = 0.00;
for (int i = 0; i < attendeeList.size(); ++i) {
if (attendeeList.at(i)->metaObject()->className() == t)
total += attendeeList.at(i)->calculateFee();
}
void MainWindow::totalFees_clicked() {
if (ui->rdbGuest->isChecked()) {
double total = m_rList.totalFees("GuestRegistration");
QMessageBox::information(this, "Total guest registration fees", QString("Total guest registration fees: R %1").arg(total), QMessageBox::Ok);
}
else if(ui->rdbStandard->isChecked()) {
double total = m_rList.totalFees("StandardRegistration");
QMessageBox::information(this, "Total standard registration fees", QString("Total standard registration fees: R%1").arg(total), QMessageBox::Ok);
}
else if (ui->rdbStudent->isChecked()) {
double total = m_rList.totalFees("StudentRegistration");
QMessageBox::information(this, "Total student registration fees", QString("Total student registration fees: R%1").arg(total), QMessageBox::Ok);
}
}

Add DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII to your project file, recompile your code, and fix all the bugs.
Hint: your className comparison with t doesn't do what you think it does. You're comparing pointers, where you should be comparing strings. Rewrite the test as one of the following:
QString::fromLatin1(attendeeList.at(i)->metaObject()->className()) == t, or
!strcmp(attendeeList.at(i)->metaObject()->className(), t.toLatin1()).
This operation should really be a member of the Registration class (if attendeeList contains values of type Registration*):
class Registration : public QObject {
...
public:
bool is(const QString & className) const {
return QString::fromLatin1(metaObject()->className()) == t;
}
...
};
Your totalFees should be a const method, and then you don't need all the at() verbosity: the operator[] will do what you wish, then. You should also pass strings you don't need copies of by reference, not value. The use of iterators lets you get rid of the explicit indexing altogether:
double RegistrationList::totalFees(const QString & t) const {
double total = 0.0;
for (auto it = attendeeList.begin(); it != attendeeList.end(); ++it)
if ((*it)->is(t)) total += (*it)->calculateFee();
return total;
}
If your compiler supports range-for, you should use that instead. It's not the '00s anymore.
double RegistrationList::totalFees(const QString & t) const {
double total = 0.00;
for (auto attendee : attendeeList)
if (attendee->is(t)) total += attendee->calculateFee();
return total;
}
If you wish, you could also use std::accumulate (see this answer):
double RegistrationList::totalFees(const QString & t) const {
return std::accumulate(attendeeList.begin(), attendeeList.end(), 0.0,
[t](Registration* attendee) -> double {
return attendee->is(t) ? attendee->calculateFee() : 0.0;
});
}
Finally, you should never use floating point types to deal with money. Use a proper class to wrap an integer type to represent the lowest monetary unit you wish to deal with.

Related

Create a lookup table for array of classes in C++/Arduino

I have multiple types of classes.
Each type has an array and an index in the array.
If an outside function knows only a string ID of a class and wants to use it's public function,
it must search for that particular class by ID in it's array.
This is really inefficient.
All my classes created at runtime and a function which creates it, puts it into an array.
I want to create a lookup table of some sort for this when the classes are created, so any outside
function if wants to use one class it does not have to for loop on the class's array and check each ID if it matches but to be able to reach the class by some struct or array.
Here how it is done now:
#define MAX_ONE_TYPES 20
int createdOneTypesCounter = 0;
// Create one type of classes in one for loop and put it into an array.
// We must keep track of the index because a class can be created later
// at runtime so we must keep increasing the index. I don't check for max index now...
// oneTypes is a JSON OBJECT
for (JsonPair oneTypeRef: oneTypes) {
const char* oneTypeID = oneTypeRef.key().c_str();
JsonObject oneTypeOptions = oneTypes[oneTypeID];
oneTypeClasses[createdOneTypesCounter ] = new oneTypeClass(oneTypeOptions);
createdOneTypesCounter++;
}
class oneTypeClass{
private:
// using an external ram for this kinda stuffs.
const size_t IDS_AND_NAMES_SIZE = 500;
const char * id = (char *) ps_malloc (IDS_AND_NAMES_SIZE * sizeof (char));
public:
thermHandler(JsonObject options){
// She got an ID on creation.
id = strdup(options["id"]);
}
void setModes(boolean mode){
// set some mode...
}
boolean isMyID(const char* packetID){
if( strcmp(id, packetID) == 0 ){return true;}
return false;
}
};
oneTypeClass* oneTypeClasses[MAX_ONE_TYPES] EXT_RAM_ATTR;
// Here comes an outside function. Wants to set a boolean in a class with specific ID.
static const inline void setOneTypeMode(JsonObject packet){
for(int i = 0; i < MAX_ONE_TYPES; i++){
if(oneTypeClasses[i] != NULL && oneTypeClasses[i]->isMyID(packet["id"])){
oneTypeClasses[i]->setModes(packet["mode"]);
break;
}
}
}
And here is my problem. I must search for a class by ID every time some outside function wants to do something with one of the classes.
I don't know how would i do it.
In JS i would create an object for a lookup table and every time a class is created i would put it's ID for the key and it's index to the value like this:
var oneTypeClass_Lookup{
"CLASS ID STRING" : "CLASS INDEX IN ARRAY"
};
//And a function would do it like this:
static const inline void setOneTypeMode(JsonObject packet){
int myClassIndex = oneTypeClass_Lookup[ packet["id"] ];
oneTypeClasses[myClassIndex]->setModes(packet["mode"]);
}
I'm doing this for "mass operation":
static const inline int getOneTypeClassIndex(const char* packetID){
for(int i = 0; i < MAX_THERMS; i++){
if(oneTypeClasses[i] != NULL && oneTypeClasses[i]->isMyID(packetID)){
return i;
}
}
return -1;
}
static const inline void setThing(int newThing, const char* packetID){
int index = getOneTypeClassIndex(packetID);
if( index > -1 ){
oneTypeClasses[index]->setNewThing(newThing);
}
}
static const inline void setThing_Two(int newThing, const char* packetID){
int index = getOneTypeClassIndex(packetID);
if( index > -1 ){
oneTypeClasses[index]->setNewThing(newThing);
}
}
But i can't do this in C or Arduino C++. I hope i was clear.
UI: Class id consist of numbers and characrers. The id can never start with a number. Example: "v_kw62ffss_xg0syjlvrokbxciv65a8y"

How to wrap several boolean flags into struct to pass them to a function with a convenient syntax

In some testing code there's a helper function like this:
auto make_condiment(bool salt, bool pepper, bool oil, bool garlic) {
// assumes that first bool is salt, second is pepper,
// and so on...
//
// Make up something according to flags
return something;
};
which essentially builds up something based on some boolean flags.
What concerns me is that the meaning of each bool is hardcoded in the name of the parameters, which is bad because at the call site it's hard to remember which parameter means what (yeah, the IDE can likely eliminate the problem entirely by showing those names when tab completing, but still...):
// at the call site:
auto obj = make_condiment(false, false, true, true); // what ingredients am I using and what not?
Therefore, I'd like to pass a single object describing the settings. Furthermore, just aggregating them in an object, e.g. std::array<bool,4>.
I would like, instead, to enable a syntax like this:
auto obj = make_smart_condiment(oil + garlic);
which would generate the same obj as the previous call to make_condiment.
This new function would be:
auto make_smart_condiment(Ingredients ingredients) {
// retrieve the individual flags from the input
bool salt = ingredients.hasSalt();
bool pepper = ingredients.hasPepper();
bool oil = ingredients.hasOil();
bool garlic = ingredients.hasGarlic();
// same body as make_condiment, or simply:
return make_condiment(salt, pepper, oil, garlic);
}
Here's my attempt:
struct Ingredients {
public:
enum class INGREDIENTS { Salt = 1, Pepper = 2, Oil = 4, Garlic = 8 };
explicit Ingredients() : flags{0} {};
explicit Ingredients(INGREDIENTS const& f) : flags{static_cast<int>(f)} {};
private:
explicit Ingredients(int fs) : flags{fs} {}
int flags; // values 0-15
public:
bool hasSalt() const {
return flags % 2;
}
bool hasPepper() const {
return (flags / 2) % 2;
}
bool hasOil() const {
return (flags / 4) % 2;
}
bool hasGarlic() const {
return (flags / 8) % 2;
}
Ingredients operator+(Ingredients const& f) {
return Ingredients(flags + f.flags);
}
}
salt{Ingredients::INGREDIENTS::Salt},
pepper{Ingredients::INGREDIENTS::Pepper},
oil{Ingredients::INGREDIENTS::Oil},
garlic{Ingredients::INGREDIENTS::Garlic};
However, I have the feeling that I am reinventing the wheel.
Is there any better, or standard, way of accomplishing the above?
Is there maybe a design pattern that I could/should use?
I think you can remove some of the boilerplate by using a std::bitset. Here is what I came up with:
#include <bitset>
#include <cstdint>
#include <iostream>
class Ingredients {
public:
enum Option : uint8_t {
Salt = 0,
Pepper = 1,
Oil = 2,
Max = 3
};
bool has(Option o) const { return value_[o]; }
Ingredients(std::initializer_list<Option> opts) {
for (const Option& opt : opts)
value_.set(opt);
}
private:
std::bitset<Max> value_ {0};
};
int main() {
Ingredients ingredients{Ingredients::Salt, Ingredients::Pepper};
// prints "10"
std::cout << ingredients.has(Ingredients::Salt)
<< ingredients.has(Ingredients::Oil) << "\n";
}
You don't get the + type syntax, but it's pretty close. It's unfortunate that you have to keep an Option::Max, but not too bad. Also I decided to not use an enum class so that it can be accessed as Ingredients::Salt and implicitly converted to an int. You could explicitly access and cast if you wanted to use enum class.
If you want to use enum as flags, the usual way is merge them with operator | and check them with operator &
#include <iostream>
enum Ingredients{ Salt = 1, Pepper = 2, Oil = 4, Garlic = 8 };
// If you want to use operator +
Ingredients operator + (Ingredients a,Ingredients b) {
return Ingredients(a | b);
}
int main()
{
using std::cout;
cout << bool( Salt & Ingredients::Salt ); // has salt
cout << bool( Salt & Ingredients::Pepper ); // doesn't has pepper
auto sp = Ingredients::Salt + Ingredients::Pepper;
cout << bool( sp & Ingredients::Salt ); // has salt
cout << bool( sp & Ingredients::Garlic ); // doesn't has garlic
}
note: the current code (with only the operator +) would not work if you mix | and + like (Salt|Salt)+Salt.
You can also use enum class, just need to define the operators
I would look at a strong typing library like:
https://github.com/joboccara/NamedType
For a really good video talking about this:
https://www.youtube.com/watch?v=fWcnp7Bulc8
When I first saw this, I was a little dismissive, but because the advice came from people I respected, I gave it a chance. The video convinced me.
If you look at CPP Best Practices and dig deeply enough, you'll see the general advice to avoid boolean parameters, especially strings of them. And Jonathan Boccara gives good reasons why your code will be stronger if you don't directly use the raw types, for the very reason that you've already identified.

C++ method to call a specific function without if/elseif/elseif/elseif/else

I want to create a function that takes a string that might be :
"triangle" , "square", or "rectangle"
And according to this argument, I want to return a pointer on a class Form.
I have a mother class "Form", who inherits of "Class Rectangle", "Class Square", and "Class Rectangle"
But I don't want to do :
if (name == "rectangle")
return (new Rectangle());
else if (name == "square")
return (new Square());
... etc
I thought about pointers on functions, but I wanted the simplest method and clean code, what do you recommand ?
Thank's !
Yes, you could use function pointers or lambdas. You can use a map of strings to functors:
std::map<std::string, std::function<ShapeBase*()>> actions = {
{ "rectangle", []{return new Rectangle;} },
{ "square", []{return new Square;} }
};
return actions[name]();
But if you're not going to change the actions at run time, it's hard to beat what you had really.
In a comment you asked "Imagine it had 500 forms". Indeed, the map lookup will be faster than 500 chained if statements. You could make it a switch with some effort: use hashes for the names instead of the strings themselves. If the hash function is constexpr you can write that easily:
switch (hash(name)) {
case hash("rectangle"): return new Rectangle;
case hash("square"): return new Square;
// 500 cases
}
The switch statement will be optimized to do a binary search or something like that, on the integer hash values. You'll also get a compile-time error if there is a hash clash.
You can use std::unordered_map:
using FormPtr = std::unique_ptr<Form>;
using Creators = std::unordered_map<std::string,std::function<FormPtr()>>;
FormPtr create( const std::string &name )
{
const static Creators creators {
{ "triangle", [] { return std::make_unique<Triangle>(); } },
{ "square", [] { return std::make_unique<Square>(); } },
{ "rectangle", [] { return std::make_unique<Rectangle>(); } }
};
auto f = creators.find( name );
if( f == creators.end() ) {
// error handling here
}
return f->second();
}
If you need to add creators outside you can put them into a class and allow them to update the map and register more creators dynamically.

How to call a class within a function definition in c++

Its my first time asking for help on programming. I have been working on a register program for my programming class for weeks which involves classes. Its rather frustrating for me. I have to use two classes: StoreItem and Register. StoreItem deals with the small list of items that the store sells. The register class deals mostly with processing the items, making a total bill and asking the user to pay with cash.
Here is the StoreItem.cpp file:
//function definition
#include <string>
#include <iostream>
#include "StoreItem.h"
#include "Register.h"
using namespace std;
StoreItem::StoreItem(string , double)
{
//sets the price of the current item
MSRP;
}
void StoreItem::SetDiscount(double)
{
// sets the discount percentage
MSRP * Discount;
}
double StoreItem::GetPrice()
{ // return the price including discounts
return Discount * MSRP;
}
double StoreItem::GetMSRP()
{
//returns the msrp
return MSRP;
}
string StoreItem::GetItemName()
{
//returns item name
return ItemName;
}
StoreItem::~StoreItem()
{
//deletes storeitem when done
}
Here is the Register.cpp:
Note that the last 5 function definitions in this one arent finished yet...
// definition of the register header
#include "Register.h"
#include "StoreItem.h"
using namespace std;
Register::Register()
{ // sets the initial cash in register to 400
CashInRegister = 400;
}
Register::Register(double)
{ //accepts initial specific amount
CashInRegister ;
}
void Register::NewTransAction()
{ //sets up the register for a new customer transaction (1 per checkout)
int NewTransactionCounter = 0;
NewTransactionCounter++;
}
void Register::ScanItem(StoreItem)
{ // adds item to current transaction
StoreItem.GetPrice();
// this probably isnt correct....
}
double Register::RegisterBalance()
{
// returns the current amount in the register
}
double Register::GetTransActionTotal()
{
// returns total of current transaction
}
double Register::AcceptCash(double)
{
// accepts case from customer for transaction. returns change
}
void Register::PrintReciept()
{
// Prints all the items in the transaction and price when finsished
}
Register::~Register()
{
// deletes register
}
My main question is where Register::ScanItem(StoreItem)... is there a way to correctly call a function from the storeItem Class into the Register scanitem function?
You have:
void Register::ScanItem(StoreItem)
{ // adds item to current transaction
StoreItem.GetPrice();
// this probably isnt correct....
}
This means that the ScanItem function takes one argument of type StoreItem. In C++ you can specify just the type and make the compiler happy. But if you intend to use the argument, you must give it a name. For example:
void Register::ScanItem(StoreItem item)
{
std::cout << item.GetItemName() << " costs " << item.GetPrice() << std::endl;
}
To be able to call a member function of the object you're passing as the parameter, you need to name the parameter, not just its type.
I suspect you want something like
void Register::ScanItem(StoreItem item)
{
total += item.GetPrice();
}

Value of an object will not be changed

I created a method which should change values in my shop object. Unfortunately the values are not changed.
edit: 18:13
I could create a new shop object and return it, but I thought it should work with passing the object by reference?
To make my question clearer: My only problem is, that the new values are not stored in the object. I did run the debugging and the values are all correctly calculated and as expected.
The problem is in the lines:
shop.get_stock().push_back(inventory_bonbon);
This line should push a new inventory item to the vector (containing the inventory items), if this inventory item is currently not in stock.
Here I increase the amount of an inventory item, when the item is currently in stock:
i_inventory_shop.increase_amount(shop.get_stock()[i], amount);
(I have unit-tested the increase_amount() method and it works fine.)
The two lines are called as expected (meaning I find when an item is in stock or not).
void IRooms::increase_inventory_shop(Shop & shop, Bonbon & bonbon, int amount)
{
OutputDebugString("I_Game Logic increase_inventory_shop called \n");
IInventoryItemsBonbons i_inventory_shop;
bool bonbon_in_shop = false;
for (int i = 0; i < shop.get_stock().size(); i++)
{
OutputDebugString(("I_Game Logic shop vector size \n" + std::to_string(shop.get_stock().size()) + "\n").c_str());
OutputDebugString(("I_Game Logic bonbon name \n" + bonbon.get_name() + "\n").c_str());
OutputDebugString(("I_Game Logic bonbon amount \n" + std::to_string(amount) + "\n").c_str());
if (bonbon.get_name() == shop.get_stock()[i].get_bonbon().get_name())
{
bonbon_in_shop = true;
OutputDebugString("Bonbon found \n");
i_inventory_shop.increase_amount(shop.get_stock()[i], amount);
break;
}
}
if (bonbon_in_shop == false) {
OutputDebugString("Bonbon not found \n");
InventoryItemBonbons inventory_bonbon = i_inventory_shop.create(amount, bonbon);
shop.get_stock().push_back(inventory_bonbon);
}
}
This method calls: (the method below, I have tested it)
void IInventoryItemsBonbons::increase_amount(InventoryItemBonbons & inventoryitem_shop, int amount)
{
int old_amount = inventoryitem_shop.get_amount();
int new_amount = old_amount + amount;
inventoryitem_shop.set_amount(new_amount);
}
edit 17:51:
Shop.h
std::vector<InventoryItemBonbons> get_stock();
Shop.ccp
std::vector<InventoryItemBonbons> Shop::get_stock()
{
return stock_bonbons;
}
_____________________________________________________________________________edit: 19:54
I have now introduced local variables and I return the local shop.
Shop IRooms::increase_inventory_shop(Shop & shop, Bonbon & bonbon, int amount)
{
Shop shop_temp = shop;
std::vector<InventoryItemBonbons> inventory_items_temp = shop.get_stock();
IInventoryItemsBonbons i_inventory_shop;
bool bonbon_in_shop = false;
for (int i = 0; i < shop_temp.get_stock().size(); i++)
{
if (bonbon.get_name() == shop_temp.get_stock()[i].get_bonbon().get_name())
{
bonbon_in_shop = true;
i_inventory_shop.increase_amount(inventory_items_temp[i], amount);
break;
}
}
if (bonbon_in_shop == false) {
InventoryItemBonbons inventory_bonbon = i_inventory_shop.create(amount, bonbon);
inventory_items_temp.push_back(inventory_bonbon);
}
shop_temp.set_stock(inventory_items_temp);
//shop = shop_temp;
//return shop;
return shop_temp;
}
The only thing I want to know, why the values of shop won't change. I have tried to copy shop_temp to shop, but even this does not work.
std::vector<InventoryItemBonbons> get_stock();
Since get_stock returns by value, not by reference, any changes to the value returned will be lost as soon as that temporary goes out of scope.
shop.get_stock().push_back(inventory_bonbon);
So this modifies the temporary returned by get_stock, which immediately goes out of scope, is destroyed, and the modification is lost.
You probably wanted:
std::vector<InventoryItemBonbons>& get_stock();
...
std::vector<InventoryItemBonbons>& Shop::get_stock()
{
return stock_bonbons;
}