using functions from outside a class [duplicate] - c++

This question already has answers here:
Why is my HelloWorld function not declared in this scope?
(11 answers)
Closed 2 years ago.
First of forgive me if this has been answered, i could not find it.
i m pretty new to C++ and programming in general just fumbling my way thorough. I created a class called "Angle". outside of this class i have a function to test input from user to make sure its an integer called "isInt". But i keep getting an error "identifier not fund" if i try to use the isInt function inside the Angle class. do i have no choice but to repeat the code for isInt inside the class ?
Thanks in Advance
Bellow is my code for the class and the function
class Angle {
private:
int degrees;
float minutes;
char direction;
public:
//Setters
void setDegrees(int x) {
degrees = x;
}
void setMinutes(float y) {
minutes = y;
}
void setDirection(char z) {
direction = z;
}
//Getters
int getDegrees() {
return degrees;
}
float getMinutes() {
return minutes;
}
char getDirection() {
return direction;
}
//Constructors
Angle() {
degrees = -1;
minutes = -1;
direction = NULL;
}
//Member Functions
void getAngleFromUser() {
bool test = false;
std::string temp;
std::cout << "Please input an angle value (degrees and minutes)\n";
while (test != true) {
std::cout << "Please input and angle in degrees (it must be an integer): ";
std::cin >> temp;
test = isInt(temp);
}
setDegrees(std::stoi(temp));
}
};
bool isInt(std::string input)
{
if (input.find_first_not_of("1234567890") != std::string::npos) {
std::cout << "invalid input. please input integer\n";
return false;
}
else {
return true;
}
}

To use any function you must declare/define it before using it.
i.e if you write
bool isInt(std::string); before defining your class, your problem will be solved.
Other things
your getters (getDegrees/Minutes/Direction) should be constant.
input argument should be constant.
why don't you include isInt in your class as a private function to encapsulate it?
And what do you mean by Null to be assigned to a character? you may mean \0
while (test != true) equiv while(!test)

Related

Having issues with overloading C++ operators

