gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Is it possible to put functions in other files?

#68907 - gs2phateon - Fri Jan 27, 2006 8:20 am

I've gotten variables and defines to work in seperate files, but the compiler doesn't recognize functions.

#68920 - Cearn - Fri Jan 27, 2006 10:36 am

Declarations.

Each file (that isn't #included) is a separate entity, oblivious to the rest of the world. In order to make functions and variables visible, you'll need to add declarations to the file that you want to use them in. For variables, use the 'extern' keyword, for functions, well, just the function name & args followed by a semi-colon.

Code:
// file A.c
// Defines var and func

// variable definition
int var;

// function definition
void func(int x)
{
   // blah
}


Code:
// file B.c
// Uses var and func

// variable declaration
extern int var;

// function declaration
void func(int x);


Usually those declarations are put in header files (that's what headers are for) and included in both A.c and B.c, but you can do it manually if you want.

Note: there can only be one definition of a variable/function in a project, but as many declarations as you want.

#68921 - keldon - Fri Jan 27, 2006 10:40 am

easy makefile tutorial
tonc gba tutorials

Check out that makefile and tonc tutorials. You need declarations in headers.

foo.h
Code:

extern const char text*;
void myFunc ();


foo.c
Code:

const char *text = "text";

void myFunc(){
}


Extern means that the variable will be later declared.

EDIT: Okay I guess Cearn had replied to this at the same time

#68980 - gs2phateon - Fri Jan 27, 2006 7:36 pm

OK, I think I know what's going on. It keeps saying that there is a multiple definition of the function I'm using. It's one that doesn't need any variables. I'm trying to define the function in a different file than the main file, and use it in the main file.

#68984 - gs2phateon - Fri Jan 27, 2006 7:54 pm

Now I think I almost have it, but my function involves a structure. I defined it in the header, but I can't use it in the other file. Normally it says that it wasn't defined. But when I put "extern" in front, it says that there's a syntax error.

#68991 - Dwedit - Fri Jan 27, 2006 8:33 pm

Typedefs, structs and classes are declared the same way. Just as long as it isn't the struct keyword creating an object.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."

#68994 - gs2phateon - Fri Jan 27, 2006 8:50 pm

Ok, I can prototype the function wherever I want, but for some reason I can't use the function anywhere but the header and the main C file.

This is what I'm trying to do (I'll just forget the technical terms in case I get define/declare mixed up):

Prototype the function in the header if I have to
Set up the function in my "Sprites" file
Actually use the function in the main file

Everytime I set up the function in the Sprites file, it says the function has a multiple definition.

#68997 - Cearn - Fri Jan 27, 2006 9:02 pm

gs2phateon wrote:
Now I think I almost have it, but my function involves a structure. I defined it in the header, but I can't use it in the other file. Normally it says that it wasn't defined. But when I put "extern" in front, it says that there's a syntax error.

What are you doing now exactly? Some code and the error message would be nice.

#68998 - gs2phateon - Fri Jan 27, 2006 9:09 pm

Ok, this is a function I'm trying to move from the main file to my Sprites file:
Code:
//Function: HideSprites
//Moves all sprites off the screen
void HideSprites()
{
    int n;
    for (n = 0; n < 128; n++)
    {
        sprites[n].attribute0 = 160;
        sprites[n].attribute1 = 240;
    }
}


and the error message I get is:
Sprites.o(.text+0x0): In function `HideSprites':
: multiple definition of `HideSprites'
main.o(.text+0x0): first defined here
make: *** [CKGame.elf] Error 1

#69032 - n0va - Sat Jan 28, 2006 1:57 am

Functions and variables must be defined in the source file, then have an extern declaration in the header. If your function is inline, you have to put the whole thing in the header as inline functions must be declared on every source file using it.
If you have any structs, throw it in the header.

Code:
// File1.h
extern int var1;
extern int func1();
struct struct1 {
    int int1;
    int int2;
}
inline int func2() {
    // Code goes here
}


Code:
// File1.c
#include "File1.h"
#include "File2.h"
int var1 = 253;
int func1() {
    func3();
    func2();
}


Code:
// File2.h
extern int var2;
extern int func3();


Code:
// File2.c
#include "File1.h"
#include "File2.h"

int var2 = 6342;
int func3() {
    func1();
    func2();
}


If you try to get the var2's value from file1.c, you can get it because you included the external declaration which was found in file2.h

#69066 - gs2phateon - Sat Jan 28, 2006 6:37 am

Thanks, that last post helped a lot! I got the function to work. But I still have one more problem. In the header with my #defines, all of the other variables are multiply defined. I tried moving them to a separate header only included by the main file, but that didn't solve the problem (although it worked for the other #includes in the same header). Putting "extern" didn't solve the problem, again, even when I moved the variables to the header or the source file.

#69108 - Cearn - Sat Jan 28, 2006 3:05 pm

Definitions can happen only once, declarations are allowed anywhere and as many times as you want.

Look at it this way. The things that make up the final program are symbols: things that have memory allocated to them in the final binary. Symbols include constant data, variables and functions, as those are actually the things that do anything. Each of these has a unique name. You can't have multiple symbols with the same name, because then the linker doesn't know which version you're referring to.

The problem with multiple definitions is that the compiler and the linker aren't the same thing. The compiler looks at individual files. If there aren't any symbols with the same name in a file, it'll pass that stage. However, the linker looks at all the files in a project, and will balk if there are symbols with the same name on a global level. The error messages show you where the multiple definitions are, so remove all of them except one.

Back to what you should put in header files, and what in source files. If it forms a symbol, it should not go in header files. A 'compiled' header file should be empty (or: a compiled source file should be the same size with or without the #include). Functions, variables and data would all use up memory, so they should not appear in header files. Class/struct definitions, preprocessor directives and inline functions don't use up memory, so they go in header files. (Or not. You don't have to use header files, but adding all that stuff to each file manually just isn't very convenient.)

Back to the declaration vs definition thing again. Each time you use an identifier in C, it has to have been declared or defined previously in the file. This is a Good Thing(TM), because it safeguards against typos and other nasties. When you make a declaration for a symbol, all you're telling the compiler that "something with that name exists in the project, but not necessarily in this file." Whether the thing actually does exist (i.e., has a definition) in the project is the linker's problem.

Every definition is also a declaration, but not the other way around. As a base rule, anything symbol-name that has 'extern' on it is a declaration, anything else is a definition. In practice this isn't quite true because functions don't need 'extern', but generally it's true. Things using '=' or '{}' are also usually definitions.

gs2phateon wrote:
Thanks, that last post helped a lot! I got the function to work. But I still have one more problem. In the header with my #defines, all of the other variables are multiply defined. I tried moving them to a separate header only included by the main file, but that didn't solve the problem (although it worked for the other #includes in the same header). Putting "extern" didn't solve the problem, again, even when I moved the variables to the header or the source file.

Don't just slap 'extern' in front of everything. Go through the headers and for every identifier ask yourself if this would use memory in the binary (i.e., if it's a symbol: function/variable or data). If yes, remove it from the header and place it in a source file. Then recompile everything and watch the slew of compiler errors wash over you. Read the error messages, determine which symbols should be shared, then put the declarations back into the headers. Look at n0va's and my earlier post to see what declarations look like. Yes it is a dirty job, but it's the only way to be sure. Depending on how comfortable you are with all this you can short-circuit the procedure, but you might miss something. It might also be a good idea to practice with a smaller project to get a feel for this stuff.

When you get error messages (or even warnings), you'd be wise to read them. They will give you clues to where you should start looking. Also, it's possible that while you've not recompiled everything so that the old object files still have the old definitions included from the old headers.

Lastly, on this thing:
n0va wrote:
Code:
inline int func2() {
    // Code goes here
}

This is an inline function. It looks like a function (same syntax), but works like a macro (the code is directly inserted into the calling function). Because the body isn't actually compiled, it belongs in header files too. Well, that's the theory. In practice, they will be compiled to functions if you're compiling without optimization, which may cause multiple definitions again. To avoid this, use 'static inline' instead of just 'inline'.

#69141 - gs2phateon - Sat Jan 28, 2006 7:24 pm

WOW! I finally got it! For the last few variables all it took was for me to move them into their corresponding functions (including main() ). Thanks for all your help, I think I'm starting to understand how this works.

#69239 - lorbar - Sun Jan 29, 2006 3:25 pm

@cearn: about those inline functions... where to place their definition best? right now i have some inline functions declared/prototyped in their corresponding header files and defined in source file, which works and seems to be the right thing (at least for regular functions). but i wonder if the toolchain can actually inline functions which are defined in another source file than the one currently compiling?

maybe you can clear that up for me: when is inling done? when i'm compiling or when i'm linking? if it is done at compile time i guess i should put the definitions in the header files and use static inline instead?

#69244 - Cearn - Sun Jan 29, 2006 3:58 pm

lorbar wrote:
@cearn: about those inline functions... where to place their definition best? right now i have some inline functions declared/prototyped in their corresponding header files and defined in source file, which works and seems to be the right thing (at least for regular functions). but i wonder if the toolchain can actually inline functions which are defined in another source file than the one currently compiling?

maybe you can clear that up for me: when is inling done? when i'm compiling or when i'm linking? if it is done at compile time I guess I should put the definitions in the header files and use static inline instead?

Oh dear, good questions. Inline functions can be a little tricky. I'm not 100% sure about this myself, but here goes.

The inlining itself (i.e., integrating them in the functions that call them) is done at compile time. After that it's too late. Linking is only putting all the pieces and references together, but doesn't actually create code itself.

When you call an inline function that's defined in another source file, it will use it like it would any other function: a branch to its address. It's not inlined because it doesn't know what the thing actually does. Source files are stand-alone entities after all.

But for this to work, the original inline function would need a body. The way I understand it, if you just use 'inline', it'll inline the functions if the definition is visible, and create a body for it for outside funtions. Of which, again, there can be only one.

Now for 'static inline'. This won't create a body. "Ah, but what if I want to call that from an outside source?" Well, you can't. After all, that's what 'static' means! Static functions can only be seen in the file in which they're actually defined, that goes for inline functions as well. You can even have static function definitions in multiple files, the one exception to the Highlander rule. Of course, multiple bodies take up more space so that's usually not advised. It's not a problem for inlines because they won't have bodies. And because you might want to use it in multiple files, you might as well put it in a header file.

*checks the GCC manual *
Cool, I remembered correctly.

Finally, something a leetle off-topic, but well worth pointing out to those of us joining from C++ land: In C++, 'const' data are 'static' by default! That means that they're invisible to outside files even if you have declarations in those outside files. To get them to work properly, also have 'extern' declarations in the file that has the definitions.

#69282 - poslundc - Sun Jan 29, 2006 7:59 pm

Just to add to Cearn's explanation:

For a function to be inlined, it must have its function body visible at compile time. This means it needs to go into the header file so it can be #included directly, but this can make header files big and cumbesome and hard to read/navigate, and kind of violates their general purpose. For this reason, oftentimes it's practical to create .inl files for your inlined functions that can be #included at the end of your .h file.

I've heard chatter that the next version of GCC will let you declare inlined functions in separate source files and have them be correctly inlined even if the code calling the inlined function is compiled separately, but I do not know if they will be doing this for sure.

Dan.

#69288 - lorbar - Sun Jan 29, 2006 8:26 pm

ok, thanks for the input!

let me try to put it in my words and please let me know what you think. right now i'm too lazy to try it out, but even it would seem to work i wouldnt be able to tell if gcc did inline everything the way i wanted it to be. (i can't read ASM)

(replace foo with whatever name you like: ) so i guess i should be using extern + inline + definition in my foo.h (or foo.inl-files) for gcc to be able inline them everywhere i want it to be inlined. for calls that cannot be inlined i would still need an additional function definition definition in foo.c right? right now i'm actually allways including foo.h from foo.c... wouldnt the toolchain complain about two definitions?

#70379 - RickA - Mon Feb 06, 2006 12:53 am

Just as a quick caution, I'm pretty sure the 'inline' keyword doesn't actually force the compiler to inline the function, it's more of a suggestion.