STRUCT not taking values

closed account (NCRLwA7f)
I am having a problem assigning a value to a variable in a struct. I'm placing my whole code to see if anyone can spot a problem, but the area I am having trouble with is the function CHARGE_PAYMENTS, last function at the bottom of the page.

more detail:
in the last function CHARGE_PAYMENTS I am reading from file Month1 of customer charges and payments. what I am trying to do is match the customer account number with what is stored in one of the structs ( I have checked the struct SDbase Cstmr and all the customer data is saved (account#, name, address, balance). I figured that it would be easier to cycle through the customers to match the 1st account # entry on the file vs cycling the file entries against one customer at a time. The goal is to make a match between the saved record of a customer and an entry in Month1.txt in the 1st IF statement --> then in the second IF statement sends the value of "money" to either PAYMENT or CHARGE.
Each payment or charge variable can hold 10 and 15 entries respectively (a record)

If anyone can see what the problem is with my function at the bottom that would be very helpful. Also, how can I pass a STRUCT by reference when it has brackets? for some reason I get errors when placing & on it.
ex.
struct: Example myFile[x]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <iostream>
#include <fstream>
#include <string>
using namespace std;


//STRUCTS//
struct cInput
{
double payment[10];
double charge[15];
};


struct FNC
{
    double Balance;
    double tPayments; //total payments
    double tCharges;   //total cahrges
    cInput  Card;
};


struct SDbase
{
    int memberNumb;
    string name;
    string address;
    FNC account;
};




//PROTOTYPE//
int CNT_CUST();
void  READ_DATA_FILE(SDbase Cstmr[], int size);
void CHARGE_PAYMENTS(SDbase Cstmr[], int size);

//---------------------------------------------------

    ifstream CSTMR("Customer.txt");
    ifstream MNT1("Month1.txt");
    ifstream MNT2("Month2.txt");



int main()
{


    int size;
    size = CNT_CUST();
    
    SDbase Cstmr[size];
    FNC Records;
    cInput cCard;
    
    READ_DATA_FILE(Cstmr, size);
    CHARGE_PAYMENTS(Cstmr, size);
   
    
    
    CSTMR.close();
    MNT1.close();
    MNT2.close();

return 0;
}


//FUNCTION//


//Reads Customer file and counts the number of customers (lines)
int CNT_CUST()
{
    string value = " ";   int cnt = 0;

    while(getline(CSTMR, value))
        cnt++;
    return cnt;
}

//Reads Customer information to Struct SDbase
void  READ_DATA_FILE(SDbase Cstmr[], int size)
{
ifstream CSTMR("Customer.txt");

    int acc;
    char nm[16];
    char adrs[20];
    double bal;
    
    for(int i = 0; i < size; i++)
    {
        CSTMR >> acc;
        Cstmr[i].memberNumb = acc;
        
        CSTMR.ignore(3);
        
        CSTMR.get(nm, 16);
        Cstmr[i].name = nm;
        
        CSTMR.ignore(1);
        CSTMR.get(adrs, 20);
        Cstmr[i].address = adrs;
        
        CSTMR.ignore(11);
        CSTMR >> bal;
        Cstmr[i].account.Balance = bal;
    }
CSTMR.close();
}

//Submits Charges or Payments to Strut cInput using SDbase Cstmr
void CHARGE_PAYMENTS(SDbase Cstmr[], int size)
{
 ifstream MNT1("Month1.txt");

    int accNb = 0, x = 0;
    double money = 0;
    string tag;
    
    while(MNT1 >> accNb)
        {
            MNT1 >> tag;
            MNT1 >> money;

        for(int i = 0; i < size; i++)
            {
                if(accNb == Cstmr[i].memberNumb)
                    {
                        if(tag == "PAYMENT")
                        {
                            Cstmr[i].account.Card.payment[x] = money;
                            x++;
                        }
        
                        else
                        {
                            Cstmr[i].account.Card.charge[x] = money;
                            x++;
                        }
                    }
            }
    
        }
   MNT1.close();

}


This is what I need to do for this assignment.
there are 10 customers, each has a store account that either logs payments or charges to a personal store credit log. If the store owes the customer money, then the customer's account reflects it, and vice versa.
Last edited on
I believe you can't provide an array as argument as if it was a primitive variable type. At compile time, the length of an array that will be passed as an argument can not be known (since you don't know which array will be passed) so it is impossible to reserve the correct amount of memory.

You could pass the function a pointer to the array, since you know the type and you already provide a size parameter that should be sufficient too.
Hello DaRealFonz,

Nico has a point but I am not sure yet if I would agree with it. Generally an array passed to a function degrades to a pointer at the function. Not that you realize that it has happened.

Mort to the point, SDbase Cstmr[size]; will not work as "size" needs to be a constant number at compile time so the compiler knows how much space to reserve for the array. A possible fix could be something like this:

1
2
3
4
5
    constexpr MAXSIZE{ 50 };
    int size;
    size = CNT_CUST();
    
    SDbase Cstmr[MAXSIZE];

This creates an array larger than you will need and "size" is used to limit how much of the array that you have used.

At first look I find the function "READ_DATA_FILE" suspicious. Line 88 I believe is trying to open a file that is already open. I am thinking that at the end of "CNT_CUST" function that the file stream "CSTMR" should be closed an then reopened at line 88. What you may need to do for line 88 is re position the file pointer to the beginning of the file. Also wondering why you need the line like CSTMR.ignore(3);? It makes me thing that either the input file is set up wrong or you are trying to read the file the wrong way.

I would need an example of the input file to fully understand what is going on. Please post the input file or at least a small portion.

I will load up your program after lunch and see what else I can find.

Hope that helps,

Andy
closed account (NCRLwA7f)
Thank you to everyone taking the time help with this problem.
Here are the two files (Customer, Month1 and Month 2 are similar)

Line 86, the "READ_DATA_FILE" function, I'm reading in mixed data. This is the only way I could figure out how to separate the data into each individual parts to fill the respective fields in my struct SDbase Cstmr[size];. using CSTMR.ignore(); skips some blank spaces.

The file was reopened in the function because my understanding is that once the data is read, it will not go back and read again.

Customer.txt

631502   Legg T.         2839 Lionel Ave.               +16.30
180003   Hofstra M.      225 Smith Str                   -6.00
000963   Malioneyh P. J. 4344 Nero Rd                  +366.92
517724   Morier G. E.    19 Cumberland Ave              +60.40
432649   Hauser M.       2940 Toronto Rd                  0.00
817387   Currie W. D.    281 Harvard Str                +55.00
913478   Hoos R. Dadie   821 County Road               -100.00
713422   Smelly Tau      7281 Tobe Cir                    0.00
822412   Joe Sur Real    21 Ched Akins                   15.00
932384   Jones T Booker  125 West Texas Dr.             -20.00


So once again, the goal is to pull the monetary value from the Month1 file and match up the record with the same customer account number, then place the value in either payment or charge in each customers record.

Month1.txt

180003    PAYMENT       4.00
000963    CHARGE       23.88
817387    PAYMENT      55.00
000963    CHARGE       10.00
000963    PAYMENT      60.00
517724    CHARGE        3.43
631502    CHARGE      118.60
000963    CHARGE        15.75
932384    CHARGE       200.00
932384    CHARGE       270.00
932384    CHARGE       100.00
432649    CHARGE        50.00
913478    CHARGE        45.00
822412    CHARGE        23.50
713422    CHARGE        15.00
631502    CHARGE       100.00
913478    CHARGE        75.00
822412    CHARGE        43.50
000963    PAYMENT      125.00
713422    CHARGE        63.80
432649    PAYMENT       25.00
822412    CHARGE        63.50
932384    CHARGE       600.00
913478    PAYMENT      200.00
631502    PAYMENT       50.00
822412    CHARGE        73.50
713422    CHARGE        47.25
932384    PAYMENT      500.00
913478    CHARGE         5.00
432649    PAYMENT       25.00
822412    PAYMENT       50.00
631502    CHARGE        75.00
713422    PAYMENT      150.00
Hello DaRealFonz,

The file was reopened in the function because my understanding is that once the data is read, it will not go back and read again.

Partially true to more precise what happens is that when something tries to read past the end of file the state bits of the file stream are set, e.g., EOF and fail, These need to be cleared before the file stream can be used again. These two lines will do the same as opening the file stream again, which should be closed before it is reopened.

1
2
CSTMR.clear();
CSTMR.seekg(0, std::ios::beg);


The first line resets the state bits so the stream can be used again and the second line places the file pointer at the beginning. In more advanced use line 2 can be used to move the file pointer around the file.

My next question: Are you stuck with the file layouts or can they be changed? If you can change the file layouts there are better ways to read these files.

That said the for loop for reading the file works.

Now that I have something to work with I can check out the rest of the program.

Hope that helps,

Andy
closed account (NCRLwA7f)
No, unfortunately, I cannot modify the file layout.
Since this is just my second class to c++ we have not covered any really advanced topics.
What you have mentioned and explained so far is very reasonable.

Also, if you know of a simpler way of reworking the void CHARGE_PAYMENTS(SDbase Cstmr[], int size) function, do advice.

On a side note: For line 136 and 142 since I am using nested structs am I correctly defining the location for the variable payments and charges respectively?
Last edited on
Hello DaRealFonz,

I did make one change to the "CHARGE_PAYMENTS" function which is actually two changes.

First
1
2
3
4
5
6
7
struct cInput
{
	double payment[10]{};
	double charge[15]{};
	size_t pIndex{};  // <--- Added.
	size_t cIndex{};  // <--- Added.
};


In the function:
1
2
3
4
5
6
7
8
9
10
11
				if (tag == "PAYMENT")
				{
					Cstmr[i].account.Card.payment[Cstmr[i].account.Card.pIndex] = money;
					Cstmr[i].account.Card.pIndex++;
				}

				else
				{
					Cstmr[i].account.Card.charge[Cstmr[i].account.Card.cIndex] = money;
					Cstmr[i].account.Card.cIndex++;
				}

Notice how I replaced and dealt with "x".

The problem here is that each time you add one to "x" it would place the value in the wrong place in the arrays as you progress through "Month1.txt" eventually "x" would exceed the boundary of the arrays and the program would crash. This way each instance of "Cstmr" will store a value of "cIndex" and "pIndex" that is unique to that instance. At some point those values should be checked to make sure that they do not exceed the array size. Not a big concern right now unless you double or triple the sizr of "Month1,txt".

On a side note: For line 136 and 142 since I am using nested structs am I correctly defining the location for the variable payments and charges respectively?

Yes, 99% of what you started with works. The changes I made are small compared to the whole program.

Hope that helps,

Andy
closed account (NCRLwA7f)
Hey, Handy Andy,

ok so I made all the changes to the code and I am very satisfied with the results, everything appears to be on point.

I want to make sure if I have understood the charges correctly.

When I had x++ in the "CHARGE_PAYMENTS" function, I was using the variable x as a counter for all times the loop ran in that particular IF statement.
By changing it to
1
2
Cstmr[i].account.Card.pIndex
Cstmr[i].account.Card.cIndex
It made a count variable for each individual record making it more predictable and accurate. (in a nutshell)

Anyway, the code is running great, now I just need to finish it but I think it is manageable now. Thank you for the help, It was a stressful 2 days of debugging.
Last edited on
Hello DaRealFonz,

Before I get side tracked again I want to mention that you should initialize all your variables when you define them. The only exception I can think of is a "class", but that initialization is done differently. Look at my earlier post where I listed the struct "cInput" the empty {} braces will initialize numeric variables to zero, strings to empty strings. Not sure what a "char" would end up with, still need to check that out.

When I had x++ in the "CHARGE_PAYMENTS" function, I was using the variable x as a counter for all times the loop ran in that particular IF statement.
By changing it to

1
2
Cstmr[i].account.Card.pIndex
Cstmr[i].account.Card.cIndex

It made a count variable for each individual record making it more predictable and accurate. (in a nutshell)


Yes, you understand what I did here.

One last thought in the "CHARGE_PAYMENTS" function in the nested if/else statement when you are dealing with "payment" or "charge" this could be a good place to adjust the balance unless you had something else in mind.

Good work. Glad I could help,

Andy

P.S. Do not forget to green check this if you are finished.
Last edited on
Topic archived. No new replies allowed.