I'm having some issues understanding this concept. In the main.cpp file, we have a function as follows:
void TestComparison()
{
MyFloat X, Y;
cout << "\n\n============ Testing \"==\" for MyFloat ================\n";
do
{
cout << "\nEnter X ==> ";
X.Read();
cin.ignore(1000, '\n'); // Discard all chars in input stream.
cout << "\nEnter Y ==> ";
Y.Read();
cin.ignore(1000, '\n'); // Discard all chars in input stream.
cout << "\n\n";
if ( X == Y )
{
X.Write(); cout << " is equal to "; Y.Write();
}
else
{
X.Write(); cout << " is NOT equal to "; Y.Write();
}
}
while ( SpaceBarToContinue() );
}
This is the class I'm writing:
class MyFloat
{
enum {MAXDIGIT=20};
char Number[MAXDIGIT+1];
char NumberOfDigits;
public:
friend void AssignValue(MyFloat& X);//remove after the program works
MyFloat();
int Digits();
int MaxDigits();
void Read();
void Write();
MyFloat operator + (MyFloat x);
int operator== (MyFloat x);
};
Here is my == overload function stub:
int MyFloat::operator== (MyFloat x)
{
int Flag=0;
return 1;
}
The only purpose of this is to compare two an array of objects X and Y. They are passed into a == overloaded function. I'm supposed to write the algorithm that compares them. I know how to write the algorithm that compares these two character arrays, thats not the issue, but what I'm failing to understand is how both X and Y get into the the overloaded function to compare them? In the main, the code ( X == Y ) is used to obtain a 0 or 1. How are X and Y passed into the function?
For instance, I would assume my function stub would need to be rewritten with 2 parameters:
int MyFloat::operator== (MyFloat x, MyFloat y)
{
int Flag=0;
return 1;
}
But doing this produces an error back in the main during the function call of ( X == Y ) that states 'Overload "operator==" must be a binary operator (has 3 parameters)'
So I'm totally confused on how to get both Objects of MyFloat into the function to compare them. I'm still fairly new to programming (5-6 months of learning), any plain and simple answers are greatly appreciated.
When you write:
if(a == b)
what it really means is:
if(a.operator==(b))
So in your method:
bool MyFloat::operator==(const MyFloat &x) const
{
// x is b in call above
// (*this) is a in call above
// Your class invariant should guarantee this:
// assert(x.NumberOfDigits < MAX_DIGITS);
// In the scope of your class' methods:
// NumberOfDigits corresponds to this->NumberOfDigits
// Number corresponds to this->Number
if(x.NumberOfDigits != NumberOfDigits) return false;
// Same as: if(x.NumberOfDigits != this->NumberOfDigits) return false;
return strncmp(x.Number, Number, NumberOfDigits) == 0;
// Same as: return strncmp(x.Number, this->Number, this->NumberOfDigits) == 0;
}
Note that I changed the signature of your method. The correct signature returns a bool and takes a const (because you don't want to change the parameter) reference (avoid copying a big object) as parameter. The method is (and must be) const because it's not supposed to modify the object and it must be callable on a const object.
Note that it is possible to define the operator as a non-member function (i.e outside of the class) with the following signature:
bool operator==(const MyFloat &a, const MyFloat &b)
You should use this pointer. For more information: Source
bool MyFloat::operator==(const MyFloat& x) const
{
for(int i = 0; i < x.MaxDigits; ++i)
{
if(x[i] != (*this)[i])
return false;
}
return true;
}
member functions (including overloaded operators) have an implicit this parameter passed in. In your case since you are using a member version of operator== you should only need one parameter the other is this.

Issue With My School Assignment on Classes

So I have an assignment due in my C++ class on classes, and I'm having some trouble. Here is the description of the assignment:
Programming Challenge 7 on page 499 of your text asks you to design and Inventory Class that can hold information for an item in a retail store's inventory. You are given the code for the creation of the class along with code for the implementation of the functions. Demonstrate the class by writing a simple program that uses it. This program should demonstrate that each function works correctly. Submit your .cpp file using the link provided.
And here are the contents of the file sent (it's quite lengthy):
// Chapter 7---Files for Programming Challenge 13---Inventory Class
// This is the inventory.h file.
// It contains the Inventory class declaration.
#ifndef INVENTORY_H
#define INVENTORY_H
class Inventory
{
private:
int itemNumber;
int quantity;
double cost;
double totalCost;
public:
// Default constructor
Inventory()
{ itemNumber = quantity = cost = totalCost = 0; }
// Overloaded constructor
Inventory(int, int, double); // Defined in Inventory.cpp
// Mutators (i.e., "set" functions) defined in Inventory.cpp
void setItemNumber(int);
void setQuantity(int);
void setCost(double);
// setTotalCost calculates the total cost
// and stores the result in the totalCost member
void setTotalCost()
{ totalCost = cost * quantity; }
// Accessors (i.e., "get" functions)
int getItemNumber()
{ return itemNumber; }
int getQuantity()
{ return quantity; }
double getCost()
{ return cost; }
double getTotalCost()
{ return totalCost; }
// Input validation functions
bool validInt(int);
bool validFloat(double);
};
#endif
// This is the inventory.cpp file.
// It contains the Inventory class function definitions.
#include <iostream>
#include "Inventory.h"
using namespace std;
//************************************************************
// Overloaded constructor
// Accepts arguments to be stored in each member variable.
//************************************************************
Inventory::Inventory(int in, int q, double c)
{
setItemNumber(in);
setQuantity(q);
setCost(c);
setTotalCost();
}
//************************************************************
// setItemNumber accepts an argument to be stored in item number.
//************************************************************
void Inventory::setItemNumber(int in)
{
while (!validInt(in))
{
cout << "Item Number must be positive. Please re-enter: ";
cin >> in;
}
itemNumber = in;
}
//************************************************************
// setQuantity accepts an argument to be stored in quantity.
//************************************************************
void Inventory::setQuantity(int q)
{
while (!validInt(q))
{
cout << "Quantity must be positive. Please re-enter: ";
cin >> q;
}
quantity = q;
}
//************************************************************
// setCost accepts an argument to be stored in cost.
//************************************************************
void Inventory::setCost(double c)
{
while (!validInt(c))
{
cout << "Cost must be positive. Please re-enter: ";
cin >> c;
}
cost = c;
}
//************************************************************
// The validInt member tests its integer argument to see
// if it is negative. If the argument is negative, the function
// returns false. Otherwise, the function returns true.
//************************************************************
bool Inventory::validInt(int value)
{
if (value < 0) // the value is negative so it is NOT valid
return false;
else // the integer value is valid
return true;
}
//************************************************************
// The validFloat member tests its floating-point argument to see
// if it is negative. If the argument is negative, the function
// returns false. Otherwise, the function returns true.
//************************************************************
bool Inventory::validFloat(double value)
{
if (value < 0) // the value is negative so it is NOT valid
return false;
else // the floating-point value is valid
return true;
}
I'm just not sure how to use this information to make a program that demonstrates the class, and it could be as simple as me not saving the file the correct way
Just write a main function which instantiates an Inventory object and calls each of its methods in a meaningful way. This isn't a puzzle, just find a way to call the functions that makes sense to you.

Casting objects in vector in c++

I know this question has already been asked, but none of the answers that I have as of yet found seem to suffice. I am creating a vector of pointers to a base class and adding all sorts of derived classes to it. Now, the base class has a virtual function that is overridden in all of the derived classes and that is unique to each of them. So, when I go through the vector and retrieve those objects and call the function on that object, I need it to call the right one, but all it will do is call the base class version. I am even trying to cast the individual elements back to their original class when I retrieve them from the vector but they refuse to be cast! e.g.
vector<base*> myBase;
DerivedClass *myDerived = static_cast<DerivedClass> myBase[i];
This doesn't work, despite the fact that everything I've read suggests that it should. My debugger says that despite all of this, myDerived is still of type base and it's version of my virtual function is being called.
Any ideas?
class BankAccount {
public:
BankAccount(string namein, string typein){
name = namein;
type = typein;
balance = 0;
}
virtual string getType();
virtual void printTransactions() = 0;
virtual int withdraw(double amt){
return getBalance() -amt;
}
};
class SavingsAccount: public BankAccount {
public:
SavingsAccount(string namein, string typein);
void addTransaction(string transType, string name);
virtual int withdraw(double amt);
void printTransactions();
virtual string getType();
private:
};
SavingsAccount::SavingsAccount(string namein, string typein): BankAccount(namein, typein) {
}
int SavingsAccount::withdraw(double amt){
double aBal = getBalance() - amt;
if (aBal > 0){
setBalance(aBal);
}
return getBalance() - amt;
}
class CheckingAccount: public SavingsAccount {
public:
CheckingAccount(string nameIn, string typein): SavingsAccount(nameIn, typein){
}
virtual int withdraw(double amt);
void printTransactions();
string getType(){
return "Checking";
}
};
int CheckingAccount::withdraw(double amtIn){
double newBal = getBalance() - amtIn;
if (newBal < 500.00 && newBal > 2.49) {
setBalance(newBal - 2.50);
}
return newBal;
}
int main(int argc, const char * argv[])
{
vector<BankAccount*> myAccts;
SavingsAccount *mySav;
CD *myCD;
CheckingAccount *myCheck;
switch (option) {
case 1: {
string name;
string type;
cout << "Enter name: ";
cin >> name;
getline(cin, dump);
cout << "Enter account type: ";
cin >> type;
getline(cin, dump);
if (type.compare("Checking") == 0) {
CheckingAccount myCheck1 = CheckingAccount(name, type);
myAccts.push_back(&myCheck1);
}
case 3:{
for (int x = 0; x < myAccts.size(); x++) {
if (myAccts[x]->getName() == name && myAccts[x]->getType() == type) {
if (type == "Savings") {
mySav = static_cast<SavingsAccount*>(myAccts[x]);
double y = mySav->withdraw(amt);
if (y < 0){
cout << "Insufficient funds!";
}
}
if (type == "Checking") {
myCheck = myAccts[x]->GetDerived();
double y = myCheck->withdraw(amt);
if (y < 0){
cout << "Insufficient funds!";
}
if (y < 497.5) {
cout << "Withdrawal fee: $ 2.50" << endl;
}
}
}
Checking Account is a child of Savings Account. Sorry.
Your concept is correct and shouldn't require any casting. The whole point of virtual functions is that if you're holding a base class pointer or reference and call a virtual function, the most derived version of this function will be called at runtime.
The error I see is this:
if (type.compare("Checking") == 0) {
CheckingAccount myCheck1 = CheckingAccount(name, type);
myAccts.push_back(&myCheck1);
You are creating a checking account on the stack, then taking it's address and pushing that address into the vector. At the end of the if block myCheck1 will go out of scope and be destroyed. Your vector will have an address to a location in the stack and you will have Undefined Behavior.
Instead do:
if (type.compare("Checking") == 0) {
myAccts.push_back(new CheckingAccount(name, type));
And similar for the other types. Get rid of all of those casts. In this version you will have to delete all of the items in the vector at the end. If you use a std::vector<std::unique_ptr<BankAccount>> then the unique_ptr will take care of cleaning up your allocated objects.
You need to use new to create your accounts... you have:
if (...)
{
CheckingAccount myCheck1 = CheckingAccount(name, type);
myAccts.push_back(&myCheck1);
}
...myCheck1 gets destroyed when leaving that if scope, leaving myAccts with a pointer to an effectively random location on the stack that has undefined behaviour if accessed. Change to:
if (type == "Checking")
myAccts.push_back(new CheckingAccount(name, type));
You will then need to have matching deletes for the vector elements. Googling "C++ new delete tutorial" would be a good idea. The next stage is to learn how to use smart pointers, for example - std::shared_pointer - which remove the burden of remembering to delete.
"Case 3" can be corrected/simplified to:
for (int x = 0; x < myAccts.size(); x++)
if (myAccts[x]->getName() == name && myAccts[x]->getType() == type) {
double y = myAccts[x]->withdraw(amt);
if (y < 0)
cout << "Insufficient funds!";
if (type == "Checking" && y < 497.5)
cout << "Withdrawal fee: $ 2.50" << endl;
}
Notice in particular the double y = myAccts[x]->withdraw(amt); - the virtual function makes sure the right version is called without you having to do anything type-specific in the calling code.
Have you tried something like this?
class base
{
public:
inline DerivedClass *GetDerived() {return (DerivedClass*)this;}
...
};
DerivedClass *myDerived = myBase[i]->GetDerived();

Seemingly random program failure at end of completion C++ [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 9 years ago.
Improve this question
Warning: I understand if anyone may want to stop reading now, this post contains ~275 lines of code across 6 files (although nothing very complex)! I realize this is usually a bad thing to do, but it's a last ditch effort as I've put cout's in everything method showing none of them crash or do anything that I wouldn't expect, researched implementation of the standard methods I'm using, and ran this code with a huge variety of inputs but sometimes it runs successfully, other times it fails (after finishing everything). I can't find any pattern or broken code segment.
The program simulates a type of shop with a single server allowing customers to order one of two things and there is a waiting line. The user inputs the simulation length, customer arrival probability per time unit (minute), and the time it takes for each item to be made. After running, the program then prints out a few statistics - total wait time (excluding those remaining in line), total customers served, and average wait time.
Even with long simulations (100,000 minutes) I've seen successful and failed runs (again, only failing after simulation completion). At first I thought it looked like using (>= 1) for arrival probability (customer arrives each minute) always worked, but have since noticed those failing. If anything, it seems fairly high arrival (> ~.8) and very low (<= ~.01) arrival probabilities crash the least often in long simulations, but still can sometimes in short ones. Very odd!
Whenever it does crash, the debugger shows the program counter stopping at the closing brace of queueType's destructor, but this destructor seems extrememly standard to me, and the same syntax has worked with other classes that allocate memory on the heap with their constructors? I feel like the answer must be something fairly basic that is eluding me.
Any help would be greatly appreciated, code follows:
queueType.h:
#ifndef QUEUETYPE_H
#define QUEUETYPE_H
#include <algorithm>
#include <cstdlib>
template<class Type>
class QueueType {
public:
QueueType();
~QueueType();
QueueType(const QueueType& other);
Type& getFront() {return queueArray[front];}
int getNumElements() const {return numElements;}
void reposition();
void addElement(Type);
bool isEmpty() const {return numElements == 0;}
bool isFull() const {return SIZE == numElements;}
void updateWaitTimes(Type*&, int&, int&);
QueueType<Type>& operator=(const QueueType other);
friend void swap(QueueType& first, QueueType& second) {
using std::swap;
swap(first.front, second.front);
swap(first.back, second.back);
swap(first.numElements, second.numElements);
swap(first.queueArray, second.queueArray);
}
private:
static const int SIZE = 25;
int front, back, numElements;
Type *queueArray;
};
template<class Type>
QueueType<Type>::QueueType() {
queueArray = new Type[SIZE];
front = back = numElements = 0;
}
template<class Type>
QueueType<Type>::~QueueType() {
delete [] queueArray;
}
template<class Type>
QueueType<Type>::QueueType(const QueueType& other):
queueArray(new Type[SIZE]),
front(other.front),
back(other.back),
numElements(other.numElements)
{
std::copy(other.queueArray, other.queueArray + SIZE, queueArray);
}
template<class Type>
void QueueType<Type>::reposition() {
front = (front + 1) % SIZE;
back = (back + 1) % SIZE;
numElements--;
}
template<class Type>
void QueueType<Type>::addElement(Type newElement) {
if (isEmpty()) {
queueArray[0] = newElement;
front = back = 0;
numElements = 1;
} else {
back = (back - 1) % SIZE;
queueArray[back] = newElement;
numElements++;
}
}
template<class Type>
void QueueType<Type>::updateWaitTimes(Type*& element, int& position, int& counter) {
if (isEmpty()) {
element = NULL;
} else {
if (position == 0) {
position = front;
}
element = &queueArray[position];
position = (position + 1) % SIZE;
}
if (counter == numElements) {
element = NULL;
}
counter++;
}
template<class Type>
QueueType<Type>& QueueType<Type>::operator=(const QueueType other) {
swap(*this, other);
return *this;
}
#endif /* QUEUETYPE_H */
customerType.h:
#ifndef CUSTOMERTYPE_H
#define CUSTOMERTYPE_H
class CustomerType {
public:
CustomerType();
CustomerType(int, int);
~CustomerType();
CustomerType(const CustomerType& other);
void incrementWaitTime() {waitTime++;}
int getArrivalTime() const {return arrivalTime;}
int getWaitTime() const {return waitTime;}
CustomerType& operator=(const CustomerType& other);
private:
int ID, arrivalTime, waitTime;
};
#endif /* CUSTOMERTYPE_H */
customerType.cpp:
#include "customerType.h"
CustomerType::CustomerType() {
waitTime = arrivalTime = ID = 0;
}
CustomerType::CustomerType(int arrivalTime, int ID) {
this->arrivalTime = arrivalTime;
this->ID = ID;
waitTime = 0;
}
CustomerType::~CustomerType() {
}
CustomerType::CustomerType(const CustomerType& other) {
waitTime = other.waitTime;
arrivalTime = other.arrivalTime;
ID = other.ID;
}
CustomerType& CustomerType::operator=(const CustomerType& other) {
waitTime = other.waitTime;
arrivalTime = other.arrivalTime;
ID = other.ID;
return *this;
}
serverType.h:
#ifndef SERVERTYPE_H
#define SERVERTYPE_H
#include "customerType.h"
#include <cstdlib>
#include <string>
class serverType {
public:
serverType();
~serverType();
serverType(const serverType& other);
bool isFree() const {return (status == "free");}
void setCustomer(CustomerType& newCustomer, int& transactionTime);
void decrementTransactionTime();
serverType& operator=(const serverType& other);
private:
std::string status;
int transactionTime;
CustomerType currentCustomer;
};
#endif /* SERVERTYPE_H */
serverType.cpp:
#include "serverType.h"
serverType::serverType() {
status = "free";
transactionTime = 0;
}
serverType::~serverType() {
}
serverType::serverType(const serverType& other) {
status = other.status;
transactionTime = other.transactionTime;
currentCustomer = other.currentCustomer;
}
void serverType::setCustomer(CustomerType& newCustomer, int& transactionTime) {
currentCustomer = newCustomer;
this->transactionTime = transactionTime;
status = "busy";
}
void serverType::decrementTransactionTime() {
transactionTime--;
if (transactionTime == 0)
status = "free";
}
serverType& serverType::operator=(const serverType& other) {
status = other.status;
transactionTime = other.transactionTime;
currentCustomer = other.currentCustomer;
return *this;
}
main.cpp:
#include "queueType.h"
#include "serverType.h"
#include <ctime>
#include <climits>
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
int simulationTime, coneTime, shakeTime, currentTime = 0;
int customerID = 1, totalWaitTime = 0, customersServiced = 0;
double arrivalProb;
cout << "Time-driven ice cream shop simulation" << endl
<< "Enter the following information to begin:" << endl << endl;
cout << "Length of simulation (in minutes): ";
cin >> simulationTime;
cout << endl << "Probability of customer arrival each minute (example: 0.25): ";
cin >> arrivalProb;
cout << endl << "Minutes to make an ice cream cone: ";
cin >> coneTime;
cout << endl << "Minutes to make a shake: ";
cin >> shakeTime;
cout << endl << endl;
QueueType<CustomerType> Line;
serverType server;
float chance;
srand(time(0) % INT_MAX);
while (currentTime < simulationTime) {
chance = float (rand())/RAND_MAX;
if (chance < arrivalProb) {
if (!Line.isFull()) {
Line.addElement(CustomerType(currentTime, customerID));
customerID++;
} else {
cout << "Customer #" << customerID
<< " came during a full line and left!" << endl;
customerID++;
}
}
if (server.isFree() && (!Line.isEmpty())) { //going with 40% shake, 60% cone
customersServiced++;
if (chance < 0.4) {
server.setCustomer(Line.getFront(), shakeTime);
} else {
server.setCustomer(Line.getFront(), coneTime);
}
totalWaitTime += Line.getFront().getWaitTime();
Line.reposition();
} else if (!server.isFree()) {
server.decrementTransactionTime();
CustomerType *customerPointer = new CustomerType();
int position = 0, counter = 0;
Line.updateWaitTimes(customerPointer, position, counter);
while (customerPointer != NULL) {
(*customerPointer).incrementWaitTime();
Line.updateWaitTimes(customerPointer, position, counter);
}
delete customerPointer;
}
currentTime++;
}
cout << endl << endl << "Simulation complete." << endl << endl;
cout << "Total wait time: " << totalWaitTime << endl
<< "Customers serviced: " << customersServiced << endl
<< "Average wait time: " << float (totalWaitTime) / customersServiced;
return 0;
}
Note that the queueType copy constructor/overloaded =/destructor never getting called until the destructor does once in the very end. I also know I don't need to have a customerType (currentCustomer) as one of serverType's private members, but just for the sake of realism.
You are mismanaging memory here:
CustomerType *customerPointer = new CustomerType();
int position = 0, counter = 0;
Line.updateWaitTimes(customerPointer, position, counter);
You are allocating memory for customerPointer. Then you change the value of what customerPointer points to in the Line.updateWaitTimes function. Then you do this:
delete customerPointer;
So what you allocated and what you deleted have different values. You're corrupting the heap by attempting to delete an address that doesn't start at the beginning of the dynamically allocated block.
If what you are deleting is a pointer to dynamically allocated memory, i.e. you designed it this way, but is a "different" pointer than the original you started out with, then you need to rewrite your code so you're not doing this "pointer dance" between customerPointer and the Line.updateWaitTimes function.
This is just one of probably many issues with your code you need to fix. One fix is to quit with the manual memory mamagement within your main() function. Learn to write code that minimizes or eliminates the usage of raw naked pointers. Yes, your QueueType class must do memory management, sure, but that doesn't mean your main() has to do this also.
Also, your QueueType class maintains its own memory that should not be fooled around with by an outside entity. Look at your QueueType::updateWaitTimes function -- why is it giving a pointer to the passed in "element" pointer? You then use this pointer to your internal queue and finagle with it in main(), which gives disastrous results. Writing code like this means that you haven't totally grasped the meaning of "encapsulation".
This line likely as problem, as it can leave back as negative
back = (back - 1) % SIZE;
you probably meant something like
back = (SIZE + back - 1) % SIZE;
WOW. Just finally realized the reason it was crashing was how I changed back around in queueType::reposition and queueType::addElement, in reposition I didn't need to move back at all since it's just called after someone leaves the front, and in my add I meant to move back BACK one but used - not + and moved it forward...program fixed. Thank you for answers/comments

C++ Dynamically Define Function

I am on visual c++ working on a console calculator, I am creating a way to let the user define a custom linear function. Here is where I am stumped: Once I get the users desired name of the function, the slope, and the y-intercept, I need to use that data to create a callable function that I can pass to muParser.
In muParser, you define custom functions like this:
double func(double x)
{
return 5*x + 7; // return m*x + b;
}
MyParser.DefineFun("f", func);
MyParser.SetExpr("f(9.5) - pi");
double dResult = MyParser.Eval();
How could I dynamically create a function like this based on the users input for the values 'm' and 'b' and pass that to the 'DefineFun()' method?
This is what I have so far:
void cb_SetFunc(void)
{
string FuncName, sM, sB;
double dM, dB;
bool GettingName = true;
bool GettingM = true;
bool GettingB = true;
regex NumPattern("[+-]?(?:0|[1-9]\\d*)(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?");
EchoLn(">>> First, enter the functions name. (Enter 'cancel' to abort)");
EchoLn(">>> Only letters, numbers, and underscores can be used.");
try
{
do // Get the function name
{
Echo(">>> Enter name: ");
FuncName = GetLn();
if (UserCanceled(FuncName)) return;
if (!ValidVarName(FuncName))
{
EchoLn(">>> Please only use letters, numbers, and underscores.");
continue;
}
GettingName = false;
} while (GettingName);
do // Get the function slope
{
Echo(">>> Enter slope (m): ");
sM = GetLn();
if (UserCanceled(sM)) return;
if (!regex_match(sM, NumPattern))
{
EchoLn(">>> Please enter any constant number.");
continue;
}
dM = atof(sM.c_str());
GettingM = false;
} while (GettingM);
do // Get the function y-intercept
{
Echo(">>> Enter y-intercept (b): ");
sB = GetLn();
if (UserCanceled(sB)) return;
if (!regex_match(sB, NumPattern))
{
EchoLn(">>> Please enter any constant number.");
continue;
}
dB = atof(sB.c_str());
GettingB = false;
} while (GettingB);
// ------------
// TODO: Create function from dM (slope) and
// dB (y-intercept) and pass to 'DefineFun()'
// ------------
}
catch (...)
{
ErrMsg("An unexpected error occured while trying to set the function.");
}
}
I was thinking that there isn't a way to define an individual method for each user-defined-function. Would I need to make a vector<pair<double, double>> FuncArgs; to keep track of the appropriate slopes and y-intercepts then call them dynamically from the function? How would I specify which pair to use when I pass it to DefineFun(FuncStrName, FuncMethod)?
What you need (in addition to a script language interpreter) is called a "trampoline". There is no standard solution to create those, in particular since it involves creating code at runtime.
Of course, if you accept a fixed number of trampolines, you can create them at compile time. And if they're all linear, this might be even easier:
const int N = 20; // Arbitrary
int m[N] = { 0 };
int b[N] = { 0 };
template<int I> double f(double x) { return m[I] * x + b; }
This defines a set of 20 functions f<0>...f<19> which use m[0]...m[19] respectively.
Edit:
// Helper class template to instantiate all trampoline functions.
double (*fptr_array[N])(double) = { 0 };
template<int I> struct init_fptr<int I> {
static const double (*fptr)(double) = fptr_array[I] = &f<I>;
typedef init_fptr<I-1> recurse;
};
template<> struct init_fptr<-1> { };
I would keep it simple:
#include <functional>
std::function<double(double)> f; // this is your dynamic function
int slope, yintercept; // populate from user input
f = [=](double x) -> double { return slope * x + yintercept; };
Now you can pass the object f to your parser, which can then call f(x) at its own leisure. The function object packages the captured values of slope and yintercept.
GiNaC is C++ lib which can parse and evaluate math expressions.
Generating a fixed array of functions bindable to boost function.
Someone else already said about a similar method, but since I'd taken the time to write the code, here it is anyway.
#include <boost/function.hpp>
enum {
MAX_FUNC_SLOTS = 255
};
struct FuncSlot
{
double (*f_)(double);
boost::function<double(double)> closure_;
};
FuncSlot s_func_slots_[MAX_FUNC_SLOTS];
template <int Slot>
struct FuncSlotFunc
{
static void init() {
FuncSlotFunc<Slot-1>::init();
s_func_slots_[Slot - 1].f_ = &FuncSlotFunc<Slot>::call;
}
static double call(double v) {
return s_func_slots_[Slot - 1].closure_(v);
}
};
template <> struct FuncSlotFunc<0> {
static void init() {}
};
struct LinearTransform
{
double m_;
double c_;
LinearTransform(double m, double c)
: m_(m)
, c_(c)
{}
double operator()(double v) const {
return (v * m_) + c_;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
FuncSlotFunc<MAX_FUNC_SLOTS>::init();
s_func_slots_[0].closure_ = LinearTransform(1, 0);
s_func_slots_[1].closure_ = LinearTransform(5, 1);
std::cout << s_func_slots_[0].f_(1.0) << std::endl; // should print 1
std::cout << s_func_slots_[1].f_(1.0) << std::endl; // should print 6
system("pause");
return 0;
}
So, you can get the function pointer with: s_func_slots_[xxx].f_
And set your action with s_func_slots_[xxx].closure_
Try to embed to your application some script language. Years ago I was using Tcl for similar purpose - but I do not know what is the current time best choice.
Either you can start from Tcl or search yourself for something better:
See: Adding Tcl/Tk to a C application