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++ > gcc "undefined reference to"

#22075 - FPChris - Sat Jun 12, 2004 11:18 pm

Hey All,

It's been several years since I've touched C. I've mainly done C++
for the past 7-8 years. I'm now coding with devkitadv r5 with
VisualC and my C is a little rusty...

The problem ...

I have a main.c file that includes a xfont.h file.

Code:

//----------------------------------------------------------------
//xfont.h
//----------------------------------------------------------------
#ifndef XFONT_H
#define XFONT_H


#include "xgbatypes.h"

struct xGBAFont{

   u8 pali[4]; //color palettes indexes
   u8 *pBmp[256];  //pointers to characters

};


//----------------------------------------------------------------
//xfont function prototypes
//----------------------------------------------------------------
extern void xPutCh(s16 x, s16 y, u16* buf, u8* pCh,struct xGBAFont* pF);

#endif
//----------------------------------------------------------------
//end of file



When compiled main can see the xGBAFont struct but not
the xPutCh() function.

gcc error is:

main.o: In function `main':
main.o(.text+0x188): undefined reference to `xPutCh(short, short, unsigned short*, unsigned char*, xGBAFont*)'
collect2: ld returned 1 exit status
NMAKE : fatal error U1077: 'gcc' : return code '0x1'

This is driving me batty. I even moved the function proto into
main.c and it STILL generates this same error.

WTF?

Any advice would be greatly appreciated.
Chris


[/code]

#22077 - FPChris - Sat Jun 12, 2004 11:34 pm

Ok I shuffled things around and gcc showed an error
in an unrelated file that clear it up.

Looks like I'm in for some hair pulling if this is the norm for gcc errors.

Chris

#22078 - dagamer34 - Sun Jun 13, 2004 12:23 am

Did you add your .o file where xfont is defined to your makefile? It's not being linked into your binary.
_________________
Little kids and Playstation 2's don't mix. :(

#22080 - Abscissa - Sun Jun 13, 2004 1:11 am

Also, you might have messed up the parameters to the function: Missing params, extra params, using an int when it expects a float, etc.

#22089 - FPChris - Sun Jun 13, 2004 3:36 am

What happen was I'm so used to C++ that I declared a new font as...

xGBAFont xSysFont;

instead of...

struct xGBAFont xSysFont;

This was in my "xsysfont.h" and simply adding 'struct'
resolved the error.

I've done this a couple times already and gcc never seems
to generate a error than is in reference to the omission.
Leaves me looking all over and screaming at the monitor :)

Thanks anyways...
Chris

#22095 - Abscissa - Sun Jun 13, 2004 5:09 am

There's a little trick with typedef in C that's commonly used so that you don't have to worry about declaring using "struct":

Code:
typedef struct t_xGBAFont{

   u8 pali[4]; //color palettes indexes
   u8 *pBmp[256];  //pointers to characters

} xGBAFont;


With that, you can just say "xGBAFont xSysFont" even on very strict C compilers.

#22098 - keldon - Sun Jun 13, 2004 9:38 am

Abscissa wrote:
There's a little trick with typedef in C that's commonly used so that you don't have to worry about declaring using "struct":

Code:
typedef struct t_xGBAFont{

   u8 pali[4]; //color palettes indexes
   u8 *pBmp[256];  //pointers to characters

} xGBAFont;


With that, you can just say "xGBAFont xSysFont" even on very strict C compilers.


It's not a trick, it's a language feature :D lolol

#22100 - FPChris - Sun Jun 13, 2004 12:21 pm

Excellent. That's what I was forgetting.

Its working as I'd like it too now.

Thanks guys.

#22101 - sgeos - Sun Jun 13, 2004 1:49 pm

Abscissa wrote:
There's a little trick with typedef in C that's commonly used so that you don't have to worry about declaring using "struct"

If you always declare structs with the struct keyword people reading your code will know that the data type is a struct and not something else. That can be useful:
Code:
typedef void (*big_magic_action)(int, int);
typedef struct widget {
   const char *name;
   int cost;
   big_magic_action please_do_big_magic;
} widget;
typedef int life_bar;

Naming conventions are helpful. I think that leaving struct in front of structs is a fine naming convention.

One of these would be a struct:
Code:
   const widget funny_thing_that_is_completely_useless_and_can_not_be_purchased_or_sold = MAGIC_BUTTON_PRESS_MACRO_TOKEN_MAKER_DEALIE_MA_JIG(last_funny_thing_you_get_for_pressing_, BUTTON_TRACKED_USING_HAPPY_SKILLS, _at_least_, BIG_UNREASONABLE_NUMBER, _times_total);
   life_bar candys_bar_of_life = DEFAULT_GOLD_BAR;
   big_magic_action super_hyper_super_neo_ultra_magic_radio_missle_death_wa
ve = divine_beam_spray_deluxe_super_special_ten_billion_hit_combo_mk_viii_red_version;

-Brendan

#22103 - poslundc - Sun Jun 13, 2004 2:09 pm

sgeos wrote:
Naming conventions are helpful. I think that leaving struct in front of structs is a fine naming convention.


In priciple I agree, but in practice I find the struct keyword is usually superfluous. Especially since I rarely find it practical to typedef either function pointers or primitive data types (beyond the u32, s16, etc. contractions).

Dan.

#22113 - Abscissa - Sun Jun 13, 2004 4:03 pm

