Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
I'm knocking my head against the wall. Here's what I want to do. I have a class called Record. I have another class called Field. A Record is made of multiple Fields, and each Field has several attributes.

In this code sample, all the field definitions are hard coded. At some point, they will be derived at run time, but that's not my issue right now. I'll typically only have 1 record.

Also, for indexing through this series of fields at runtime, I'm going to assume that I want to do pointer arithmetic to index through the array. I still need that definition too.

I can't figure out how to define my classes to get this thing to compile. Any advice is greatly appreciated.

(I could code this in Ruby with my eyes closed... :()

Code:
#include <iostream>
using namespace std ; 

// Field class definition 
class Field { 
	int offset ;      // Offset in the record to this field. 
	char datatype ;   // Datatype of this field.  char, num, dec, flt, dble 
	int len ;         // Number of bytes occupied by this field in the input record 
	int maxlength ;   // length of this field in the output record 
	int scale ;       // If packed decimal, this is the number of digits to the right of the decimal point. 
public: 
	void setv(int offset, int len, char dt, int maxlength, int scale) ; // set the values for the field. 
	int  get_offset() ; 
	char get_datatype() ; 
	int  get_len() ; 
	int  get_maxlength() ;
	int  get_scale() ; 
} ; 

int Field::get_offset()    {  return offset   ; } 

char Field::get_datatype() { return datatype  ; } 

int Field::get_len()       { return len       ; } 

int Field::get_maxlength() { return maxlength ; } 

int Field::get_scale()     { return scale     ; } 


// Record class definition 
class Record { 
public: 
	Field field[5] ;   // I need an array of 5 elements.
	Record() ; // Constructor 
} ; 

// Constructor for the Record class 
Record::Record() { 
	cout << "Here in Record Constructor..." << endl ;  
	Field field[5] ;     // An array of Fields. 
	field[0].setv(   0,  1, 'c' ,  1, 0 ) ; // parms are: ( offset, length in bytes, data type,  
	field[1].setv(   1,  2, 'c' ,  2, 0 ) ; // max length needed in output record, scale for decimal values) 
	field[2].setv(   3,  2, 'c' ,  2, 0 ) ;
	field[3].setv(   5,  2, 'c' ,  2, 0 ) ;
	field[4].setv(   7,  4, 'c' ,  4, 0 ) ;
}

int main (int argc, char * const argv[]) {
	Record rec ;          // Initialize the Record & Field layouts 	 
	for (int i = 0 ; i < Record::field.length ; ++i) { 
		printf("Record %02d: offset=%02d, length=%02d, type=%c, Max Length=%02d, Scale=%02d\n", 
				i, 
				rec.field[i].get_offset() , 
				rec.field[i].get_len() , 
				rec.field[i].get_datatype() , 
				rec.field[i].get_maxlength() , 
				rec.field[i].get_scale() ) ; 
	}  
    return 0;
}

Todd
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
7
A few things: 1) put your classes in separate .h and .cpp files, and 2) your main() function goes outside of any classes.
 

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
Hi

Just had a quick look and noticed you've got an error in your for loop. I think it should be:-
Code:
for (int i = 0 ; i < rec.field[ i ].get_len() ; ++i)


hope this helps!

b e n
 

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
Hi Todd

In your for loop you are accessing the fields of the record directly. I think it would be best to add a couple of methods to Record to allow this:-

Code:
class Record
{ 
     public: 

		Record() ; // Constructor 
           
           int size() ;                     // Return the number of fields
           Field & field( int index ) ; // Accessor to a particular field

           Field & operator[] ( int index ) ; // An alternative way of providing an accessor to the fields!
 
    private:

		Field field[5] ;   // I need an array of 5 elements.

} ;

b e n
 

iSee

