I posted a while ago about this (here (http://justlinux.com/forum/showthread.php?t=142817)), but I think that I'm confused on a few issues mentioned there.
I just started writing a medium sized C++ program with SDL and would like to spread the source code among multiple files. From what bwkaz explained in the previous thread, I took it that the best method of organizing a C++ program is to divide it into a header file for classes and function declarations, a library for functions and methods, and a source file for the main function.
The problems I've been having are:
1. Methods require their class in order to be defined, but the main source file also requires these classes in order to create objects. Is it possible that these classes are meant to be placed inside of the header of the library? Same with function declarations of functions in the library, should they be in the header of the library, or in the header of the main C++ source file? Does it make any sense to declare prototypes in a header file of a source file that doesn't contain their functions (which are stored in a separate library) like I thought I understood before? I'm really confused here.
2. When is it appropriate to use the "extern" command? I used it several times at the top of the library I use to hold all of the programs functions so that they can use globals from the main source file.
3. I've heard that it is common practice to put classes in their own header files, and their methods in matching source files. How does this work with inheritance?
I suppose I'm sort of directing this at bwkaz, who helped me with my previous questions, but if anyone could help I'd be eternally grateful.
Thanks
bwkaz
03-17-2006, 08:52 PM
I took it that the best method of organizing a C++ program is to divide it into a header file for classes and function declarations, a library for functions and methods, and a source file for the main function. Essentially correct, although there's no requirement that main be in its own file.
The problems I've been having are:
1. Methods require their class in order to be defined, but the main source file also requires these classes in order to create objects. There's a difference between the class's definition and its implementation. All code that refers to a class (either code in main, to create an instance of the class, or code in the library, to generate class methods) must have the class's definition available. You put it in one header file, included from multiple places, so that you only have to change the definition in one place if the definition changes. (Well, you might also have to change the calling code, and you might also have to change the functions. But see below for why you can't put the functions into the header.)
Is it possible that these classes are meant to be placed inside of the header of the library? You're supposed to include the same file from the library as you use from the file containing main.
Same with function declarations of functions in the library, should they be in the header of the library, or in the header of the main C++ source file? If they're member functions, they need to be in the class's header file. If they're not members, but they're closely related, I'd put them in the class header as well.
If they're not related to the class much, then I'd put them either in a separate header file included only from the main.cpp file, or I'd put the declarations right inside main.cpp.
You do not want to put function code inside header files (except inline functions). The reason is because if you include the header from two different C/C++ files, and then link those two files together, you'll get duplicate definition errors from the linker. You have to put the actual code (and the actual data member creation lines) in only one compilation unit (aka only one C/C++ file). You can refer to that definition from other compilation units by using either prototypes or "extern" declarations.
Does it make any sense to declare prototypes in a header file of a source file Umm... header files are not tied to source files.
It almost sounds like that's where your confusion is coming from. Because the whole paragraph that started with this doesn't really make much sense to me either. (Maybe that's why you're confused. ;))
2. When is it appropriate to use the "extern" command? When you're telling the compiler "this compilation unit needs access to this global variable, but it's not actually stored here. It's stored in some other file; the linker will find it later." (Note that "extern" can be used even in the compilation unit that actually allocates space for the global variable, I think.)
(There's another overloaded use for extern when you're talking about C++. You can wrap a bunch of function prototypes in extern "C" { ... }, which tells the compiler to use the C calling convention for them. It doesn't mangle the names when you do this. You lose the ability to overload these functions (declare another function with the same name but different parameter types), but it's the only way to call C library functions.)
3. I've heard that it is common practice to put classes in their own header files, and their methods in matching source files. How does this work with inheritance? Inheritance doesn't affect it at all; the inheriting class acts just like the main.cpp file.
For instance, you could have in A.h:
class A {
int function();
}; and then in A.cpp:
#include "A.h"
int A::function()
{
// do whatever
return 0;
} To use this class from main, do:
#include "A.h"
A xxxx;
int main()
{
return xxxx.function();
} And compile both main.cpp and A.cpp together.
Then, to create a class B that inherits from A, do this in B.h:
#include "A.h"
class B : public A {
int function2();
}; and then in B.cpp:
// This will cause "duplicate definition of class A" errors if you don't
// wrap A.h in multiple-inclusion protection. I'm ignoring that for this
// simple case.
// The include of A.h isn't strictly needed in this case, either.
#include "A.h"
#include "B.h"
int B::function2()
{
// do whatever
return 0;
} Then, change main.cpp to be:
#include "B.h"
// You can also include A.h if you want
B yyyy;
int main()
{
yyyy.function();
return yyyy.function2();
} Compile all of main.cpp, A.cpp, and B.cpp together. Make sense?
(Alternately, you could compile A.cpp and B.cpp into a shared library, and link main.cpp against it. But it's simpler if you ignore that to start with.)
Gogeta_44
03-17-2006, 11:39 PM
Okay, I think I understand what your saying, I was kinda under the assumption that every source file had a matching header file (main.cpp, main.h; functions.cpp; functions.h), and I was confused about the placement of prototypes into header files. This link here (http://www.gamedev.net/reference/programming/features/orgfiles/page2.asp) says that:
You generally want one header file for every source file. That is, a SPRITES.CPP probably needs a SPRITES.H file, a SOUND.CPP needs a SOUND.H, and so on. Keep the naming consistent so that you can instantly tell which header goes with which normal file.
But what you said makes more sense.
My next question is on the placement of program wide global constants. Is it better to put these in a header file that both the library and main source files share, or just at the beginning of the main source file and put them as extern at the beginning of the library? Using SDL, I like to declare NULL surface pointers globally and then later define them in functions and assign a surface to them. Should I put those with the constants in where ever you say is best that I put the constants?
EDIT:
Oh ya, quick question:
I have had to define a bunch of default arguments to functions in the prototype in the header, and now when I attempt to compile the library it gives me a bunch of lines like:
[code]gamefunctions.cpp:13: error: default argument given for parameter 4 of `SDL_Surface* load_image(std::string, Uint8, Uint8, Uint8)'
header.h:37: error: after previous specification in `SDL_Surface* load_image(std::string, Uint8, Uint8, Uint8)'[code]
Which complains about me giving the default argument twice. Is it okay to only have the default values in the prototype?
bwkaz
03-18-2006, 09:54 AM
This link here (http://www.gamedev.net/reference/programming/features/orgfiles/page2.asp) says that:You generally want one header file for every source file. Well, that is one possible way of doing it -- but it only really makes sense for libraries (i.e. classes being used by main, in your case). And even in that case, there's no tie from the source file to the header; the only relationship is that to use the functions in the source file, you need to include whatever header file declares them. The tie is from the header to the source, but it's fairly weak. (The filename doesn't matter at all, for instance.)
My next question is on the placement of program wide global constants. If you must use globals, you have to allocate storage for them in only one compilation unit (where that is doesn't matter, as long as there's only one). The rest of the compilation units that refer to that global must have an "extern" declaration.
(Unless you mean "constant" as in "define", not "const". When you set up a #define, you don't need an extern, because there's no storage at runtime for a #define. It's a preprocessor-only thing. But a "const" variable does have storage at runtime, so it needs one definition and other declarations must be extern.)
Is it better to put these in a header file that both the library and main source files share, or just at the beginning of the main source file and put them as extern at the beginning of the library? Depends on who "owns" them. If the library owns them, then put an extern declaration into the header file, and put the actual definition into the library's .cpp file. If the main function "owns" them, then you would still put the extern into the header file, but you'd put the declaration into main.
Deciding who "owns" the constant isn't very cut-and-dried though. It depends on what it's used for.
Using SDL, I like to declare NULL surface pointers globally and then later define them in functions and assign a surface to them. Um, I have no idea what you're saying here... ;) Can you post some sample code so I know exactly what you're doing?
I have had to define a bunch of default arguments to functions in the prototype in the header, and now when I attempt to compile the library it gives me a bunch of lines like:
gamefunctions.cpp:13: error: default argument given for parameter 4 of `SDL_Surface* load_image(std::string, Uint8, Uint8, Uint8)'
header.h:37: error: after previous specification in `SDL_Surface* load_image(std::string, Uint8, Uint8, Uint8)'
Which complains about me giving the default argument twice. Is it okay to only have the default values in the prototype? I think so. If your code in the header looks like this:
SDL_Surface *load_image(std::string file, Uint8 x=0, Uint8 y=0, Uint8 z=0); then the code in the library .cpp file must look like this:
SDL_Surface *load_image(std::string file, Uint8 x, Uint8 y, Uint8 z)
{
// ....
} i.e. without the default values. (I know I ran into this when I first started using C++ default arguments. It's been a while since I used C++ at all, though... it's possible that that was just the compiler I was using, though I doubt it.)
Gogeta_44
03-18-2006, 09:41 PM
If the main function "owns" them, then you would still put the extern into the header file, but you'd put the declaration into main.
That helps a lot, that saves a lot of externals that I had declared in source files all over.
(Unless you mean "constant" as in "define", not "const". When you set up a #define, you don't need an extern, because there's no storage at runtime for a #define. It's a preprocessor-only thing. But a "const" variable does have storage at runtime, so it needs one definition and other declarations must be extern.)
That's funny, because I have a bunch of const int's in a header that's included in every file. Is that just bad form, or is their another reason I'm not getting any errors? I do have multiple-inclusion protection on the header, but now I have 6 object files that go into the executable and 1 object file that goes into a library that all include that header.
Um, I have no idea what you're saying here... Can you post some sample code so I know exactly what you're doing?
You already answered my question. I was basically asking if I could declare variables in a header that is included in object files that go into the same executable without using extern, which I now know you can't.
I understand the default arguments question I had now. It just seemed that it would be including to much functional code in the header to place them in the prototype.
Thanks a lot for helping me again by the way. :)
bwkaz
03-20-2006, 07:42 PM
That's funny, because I have a bunch of const int's in a header that's included in every file. Is that just bad form, or is their another reason I'm not getting any errors? I don't know; I thought it was disallowed. Let me do some experimenting here...
Well, it's not allowed in C at least. temp.h:
#ifndef TEMP_H
#define TEMP_H
const int x = 1;
extern int fn();
#endiftemp.c:
#include "temp.h"
int main()
{
int y = x;
return y + fn();
} temp2.c:
#include "temp.h"
int fn()
{
return x;
} gcc -o temp temp.c temp2.c
/tmp/cczh1Fe9.o(.rodata+0x0): multiple definition of `x'
/tmp/ccsEHOwH.o(.rodata+0x0): first defined here
collect2: ld returned 1 exit status So gcc (more accurately, ld) doesn't like it. I'm not sure what you're doing though, to get it to work... (Are you declaring the constant to be "extern"? Or are you using #define? What about namespaces -- if the constant is declared inside a class or a namespace, then it won't collide with itself in another class or namespace.)
I do have multiple-inclusion protection on the header, but now I have 6 object files that go into the executable and 1 object file that goes into a library that all include that header. Yeah, the multiple-inclusion protection won't help; it'll only protect against the header being included more than once in a single compilation unit. If you have several compilation units that you link together later, the header will exist in all of them.
And in C:
test2.o(.rodata+0x0): multiple definition of `testGlobal'
test.o(.rodata+0x0): first defined here
collect2: ld returned 1 exit status
But with C++ I got no error, and 5 was sent to stdout.
I guess C++ does accept multiple const definitions in different object files. :)
bwkaz
03-22-2006, 08:38 PM
Hmm; must be one of the small differences between C and C++ then. I know there are others (there are a few valid C programs that won't compile as C++, for instance), but I didn't think this would be one of them. Oh well though. :)
Stuka
03-28-2006, 02:18 AM
How'd you compile the 2 versions? And can you post both - this 'smells' wrong, since globals operate identically in C and C++. Oh - and unless you MUST use them, avoid globals like the plague!!!!
justlinux.com
Copyright Internet.com Inc. All Rights Reserved.