C++ error "undefined reference to" when compiling test code [duplicate] - c++

This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 8 years ago.
I've been trying to compile my code and finally got it down to just one error. If someone could explain whats going it would be great!
Here is my header file
rates.h
#ifndef RATES_H
#define RATES_H
#include <vector>
namespace power {
//! Energy usage over a period of time.
struct EnergyInterval
{
//! Constant or average power used during the interval [W].
const double power;
//! Length of the interval [s].
const double time;
};
//! The cost of using energy, within a specified bound.
struct Rate
{
/*!
* How much power this rate applies to, e.g., "the
* first 650 kWh", "the next 400 kWh" [kWh]
*/
const double amount;
//! The price of energy in this block [cents/kWh]
const double price;
};
/*!
* Calculates the cost of using energy over a billing period,
* broken down into time slices. Costs are calculated with the
* commonly-employed "so much at this price, so much at that price"
* model of rate schedules.
*
* #param[in] usage energy consumed in the billing period
* #param[in] rateSchedule the schedule of energy costs
*
* #returns total cost of energy used in the billing period [cents]
*/
double EnergyCost(const std::vector<EnergyInterval>& usage,
const std::vector<Rate>& rateSchedule);
} // namespace power
#endif
and rates.cpp
#include "rates.h"
#include <vector>
using namespace power;
double EnergyCost(const std::vector<EnergyInterval>& usage,
const std::vector<Rate>& rateSchedule){
double energyUsage = 0;
double cost = 0;
const double uSize = usage.size();
const double rsSize = rateSchedule.size();
double totalAmount = 0;
double secondTotal = 0;
for(int i=0; i < uSize; i++){
energyUsage += (usage[i].power*(usage[i].time/3600));
}
for(int j=0; j < rsSize; j++){
totalAmount += rateSchedule[j].amount;
if (energyUsage > totalAmount){
cost =+ rateSchedule[j].amount*rateSchedule[j].price;
}else if(energyUsage < totalAmount){
cost =+ (energyUsage - secondTotal)*rateSchedule[j].price;
break;
}
secondTotal += rateSchedule[j].amount;
}
return cost;
}
and test.cpp
#include "rates.h"
#include <vector>
#include <iostream>
using namespace std;
using namespace power;
int main()
{
vector<EnergyInterval> usage =
{
{.power = 60, .time = 60*60},
{.power = 2460, .time = 35*60},
{.power = 60, .time = 60*60}
};
vector<Rate> rateSchedule =
{
{.amount = 2000, .price = 10},
{.amount = 5000, .price = 5}
};
double totalCost = EnergyCost(usage, rateSchedule);
cout<<"The total cost is: " << totalCost;
return 0;
}
and when compiling in the command line I use:
g++ -std=c++11 -g test.cpp rates.cpp -o test
This is the error
C:>g++ -std=c++11 -g test.cpp rates.cpp -o test
C:\cchqT8Ro.o: In function `main':
C:\test.cpp:22: undefined reference to `power::EnergyCost(std::vector<power::EnergyInterval,
std::allocator<power::EnergyInterval> > const&, std::vector<power::Rate,
std::allocator<power::Rate> > const&)'
collect2.exe: error: ld returned 1 exit status
Any help or a push in the right direction would be great! Thanks!

In rates.cpp, you are creating a global function EnergyCost which is not the same as power::EnergyCost. This means that power::EnergyCost is still unimplemented.
You should implement EnergyCost inside the power namespace as follows:
// rates.cpp
namespace power {
double EnergyCost(const std::vector<EnergyInterval>& usage,
const std::vector<Rate>& rateSchedule){
// ..
// your code
// ..
}
}

I think EngergyCost function implementation should contained in the power namespace.
namespace Power {
double EnergyCost(const std::vector<EnergyInterval>& usage,
const std::vector<Rate>& rateSchedule){
double energyUsage = 0;
double cost = 0;
const double uSize = usage.size();
const double rsSize = rateSchedule.size();
double totalAmount = 0;
//:
}
}

Related

Segfault after adding #pragma loop