macrumors 68040
Oct 25, 2004
3,540
272
In regard to getting this to compile, the line
Code:
	for (int i = 0 ; i < Record::field.length ; ++i) {
won't work because of the "Record::field.length" part.

You probably want to get the number of fields via the record instance (rec) not the class. That would make it "rec.field.length." That won't work either though because simple C arrays don't have a length property. You'd want to add a member function to Record to get the field count, like this:
int Record::get_fieldCount() { return 5; }

Then the line becomes
Code:
	for (int i = 0 ; i < rec.get_fieldCount() ; ++i) {

Also, in your Record constructor you are declaring a local variable called field and initializing that. I'm sure you meant to initialize the member variable called field. The local variable takes precedence over the member variable, effectively "hiding" it. Anyway, just remove the line "Field field[5] ; // ..." from the Record constructor.

That's all I see...
 

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
iSee thanks! I can see what I first posted doesn't make sense.

Should have been wrt my 2nd post:-

Code:
for (int i = 0 ; i < rec.size() ; ++i)

Todd, sorry for the confusion!


b e n
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
Thanks guys. I'm going through this information now, and rereading my book chapters on classes as well. My book doesn't have an example of arrayed instances of one class in another. I'm sure it's basic stuff - but I'm still wrapping my head around the syntax and order of operations.

Todd
 

iSee

macrumors 68040
Oct 25, 2004
3,540
272
FYI, At lunch I compiled the code with my and lazydog's fixes and it looks like it works. The only other thing a ran into was that there was no implementation of Field::setv(). I added the obvious code and everything seemed to work.
 

gnasher729

Suspended
Nov 25, 2005
17,980
5,566
In the Record constructor, you have an automatic array named "field" with five elements, which by sheer coincidence has the same name as a member of class Record.

Whatever you store into that array will disappear as soon as you exit the constructor. The member named "field" will remain uninitialised.
 

slooksterPSV

macrumors 68040
Apr 17, 2004
3,544
306
Nowheresville
If you want to dynamically allocate a certain amount of arrays for a structure or that, this is how I did it with my one program. Hope it helps.

PHP:
...//
typedef float sk1Vertex[3];
typedef float sk1Color[3];

typedef struct {
 int vertices; // defines how many lines of vertices
 int colors; //how many lines will have color codes
 int colorSkip; // defines how many lines before the colors begin e.g. after 3 lines of vertices, start 1 line of color
 sk1Vertex *mdlVert;
 sk1Color *mdlColor;
}model;

class STKGL
{
public:
 void DrawScene(void (*p)());
 model *TriangleLoad(model *tem, char *Filename);
 void DrawTriangle(model*);
 STKGL() {;};
 ~STKGL() {;};
 sk1Vertex *t;
 sk1Color *p;
};

model *STKGL::TriangleLoad(model *tem, char *Filename)
{
 STKGL *Ctemp;
 ZeroMemory(tem, sizeof(model));
 int skipNum = 0;
 int colNum;
 FILE *fptr, *fOut;
 fptr = fopen(Filename, "rb");
 fscanf(fptr, "%d", &tem->vertices);
 fscanf(fptr, "%d", &tem->colors);
 fscanf(fptr, "%d", &tem->colorSkip);
 //sk1Vertex *mdlVert = new sk1Vertex[tem->vertices];
 tem->mdlVert = new sk1Vertex[tem->vertices];
 tem->mdlColor = new sk1Color[tem->colors];
...//
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
OK. I made the changes, and I understand why they had to be made. It all makes sense, but my confidence level has yet to reach 100%. That's OK - practice with this stuff will bring more comprehension and more coding speed.

Now, a question. Or, maybe it's just a point. Or, a p.s. (post script)

When I implemented the Field::setv function, I copied my member function prototype, which just happened to use the same variable names as were defined as private variables for class Field. (offset, len, dt, maxlength, scale ... although I did use "dt" instead of "datatype")

The following Field class definition compiled fine:
Code:
class Field { 
	int offset ;      // Offset in the record to this field. 
	char datatype ;   // Datatype of this field.  char, num, dec, flt, dble 
	int len ;         // Number of bytes occupied by this field in the input record 
	int maxlength ;   // length of this field in the output record 
	int scale ;       // If packed decimal, this is the number of digits to the right of the decimal point. 
public: 
	void setv(int offset, int len, char dt, int maxlength, int scale) ; // set the values for the field. 
	int  get_offset() ; 
	char get_datatype() ; 
	int  get_len() ; 
	int  get_maxlength() ;
	int  get_scale() ; 
} ;
The following was my first crack at the Field::setv member function definition:
Code:
void Field::setv(int offset, int len, char dt, int maxlength, int scale) { 
	offset    = offset ; 
	len       = len ; 
	datatype  = dt ; 
	maxlength = maxlength ; 
	scale     = scale ; 
}

The above compiled fine with no warnings. However, garbage was produced. (With Ruby, this is valid syntax) Output:
Code:
Here in Record Constructor...
Record 00: offset=-1073743368, length=-1610547904, type=c, Max Length=-1610547804, Scale=-1021043573
Record 01: offset=-1021047669, length=65535, type=c, Max Length=00, Scale=00
Record 02: offset=00, length=00, type=c, Max Length=7830, Scale=-1073743200
Record 03: offset=-1073743208, length=7869, type=c, Max Length=71952, Scale=7774
Record 04: offset=-1073743304, length=-1073743324, type=c, Max Length=4096, Scale=-1073743304

classtest has exited with status 0.

Notice above that type= was correct - it's the only field that did not duplicate the variable name in the parm list.

After changing the parm list to use generic var names, and changing the assignments, the proper data was produced.
Code:
void Field::setv(int a, int b, char c, int d, int e) { 
	offset    = a ; 
	len       = b ; 
	datatype  = c ; 
	maxlength = d ; 
	scale     = e ; 
}

Output:
Code:
Here in Record Constructor...
Record 00: offset=00, length=01, type=c, Max Length=01, Scale=00
Record 01: offset=01, length=02, type=c, Max Length=02, Scale=00
Record 02: offset=03, length=02, type=c, Max Length=02, Scale=00
Record 03: offset=05, length=02, type=c, Max Length=02, Scale=00
Record 04: offset=07, length=04, type=c, Max Length=04, Scale=00

classtest has exited with status 0.

So, grasshopper (AKA self), we have learned to not use the same variable names in the function definition. In the function prototype, it is OK to use the same names.

Todd
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
If you want to dynamically allocate a certain amount of arrays for a structure or that, this is how I did it with my one program. Hope it helps.

slooksterPSV - without comments & intent, unforunately, it would take me a day to break this down into something meaningful... Sorry. Thanks anyway.

Todd
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
7
So, grasshopper (AKA self), we have learned to not use the same variable names in the function definition. In the function prototype, it is OK to use the same names.

If I remember correctly from one of my C++ classes, whatever variable is most local is used first, if you have a global and local variable of the same name. So you were essentially setting the function parameter to itself. When you viewed the original variables, they were garbage since you didn't set an initial value in your constructor.

Someone feel free to correct me if I'm wrong ;)

Thus, the this pointer comes in handy :)
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
OK, more learning.

I had tried this.offset (et al) for all the fields and got compile errors.

However, when I tried this->offset (et al) it worked just fine.

-> is something I haven't read up on yet. Thanks for making me revisit this.

Todd
 

gnasher729

Suspended
Nov 25, 2005
17,980
5,566
OK, more learning.

I had tried this.offset (et al) for all the fields and got compile errors.

However, when I tried this->offset (et al) it worked just fine.

-> is something I haven't read up on yet. Thanks for making me revisit this.

Todd

Just one thing: One problem with C++ that beginners don't notice for a while is that the same identifier can mean so many different things. Like what happened to you: The name of a parameter and the name of a class member were the same, which just creates confusion.

One set of coding standards that I used and which works very well to avoid confusion:

1. Every member function is called by using this->function () or object->function (). Every static class function is called as classname::function (). Every function that is not a member or static class function is called as ::function (). No plain function calls whatsoever. Disadvantage: You have to type a few more letters. I feel your pain. Advantage: Everyone seeing a function call knows exactly what goes on.

2. There is a convention for names of class member variables: We used names like fMember (lower case f, followed by an uppercase letter). You can use whatever convention you want, but stick to it. Your problem would have been avoided, because you would have assigned fLength = length. Obviously nothing that is not a member of a class can have a similar name.

3. Similar, all global names start with gNameOfGlobalVariable, all static variable names start with sNameOfStaticVariable.

Beginners care how easy it is to write code. After a while you notice that what is important is how easy it is to read code. And a bit later you notice that it is important to figure out what code is supposed to do, what it actually does, and when these two are different. And a bit later again you realise you want code where every single line indicates that the author clearly understood what is going on.
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
First, thanks for your feedback. All the feedback I have received here has been very appreciated. This does not deviate.

Just one thing: One problem with C++ that beginners don't notice for a while is that the same identifier can mean so many different things. Like what happened to you: The name of a parameter and the name of a class member were the same, which just creates confusion.

Yes, it can create confusion. However, my intent for the naming convention was primarily that of self-documentation in the function prototype and function definition. When I defined int offset, that is so much clearer to a maintenance programmer than something like int a. However, with the this-> syntax, it is not confusing, or ambiguous at all. I've used other languages, like Ruby, that allows this, and yes, being new to a language, it can definately create confusion. For whatever reason (laziness? No, more of a "if it works, I'll have confirmed a known experience, and if it doesn't, I'll look it up"), I tried the syntax that I was familiar with, and learned from the experience. I see it as forward progress.

One set of coding standards that I used and which works very well to avoid confusion:

1. Every member function is called by using this->function () or object->function (). Every static class function is called as classname::function (). Every function that is not a member or static class function is called as ::function (). No plain function calls whatsoever. Disadvantage: You have to type a few more letters. I feel your pain. Advantage: Everyone seeing a function call knows exactly what goes on.

I haven't coded enough yet in C/C++ to develop a sense of need for this standard. However, I'll be the first to agree, 100%, that clear code is worth it's weight in gold. I'll be one of the first to not create ambiguity through actions like creating the same function name for different classes, and I try not to use global and local variables of the same name, etc.

2. There is a convention for names of class member variables: We used names like fMember (lower case f, followed by an uppercase letter). You can use whatever convention you want, but stick to it. Your problem would have been avoided, because you would have assigned fLength = length. Obviously nothing that is not a member of a class can have a similar name.

3. Similar, all global names start with gNameOfGlobalVariable, all static variable names start with sNameOfStaticVariable.

These are great standards. Perhaps the authors who write the books should at least mention them.

Beginners care how easy it is to write code. After a while you notice that what is important is how easy it is to read code. And a bit later you notice that it is important to figure out what code is supposed to do, what it actually does, and when these two are different. And a bit later again you realise you want code where every single line indicates that the author clearly understood what is going on.

(take deep breath... hold... release... repeat... sigh....)
I agree with you 100%. I remember when I was a beginner.

Actually, even a bit later, you don't want every line of code commented, because you are so familiar with the language and it's constructs and the platform architecture, all you really want is the author's intent of the code block itself. For when you understand that, you are able to confirm or deny the acceptableness of the code towards it intended function, and piecing the whole program together in your head is a much simpler and faster undertaking.

Todd

[size=-4](I've been coding longer than most of this forum's members have been alive, just not in this language!)[/size]
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
7
[size=-4](I've been coding longer than most of this forum's members have been alive, just not in this language!)[/size]

I find it strange when people learn other languages first, instead of C/C++. Learning C/C++ makes everything else seem easy, because you learn how pointers and memory management works. But maybe that's just me :)
 

iSee

macrumors 68040
Oct 25, 2004
3,540
272
Just to compare and contrast my habits (as a long-time C++ programmer) with gnasher's comments:
2. There is a convention for names of class member variables: We used names like fMember (lower case f, followed by an uppercase letter). You can use whatever convention you want, but stick to it. Your problem would have been avoided, because you would have assigned fLength = length. Obviously nothing that is not a member of a class can have a similar name.

3. Similar, all global names start with gNameOfGlobalVariable, all static variable names start with sNameOfStaticVariable.
I do exactly this (well actually, I use m as the prefix for private/protected member variables). I recommend that all C++ programmers for this convention.

1. Every member function is called by using this->function () ... Every function that is not a member or static class function is called as ::function ()....
I can't really argue against making code easier to read, but I don't generally use these two conventions unless I see a specific need. For example, when writing code that uses MFC and direct Win32 API calls, I use ::function for the Win32 calls because there are a lot of member functions in MFC with the same names as top level function in Win32.

Also regarding -> vs. .:
You use -> to access members from an object pointer.
You use . to access members from an object reference or a direct instance of an object. That is:
Code:
class Foo {...};

Foo obj; // an object of type Foo.
Foo *objPtr = new Foo(); // a pointer to an object of type Foo.
Foo &objRef = obj;  // a reference to obj

obj.someMemberFunction();
objPtr->someMemberFunction();
objRef.someMemberFunction();

delete objPtr; // free the memory allocated with new
For whatever reason, in C++ this is a pointer rather than a reference (as it is in some other languages). So you need to use -> to specify members. Of course there is no difference conceptually between a pointer and a reference--it's mostly just the syntax used with each that is different.
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
I find it strange when people learn other languages first, instead of C/C++. Learning C/C++ makes everything else seem easy, because you learn how pointers and memory management works. But maybe that's just me :)

LOL!! When I learned programming, C didn't exist! Assembler baby! Talk about having to learn what pointers are and do your own memory management!! LOL!

Todd
 

kainjow

Moderator emeritus
Jun 15, 2000
7,958
7
LOL!! When I learned programming, C didn't exist! Assembler baby! Talk about having to learn what pointers are and do your own memory management!! LOL!

hehe I didn't think about assembly language. I guess all the guys I know who know that know C/C++ inside and out - seems like an obvious next choice of language. Just curious - what did you learn after asm?
 

toddburch

macrumors 6502a
Original poster
Dec 4, 2006
748
0
Katy, Texas
hehe I didn't think about assembly language. I guess all the guys I know who know that know C/C++ inside and out - seems like an obvious next choice of language. Just curious - what did you learn after asm?

In order:
  • s/370 Assembler
  • REXX
  • SQL (not a programming langauge, per se, but a creature in its own right)
  • s/390 Assembler (relative branching, new hardware instructions)
  • PC Assembler (but very rusty now)
  • dabble in C
  • Javascript
  • z/Architecture Assembler (more of the same - new hardware instructions, 64-Bit)
  • Ruby
  • learning Java
  • learning C/C++

I cut my teeth on OOP with Ruby. It's a great language to learn for that. Probably my favorite language today. Although, assembler puts the bread on the table. :)

Todd
 

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
For what it's worth, I prefer to append a _ to all member variables, eg

Code:
class tuna
{

int fish_

} ;

I find this the easiest to read.

b e n
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.