poslundc wrote:
sgeos wrote:
Naming conventions are helpful. I think that leaving struct in front of structs is a fine naming convention.


In priciple I agree, but in practice I find the struct keyword is usually superfluous. Especially since I rarely find it practical to typedef either function pointers or primitive data types (beyond the u32, s16, etc. contractions).

Dan.


My experience with this has been the same as Dan's. However, I think that it *would* be very useful to typedef primitives (and to use enums more) *if* C had stronger type checking. (I suppose this is getting a little off topic, but it's been bugging me for a while). I'm actually very opposed to the way C automatically casts between a typedef or enum and it's underlying type - it makes those features more or less useless, since they're functionally identical to #define. I would love it if the compiler were, for example, to generate errors on this:

Code:

typedef enum t_shape {SQUARE, RECT, TRIANGLE} shape;
typedef int shieldAmount;

void Foo(shape myShape, shieldAmount myShield)
{
  // Do something
}

void Bar(void)
{
     int numEnemiesTotal = 7;
     u16 color = 0xFFFF;

         // This should not be allowed,
         // but C doesn't even issue a warning
     Bar(color, numEnemiesTotal);
}


Since it doesn't, this can introduce a lot of bugs, AND defeats the purpose of typedef and enum. Their purpose is to create a new type, but the only thing they really end up doing is create a name alias for existing types, so it's not particularly useful to use them. :(

#22158 - FPChris - Mon Jun 14, 2004 12:21 am

I'd say they are very useful in cases where you have:

typedef int shieldAmount;

Now say you have used shieldAmount in over 100 places in your code
and have found out that it would be better if shieldAmount was a char.

typedef char shieldAmount;

Changing one line of code is far easier than changing 100 instances.
And although were on GBA here, if the code was moved to
another piece of hardware where 'int' sized data cause a conflict
it could be easily fixed.

#22161 - Abscissa - Mon Jun 14, 2004 1:24 am

True, but that still isn't any different than doing:

#define shieldAmount int

#22162 - sgeos - Mon Jun 14, 2004 1:27 am

poslundc wrote:
I rarely find it practical to typedef either function pointers or primitive data types (beyond the u32, s16, etc. contractions).

Contrary to what some may believe, u32, s16, etc are not typedef'd to save typing. They are typedef'd because the underlying type might change. An s16 is a signed integer that is at least 16 bits. By using s16 you can port your code to another target or compiler and it will still work if the typedefs are set up correctly.

Abscissa wrote:
However, I think that it *would* be very useful to typedef primitives (and to use enums more) *if* C had stronger type checking.

Run time type checking is expensive. I don't see any reason why a compiler couldn't issue warnings.

Abscissa wrote:
this ... defeats the purpose of typedef and enum. Their purpose is to create a new type, but the only thing they really end up doing is create a name alias for existing types, so it's not particularly useful to use them. :(

One uses a typedef when the underlying type might change. An enum is used when one wants to represent numerical values using names. From what I understand, gcc's version of enum is not much different from a string of #define statements.

enum is compatable with int in C, so I can do something like this:
Code:
typedef enum {HP_DEAD = 0xffff} hp_t;

Then:
Code:
void take_damage_for_non_evil_vampire_type_creatures
(
   hp_t *hp_ptr,
   u16 damage
)
{
   if (damage < *hp_ptr)
      *hp_ptr -= damage;
   else
      *hp_ptr = HP_DEAD;
}

Sure, all of this could be done with #define statements. I can even:
Code:
int x = HP_DEAD;

If I really want to.
-Brendan

#22166 - poslundc - Mon Jun 14, 2004 1:49 am

sgeos wrote:
poslundc wrote:
I rarely find it practical to typedef either function pointers or primitive data types (beyond the u32, s16, etc. contractions).

Contrary to what some may believe, u32, s16, etc are not typedef'd to save typing. They are typedef'd because the underlying type might change. An s16 is a signed integer that is at least 16 bits. By using s16 you can port your code to another target or compiler and it will still work if the typedefs are set up correctly.


There was another thread I started some months back that discussed the philosophies behind naming conventions... I actually prefer the extra typing involved to define, for example, an "unsigned short int" but force myself to use u16 instead. Not so much for this reason - I do not expect or plan to be porting my GBA code any time soon to such a dramatically different architecture - but because if I explicitly state the variable size in its type then I know it's because I care about the size, whereas on the other hand I will always simply use "int" for generic counters, etc. where size is unimportant.

Quote:
Abscissa wrote:
However, I think that it *would* be very useful to typedef primitives (and to use enums more) *if* C had stronger type checking.

Run time type checking is expensive. I don't see any reason why a compiler couldn't issue warnings.


The kind of type-checking Abscissa is describing could easily be done at compile time.

Quote:
One uses a typedef when the underlying type might change.


... Or as simply a way to shorthand a longer type.

Quote:
An enum is used when one wants to represent numerical values using names. From what I understand, gcc's version of enum is not much different from a string of #define statements.


Other than #defines being handled by the pre-processor and enum generating symbols in the compiler. Two ways of skinning the same cat, I suppose.

Abscissa makes a point that C doesn't offer a simple way to have constants declared as a property of an exclusive type. That's more of a C++ thing. Whether or not you miss it depends on you, I suppose. I've never lost any sleep - or, more importantly, debugging time - over it, though.

Dan.