I faced a little trouble. I'm not sure if I can understand it.
So, I have some code. And I'm trying to add #pragma loop(hint_parallel(8)) statement for a few loops in the code.
When I compile that using necessary compilation options which are actually like this:
gcc -w -funroll-loops -O2 -fno-inline -fipa-pta -msse2
-funsafe-math-optimizations -ftree-vectorizer-verbose=1 -fopt-info-optimized=logs/optOpt.txt -shared -fPIC singleThread.cpp
I get segmentation fault.
fish: './a.out' terminated by signal SIGSEGV (Address boundary error)
The point is that I have no idea why it is. I suspected that it could be a problem with a constant that is used in these loops. But I don't think that this is related. if I just compile this code using -O0 optimisation it works fine (because complier doesn't vectorise something I guess).
Could you please take a look on the code below and suggest me in which direction I should check.
Thanks.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <random>
#include <cstdio>
#include <set>
#include <fstream>
#include <cstdint>
#include <climits>
using namespace std;
const int STRING_HASH_SIZE = 32;
int convert(vector<string> &inputVector, const char **outputArray);
void printCollisions(const char **charArray, int size);
void printArray(const char **arrayToPrint, int size);
int getHashCode(const char *characters, unsigned long size);
string getRandomString();
void writeFileIfNeeded(vector<string> &vector, bool needToWrite);
vector<string> generateStringsVector(int size, bool isNeedToWriteFile);
/**
* main method is present to test these native code.
* to perform some external operation we should use another method.
* #return
*/
int main() {
/**
* The constant represents number of strings that will be generated
* in the string vector generation.
*/
const int STRING_NUMBERS = 100000;
vector<string> inputVector = generateStringsVector(STRING_NUMBERS, false);
#pragma pack 8
const char *charArray[inputVector.size()];
int hashResult = convert(inputVector, charArray);
if (hashResult != 0) {
return 0;
}
printCollisions(charArray, STRING_NUMBERS);
}
/**
* Converts an input vector to char array.
* Getting a hash of
* Returns 0 if conversion from vector to array has been successfully performed.
* #param inputVector [ input array reference ]
* #param outputArray [ a char array that would contain char sequences from vector ]
* #return [ hash sum (int)]
*/
int convert(vector<string> &inputVector, const char **outputArray) {
int hashSum = 0;
#pragma loop(hint_parallel(8))
for (int i = 0; i < inputVector.size(); i++) {
outputArray[i] = inputVector[i].c_str();
}
#pragma loop(hint_parallel(8))
for (auto &i : inputVector) {
hashSum += getHashCode(i.c_str(), i.length());
}
int stringHashSize = STRING_HASH_SIZE;
#pragma loop(hint_parallel(8))
for (int i = 0; i < inputVector.size(); i++) {
hashSum -= getHashCode(outputArray[i], stringHashSize);
}
if (hashSum != 0) {
cout << "\nConversion isn't succeeded, hash = " << hashSum << endl;
} else {
cout << "\nConversion succeeded" << endl;
}
return hashSum;
}
/**
* Prints count and percentage of collisions in array hash codes
* #param charArray
* #param size
*/
void printCollisions(const char **charArray, int size) {
set<int> setOfHashes;
int stringHashSize = STRING_HASH_SIZE;
#pragma loop(hint_parallel(8))
for (int i = 0; i < size; i++) {
setOfHashes.insert(getHashCode(charArray[i], stringHashSize));
}
unsigned long collisions = size - setOfHashes.size();
cout << collisions << "/" << size << " " << 100.0 * collisions / size << "% of collisions";
}
/**
* Prints input char array
* #param arrayToPrint
*/
void printArray(const char **arrayToPrint, int size) {
cout << "\nPrinted array size = " << size << endl;
for (int i = 0; i < size; i++) {
cout << arrayToPrint[i] << ":" << getHashCode(arrayToPrint[i], STRING_HASH_SIZE) << endl;
}
}
/**
*
* #param characters
* #return
*/
int getHashCode(const char *characters, unsigned long size) {
int hash = 0;
#pragma loop(hint_parallel(8))
for (int i = 0; i < size; i++) {
hash = (31 + hash) * (characters[i]);
}
return hash;
}
/**
* Get a random String from alphabetical char sequence.
* #return a randomized string according to an alphabet.
*/
string getRandomString() {
string str("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
random_device rd;
mt19937 generator(rd());
shuffle(str.begin(), str.end(), generator);
return str.substr(0, STRING_HASH_SIZE);
}
/**
* Generates a vector with random strings
* #param size - an int value that will be used as size of a generated vector
* #return reference to generated vector.
*/
vector<string> generateStringsVector(int size, bool isNeedToWriteFile) {
vector<string> charArray;
#pragma loop(hint_parallel(8))
for (int i = 0; i < size; i++) {
string str = getRandomString();
charArray.push_back(str);
}
writeFileIfNeeded(charArray, isNeedToWriteFile);
return charArray;
}
/**
* Writes file with name according to vector size (e.g. 100000.csv)
* if needToWrite is true
* #param vector
* #param needToWrite
*/
void writeFileIfNeeded(vector<string> &vector, bool needToWrite) {
if (needToWrite) {
ofstream csvFile;
string filename = to_string(vector.size()) + ".csv";
csvFile.open(filename, fstream::out);
for (const auto &i : vector) {
csvFile << i << "\n";
}
csvFile.close();
}
}
What is causing the segmentation fault is the way you compile your code and not the pragmas (which don't have any effect in gcc anyway, see below):
gcc -w -funroll-loops -O2 -fno-inline -fipa-pta -msse2
-funsafe-math-optimizations -ftree-vectorizer-verbose=1 -fopt-info-optimized=logs/optOpt.txt -shared -fPIC singleThread.cpp
By using -shared -fPIC you are creating a DSO (dynamic shared object). If you try to execute this file, you'll get an invalid PC (program counter) and your program will crash immediately. You must compile your code without -shared -fPIC (and use -pie -fPIE if you need a position-independent executable).
Also, for compiling C++ code you should normally use g++ instead of gcc.
The given pragmas should not have any effect on your code, as these ones are only understood by Microsoft Visual Studio. Add -Wall to your compile options and gcc will show you the respective warnings.
In any case, you should get rid of vendor-specific pragmas and use standardized solutions like OpenMP instead (compile with -fopenmp). That way, you are a step closer to writing compiler-independent code.
As for the parallelized loops, you should make sure you don't run into race conditions or other synchronization failures. For example, to compute a sum, #pragma omp parallel for reduction(+: sum) is your friend in OpenMP (reference sheet).
Disclaimer: I have used gcc 7.3.0 on x86_64 (CentOS Linux).

error LNK2001 when using complex pow(),log(),exp() in VC++6

I work with VC++6 and I want to use complex power function (pow()) in one of my self-made function.
I wrote my necessary functions in a header file (.h).
Here is my function in the header file:
#include "MrServers/MrVista/Ice/IceIdeaFunctors/DllInterface.h"
// Base class:
#include "MrServers/MrVista/Include/Ice/IceUtils/IceImageReconFunctors.h"
#include "MrServers/MrVista/Include/Ice/IceUtils/IceUtils.h"
#include "MrServers/MrVista/Include/Ice/IceAlgos/IceWrapper.h"
// C++ class:
#include <vector>
#include <iostream>
#include <math.h>
#include <cmath>
#include <numeric>
#include <complex>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
// #define ACC 40.0 //Make larger to increase accuracy.
// #define BIGNO 1.0e10
// #define BIGNI 1.0e-10
// #define PI 3.14159265
typedef std::vector <float> array_1d;
typedef std::vector <array_1d> array_2d;
typedef std::vector <array_2d> array_3d;
using namespace std;
IceWrapper myice;
...other function...
void Hankel_Transform(IceAs& dstAs, IceAs& srcAs, int M, int N, int nCha, int nPHS, float delta_r, float delta_rho, int r_range)
{
//parameters for Hankel Transform
//M: Number of samples on a spoke
//N: total number of orders
//rho: a 1D vector contains rho values (in source image)
//r: a 1D vector contains r values (for final image)
//r_range: length of vector r
double res = 0.005;
int z_ord = (int) N/2;
float pii = 3.14159265f; // f means float. to avoid error
array_3d bessel_table(N, array_2d(M, array_1d(r_range)));
// load "bessel_table" from PDS// (will be added later)
// load bessel_table
//array_2d bessel_table;
//bessel_table = read_bessel_txt();
// create pointer in order to access srcAs and dstAs
CMPLX *p_srcAs = (CMPLX*) srcAs.calcSplObjStartAddr();
CMPLX *p_dstAs = (CMPLX*) dstAs.calcSplObjStartAddr();
// Hankel Formula //
//int my_ind;
float my_j;
float r;
float rho;
complex<float> temp (0.0 , 0.0);
complex<float> fn_r (0.0 , 0.0);
complex<float> immm(0, 1);
float ipow =0.0f;
//CMPLX *temp;
//CMPLX *fn_r;
for (int phase = 0; phase < nPHS; phase++)
{ for(int ord = -(N/2); ord < (N/2); ord++) //lines
{ for(int nc = 0; nc < nCha; nc++) //channels
{ for(int rr = 0; rr < r_range; rr++) //columns
{
r=(float)(rr+1)*delta_r;
fn_r=complex<float>(0.0 , 0.0);
//fn_r -> re = 0;
//fn_r -> im = 0;
for(int rhoo = 0; rhoo < M; rhoo++)
{
rho = delta_rho/2 + (float)rhoo*delta_rho;
// to avoid redunduncy in calculation of besselj in cha and phase
if ( nc == 0 && phase == 0 )
{
my_j = bessj(ord , (float)(pii*rho*r));
bessel_table[ord+z_ord][rhoo][rr] = my_j;
}
else
{
my_j = bessel_table[ord+z_ord][rhoo][rr];
}
//my_ind = (int)floor((float)(pii*rho*r/res)); // should be "round" :(
//if(my_ind==0){my_ind=1;}
//my_j = bessel_table[ord+z_ord][my_ind-1]; // dar c++ andis ha az 0 shoru mishe!!
// bayad esme jadval "bessel_table" bashad!
temp = complex<float>(rho*my_j*p_srcAs->re , rho*my_j*p_srcAs->im);
fn_r += temp;
p_srcAs++;
}
ipow = (float)ord;
//ICE_OUT(std::pow<complex>(fn_r,2));
fn_r *= std::pow(immm,ipow); //exp(ipow*log(immm));//pow(immm,ipow);
p_dstAs->re = real(fn_r);
p_dstAs->im = imag(fn_r);
p_dstAs++;
if(rr != (r_range-1) )
{
p_srcAs -= M;
}
}
}
}
}
}
Without putting this line: fn_r *= std::pow(immm,ipow) , everything works fine and the project correctly compiles. But when i try to put this line, or using exp() or log() function compiling fails with this errors:
IcePFTd_HankelFunctor.obj : error LNK2001: unresolved external symbol "struct _STL::complex<float> __cdecl _STL::pow(str
uct _STL::complex<float> const &,float const &)" (?pow#_STL##YA?AU?$complex#M#1#ABU21#ABM#Z)
/n4/x86/prod/bin/IcePFTd.dll : fatal error LNK1120: 1 unresolved externals
make: *** [\n4\x86\prod/bin/IcePFTd.dll] Error 96
I also tried to use the expression exp(ipow*log(immm)) instead of power function, but it also fails with the same error.
I tried to use this function (pow()) in a simple code in visual studio 2016,it works fine and it's just okay to consider #include <complex> in the first lines of the header.
Do you have any idea?
Any help will be appreciated.

using the return value of one function as an argument in another function in c++

I found some related answers but couldn't understand it clearly because the codes were complicated for me.
In this program I used the dif () to find the difference in price then stored the return value total in variable difrnc. Then I used the difrnc variable as an argument for the function call
inflation=inflan(difrnc,lyp) //(calculates the inflation)
Instead of storing the total in variable difrnc can I directly use the answer from the function dif() as an argument for the function inflan() in its definition and how?
Sorry if it is a repeated question it would be great if someone could explain it using this program.
#include<iostream>
using namespace std;
double dif(double lp,double cp);//cp= current price,lp= last price, current
double inflan(double difference,double lastyp);
double cost(double cp,double inrate);
int main()
{
double lyp,cyp,difrnc,inflation,one_year_cost; // lyp = last year price,cyp=current year price,
for(int i=0;i>=0;i++)
{
cout<<"Enter current years price :";
cin>>cyp;
cout<<"Enter last Years price: ";
cin>>lyp;
difrnc=dif(lyp,cyp);
if(difrnc<0)
{
cout<<"price decreased by "<<difrnc<<endl;
}
else
{
cout<<"price increased by "<<difrnc<<endl;
}
inflation=inflan(difrnc,lyp);
one_year_cost=cost(cyp,inflation);
cout<<one_year_cost<<endl;
}
}
// to find the difference in price
double dif(double lp,double cp)
{
double total;
total=cp-lp;
return(total);
}
// to find the inflation
double inflan(double difference,double lastyp)
{
double inrate;
inrate=difference/lastyp;
return(inrate);
}
// to find estimated cost in one year
double cost(double cp,double inrate)
{
double
totalc=cp+inrate;
return(totalc);
}
Yes you can like this inflatio n = inflan(dif(lyp,cyp),lyp);
However, since you use the function returned value more than once, it make more sense to keep it as it is.
Aside from your current issue in your functions you can simplify them by removing their local variables and just simply return the evaluated expression.
For Example: You have this ->
double cost( double cp, double inrate ) {
double totalc = cp + inrate;
return totalc;
}
It is safe and efficient to do this instead:
double cost( double cp, double inrate ) {
return cp + inrate;
}
You can do that for any simple function that doesn't go through any loops.
As for your actual issue check out this quick program;
Sample.cpp
#include <iostream>
int five() {
return 5;
}
int ten() {
return 10;
}
int add( int a, int b ) {
return a + b;
}
int main() {
std::cout << add( five(), ten() ) << std::endl;
return 0;
}
Set a break point within the first line of your main function and step through the code line by line while examining your stack calls as well as your local and auto variables to see what is happening on each line of your functions to see the values that are being assigned to each variable.

Why do I keep getting unresolved externals for my program?

I keep getting unresolved externals when I try to run this code in Visual Studio.
Structure Time Assignment for College
Create a structure called Time that has members days, hours, minutes,
and seconds as ints.
Create an instance of Time and initialize the members.
Create a function to normalize the time when values are added to it.
For example, after adding values to the hours, call the normalize
function which should see if the hours > 24.
If so, add 1 to the days member and reset hours by subtracting 24
from the current value.
DO the same for minutes and seconds over 59.
Your main program should add values to hours, minutes and seconds and
after each, call the normalize function to properly set the values.
Output the members after each update. Assume hours use a 24-hour
clock.
#include <iostream>
using namespace std;
Struct time
{
int days = 0;
int hours = 0;
int minutes = 0;
int seconds = 0;
};
void normalize();
int main()
{
int clockRepeating;
for (clockRepeating = 0; clockRepeating < 150; clockRepeating++)
{
normalize();
}
return 0;
}
void normalize(Time &timenormalize)
{
if (timenormalize.days > 31)
timenormalize.days = 1;
if (timenormalizehours > 24)
{
timenormalize.hours = 0;
timenormalize.days++;
}
if (timenormalize.minutes > 59)
{
timenormalize.minutes = 0;
timenormalize.hours++;
}
if (time normalize.seconds > 59)
{
timenormalize.seconds = 0;
timenormalize.minutes++;
cout << timenormalize.days, timenormalize.hours, timenormalize.minutes, timenormalize.seconds;
}
else
timenormalize.seconds++;
cout << timenormalize.days, timenormalize.hours, timenormalize.minutes,timenormalize.seconds;
The signature you declared for void normalize(); does not match the signature as it's defined in this file (void normalize(Time &timenormalize)).
Here's a fixed up version of your code. First the compilation errors:
changed Struct to struct: struct is a key word, must be lowercase;
changed Time to struct time in void normalize(..): symbols are case sensitive: Time isn't declared, but struct time is;
added missing . to if (timenormalizehours): if (timenormalize.hours);
added } to the end of the file (probably copy/paste error).
And then the linker error undefined reference to 'normalize':
change function declaration void normalize() to void normalize(struct time &): you declare the normalize function without parameters, but define it with one parameter.
And then finally the compilation error this introduces:
change the normalize(); call to normalize( mytime ); because it takes a paramter
and declare a local variable struct mytime to pass as the parameter.
#include <iostream>
using namespace std;
struct time
{
int days = 0;
int hours = 0;
int minutes = 0;
int seconds = 0;
};
void normalize(struct time &);
int main()
{
int clockRepeating;
struct time mytime;
for (clockRepeating = 0; clockRepeating < 150; clockRepeating++)
{
normalize( mytime );
}
return 0;
}
void normalize(struct time &timenormalize)
{
if (timenormalize.days > 31)
timenormalize.days = 1;
if (timenormalize.hours > 24)
{
timenormalize.hours = 0;
timenormalize.days++;
}
if (timenormalize.minutes > 59)
{
timenormalize.minutes = 0;
timenormalize.hours++;
}
if (timenormalize.seconds > 59)
{
timenormalize.seconds = 0;
timenormalize.minutes++;
cout << timenormalize.days, timenormalize.hours, timenormalize.minutes, timenormalize.seconds;
}
else
timenormalize.seconds++;
cout << timenormalize.days, timenormalize.hours, timenormalize.minutes,timenormalize.seconds;
}
It prints a series of 0. Now it's up to you to put some values in struct time mytime. I hope this helps!

Should I be attempting to return an array, or is there a better solution?

A problem set for people learning C++ is
Write a short program to simulate a ball being dropped off of a tower. To start, the user should be asked for the initial height of the tower in meters. Assume normal gravity (9.8 m/s2), and that the ball has no initial velocity. Have the program output the height of the ball above the ground after 0, 1, 2, 3, 4, and 5 seconds. The ball should not go underneath the ground (height 0).
Before starting C++ I had a reasonable, but primarily self taught, knowledge of Java. So looking at the problem it seems like it ought to be split into
input class
output class
calculations class
Physical constants class (recommended by the question setter)
controller ('main') class
The input class would ask the user for a starting height, which would be passed to the controller. The controller would give this and a number of seconds (5) to the calculations class, which would create an array of results and return this to the controller. The controller would hand the array of results to the output class that would print them to the console.
I will put the actual code at the bottom, but it's possibly not needed.
You can probably already see the problem, attempting to return an array. I'm not asking how to get round that problem, there is a workaround here and here. I'm asking, is the problem a result of bad design? Should my program be structured differently, for performance, maintenance or style reasons, such that I would not be attempting to return an array like object?
Here is the code (which works apart from trying to return arrays);
main.cpp
/*
* Just the main class, call other classes and passes variables around
*/
#include <iostream>
#include "dropSim.h"
using namespace std;
int main()
{
double height = getHeight();
int seconds = 5;
double* results = calculateResults(height, seconds);
outputResults(results);
return 0;
}
getHeight.cpp
/*
* Asks the user for a height from which to start the experiment
* SI units
*/
#include <iostream>
using namespace std;
double getHeight()
{
cout << "What height should the experiment start at; ";
double height;
cin >> height;
return height;
}
calculateResults.cpp
/*
* given the initial height and the physical constants, the position of the ball
* is calculated at integer number seconds, beginning at 0
*/
#include "constants.h"
#include <cmath>
#include <iostream>
using namespace std;
double getPosition(double height, double time);
double* calculateResults(double height, int seconds)
{
double positions[seconds + 1];
for(int t = 0; t < seconds + 1; t++)
{
positions[t] = getPosition(height, t);
}
return positions;
}
double getPosition(double height, double time)
{
double position = height - 0.5*constants::gravity*pow(static_cast<double>(time), 2);
if( position < 0) position = 0;
//Commented code is for testing
//cout << position << endl;
return position;
}
outputResults.cpp
/*
* Takes the array of results and prints them in an appropriate format
*/
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
void outputResults(double* results){
string outputText = "";
//The commented code is to test the output method
//Which is working
//double results1[] = {1,2,3,4,5};
//int numResults = sizeof(results1)/sizeof(results1[0]);
int numResults = sizeof(results)/sizeof(results[0]);
//cout << numResults; //= 0 ... Oh
for(int t = 0; t < numResults; t++)
{
ostringstream line;
line << "After " << t << " seconds the height of the object is " << results[t] << "\r";
outputText.append(line.str());
}
cout << outputText;
}
And finally a couple of headers;
dropSim.h
/*
* dropSim.h
*/
#ifndef DROPSIM_H_
#define DROPSIM_H_
double getHeight();
double* calculateResults(double height, int seconds);
void outputResults(double* results);
#endif /* DROPSIM_H_ */
constants.h
/*
* Contains physical constants relevant to simulation.
* SI units
*/
#ifndef CONSTANTS_H_
#define CONSTANTS_H_
namespace constants
{
const double gravity(9.81);
}
#endif /* CONSTANTS_H_ */
I would say that you're over-engineering a big solution to a little problem, but to answer your specific question:
Should my program be structured differently, for performance, maintenance or style reasons, such that I would not be attempting to return an array like object?
Returning an array-like object is fine. But that doesn't mean returning an array, nor does it mean allocating raw memory with new.
And it's not restricted to return values either. When you're starting out with C++, it's probably best to just forget that it has built-in arrays at all. Most of the time, you should be using either std::vector or std::array (or another linear collection such as std::deque).
Built-in arrays should normally be viewed as a special-purpose item, included primarily for compatibility with C, not for everyday use.
It may, however, be worth considering writing your computation in the same style as the algorithms in the standard library. This would mean writing the code to receive an iterator to a destination, and writing its output to wherever that iterator designates.
I'd probably package the height and time together as a set of input parameters, and have a function that generates output based on those:
struct params {
double height;
int seconds;
};
template <class OutIt>
void calc_pos(params const &p, OutIt output) {
for (int i=0; i<p.seconds; i++) {
*output = get_position(p.height, i);
++output;
}
}
This works somewhat more clearly along with the rest of the standard library:
std::vector<double> results;
calc_pos(inputs, std::back_inserter(results));
You can go a few steps further if you like--the standard library has quite a bit to help with a great deal of this. Your calc_pos does little more than invoke another function repeatedly with successive values for the time. You could (for example) use std::iota to generate the successive times, then use std::transform to generate outputs:
std::vector<int> times(6);
std::iota(times.begin(), times.end(), 0);
std::vector<double> distances;
std::transform(times.begin(), times.end(), compute_distance);
This computes the distances as the distance dropped after a given period of time rather than the height above the ground, but given an initial height, computing the difference between the two is quite trivial:
double initial_height = 5;
std::vector<double> heights;
std::transform(distances.begin(), distances.end(),
std::back_inserter(heights),
[=](double v) { return max(initial_height-v, 0); });
At least for now, this doesn't attempt to calculate the ball bouncing when it hits the ground--it just assumes the ball immediately stops when it hits the ground.
You should get rid of self-allocated double * and use std::vector<double> instead. It's not difficult to learn and a basic step in modern C++
This is how I would solve the problem:
#include <cmath>
#include <iostream>
#include <iomanip>
using std::cin;
using std::cout;
using std::endl;
using std::sqrt;
using std::fixed;
using std::setprecision;
using std::max;
using std::setw;
static const double g = 9.81;
class Calculator {
public:
Calculator(double inh) : h(inh)
{
}
void DoWork() const {
double tmax = sqrt(h / ( g / 2));
for (double t=0.0; t<tmax; t+=1.0) {
GenerateOutput(t);
}
GenerateOutput(tmax);
}
private:
void GenerateOutput(double t) const {
double x = g * t * t / 2;
double hremaining = max(h - x, 0.0);
cout << fixed << setprecision(2) << setw(10) << t;
cout << setw(10) << hremaining << endl;
}
double h;
};
int main() {
double h(0.0);
cout << "Enter height in meters: ";
cin >> h;
if (h > 0.0) {
const Calculator calc(h);
calc.DoWork();
} else {
return 1;
}
return 0;
}