Compiling a C based mud under g++
Posted by Crix
on Tue 01 Aug 2006 12:44 PM
— 26 posts, 89,680 views.
Greetings everyone,
At the suggestion of Odis, I'm posting in hoping for suggestions. I currently run a copy of SWR that I am trying to compile under g++ in the hopes of eventually being able to use many of the features of C++. I've been able to clean out all the errors in my compiles as well as declaring extern "C" around my mud.h file, but when it gets to linking it all together, I get pages and pages of undefined reference errors about functions that are prototyped in mud.h, defined in hashstr.c and used in a macro in mud.h. An example of one of the functions was str_alloc.
After begging and pleading for help from Odis(hehe, thanks a bunch for what you did), he tried renaming the function just in case there was an overloading issue, but that didn't appear to work. We also moved the prototype declarations above the macro's in case that was it, but there was no change.
I'm at a loss as to what it is because the code appears to compile all fine and such but snags itself while linking.
Thanks in advance,
-C
Hmm. Are you also using g++ for linking? It sounds like your problem is during the linking phase and changing the order of functions/prototypes won't help you.
It's also possible that you removed an object file from the link phase; did you tweak the makefile, or move stuff from one file to a new one? And did you try make clean, make all?
Lemme apologize first, I think I put this in the wrong section.
The only section that I changed in the makefile was changing gcc to g++
Other than that, I only repaired bugs and never actually touched the errored functions.
i never extern "C"'d my mud.h file when i converted.
Oh, I didn't notice that you'd used extern "C". You don't want to do that.
You only use extern "C" { ... } when the externed functions were compiled in a C (not C++) object file, and therefore use the C calling convention, instead of C++.
So if your functions are compiled using g++, you will in fact break things if you tell the compiler to expect C versions, because you have compiled C++ versions.
The problem was much the same though without that.
Well, nonetheless, you shouldn't be using extern "C". Did you do make-clean/make-all in between, after modifying mud.h and removing extern "C"?
Yup, I was always taught if you modify mud.h that you need to make clean/make. When I get back to work on it tonight, I'll remove though I'm almost certain most, if not all of the problems will still be there.
I tackled this project a while back with some success, the versions would be old now:
http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=3668
As for your link errors, I'm not sure what is happening exactly. Did you rename all your .c files as .cpp ones?
I think I would skip the "extern C" stuff, and try to make the whole lot compile and link as straight C++. I know it can be done, I think that is what I did in my earlier project.
A small sampling of some of the linker errors you're getting might help too. Those of us who have been through the g++ conversion will probably be able to tell right away what's going on.
A question of my own though Nick. Is there any benefit in renaming the files as .cpp instead of leaving them as .c ?
Quote:
Is there any benefit in renaming the files as .cpp instead of leaving them as .c ?
Yes -- it can help compilers auto-detect the right language. g++ can drop into C mode if the files have the .c extension, and that can cause some weirdness. Usually it figures it out, but it's safer, and besides, it's just good programming practice in general.
Quote:
Is there any benefit in renaming the files as .cpp ...
I agree with Ksilyan that the compiler is more likely to treat your source as C++ files, rather than C being compiled in C++ mode which gives a rather different result. Of course there will be a compiler switch to do that. Judging by the (incredibly long) man page for g++ it is:
-x c++
However a quick test seems to show that compiling a .c file with g++ does in fact treat it as C++. However as Ksilyan says, if they are C++ files, why not have them suffixed .cpp?
This doesn't totally explain your linking problems, however I would, as I said before, stop using "extern C" and make the source compile without it, as far as possible anyway.
It will probably be needed for external libraries (zlib springs to mind), but should not be needed for things like including mud.h.
As an experiment, I took the SmaugFUSS source (1.6 I think) and changed gcc in the Makefile to g++, did a "make clean" and then a make.
This compiled and linked with no errors whatsoever.
I wasn't sure what that proved, so I added a bit of definite C++ to inside comm.c :
vector<int> v (100);
generate (v.begin (), v.end (), rand);
That compiled OK too, so it seems that it should be working for you with no errors.
Thanks to Samson for changing "class" to "Class" which avoided heaps of problems I had earlier.
zlib actually has the extern "C" inside zlib.h, so you don't need to do that either.
Excellent, thank you everyone for the work, when I get home tonight, I'll try it it out and copy some of the errors that I have.
Alright, so I took out the extern "C" bit and changed all the file names to end with .cpp
I still get the same errors, now with a couple extra thrown in for good measure.
Here's what I've got so far:
act_comm.o(.text+0x1f2): In function `generate_com_freq(char_data*)':
: undefined reference to `string_allocation(char*)'
act_comm.o(.text+0x2899): In function `do_retune(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_comm.o(.text+0x29c3): In function `do_retune(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_info.o(.text+0x5a3d): In function `do_hedit(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_info.o(.text+0x5a53): In function `do_hedit(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_info.o(.text+0x5fe9): more undefined references to `string_allocation(char*)' follow
finger.o(.text+0xe4c): In function `read_finger(char_data*, char*)':
: undefined reference to `check_parse_name(char*, bool)'
magic.o(.text+0x4a29): In function `spell_create_water(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0x92e4): In function `spell_animate_dead(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0x938d): In function `spell_animate_dead(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0x9436): In function `spell_animate_dead(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0xb798): In function `spell_obj_inv(int, int, char_data*, void*)':
...
space2.o(.text+0x112): In function `reload_ship(ship_data*)':
: undefined reference to `string_allocation(char*)'
space2.o(.text+0x12e): In function `reload_ship(ship_data*)':
: undefined reference to `string_allocation(char*)'
space2.o(.text+0x2674): In function `do_giveship(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
space2.o(.text+0x280c): In function `do_cleanships(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
space2.o(.text+0x2822): In function `do_cleanships(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
space2.o(.text+0x2838): more undefined references to `string_allocation(char*)' follow
collect2: ld returned 1 exit status
make[1]: *** [swreality] Error 1
make[1]: Leaving directory `/home/mud/lots/lotscode/dist/src'
make: *** [all] Error 2
As a reference to one of the errors, string_allocation is prototyped in hashstr.cpp and found in hashstr.cpp
Thanks
I would confirm that there is no "extern C" around the declaration for string_allocation (that is, in the .h file).
Also I would check that hashstr.cpp is part of the project.
hashstr.cpp is in the project and I placed it at the front of the list of items being compiled. Also, string_allocation does not have the extern "C" bit around it.
I've gotten all the errors narrowed to this:
act_comm.o(.text+0x1f2): In function `generate_com_freq(char_data*)':
: undefined reference to `string_allocation(char*)'
act_comm.o(.text+0x2899): In function `do_retune(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_comm.o(.text+0x29c3): In function `do_retune(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_info.o(.text+0x5a3d): In function `do_hedit(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_info.o(.text+0x5a53): In function `do_hedit(char_data*, char*)':
: undefined reference to `string_allocation(char*)'
act_info.o(.text+0x5fe9): more undefined references to `string_allocation(char*)' follow
finger.o(.text+0xe4c): In function `read_finger(char_data*, char*)':
: undefined reference to `check_parse_name(char*, bool)'
magic.o(.text+0x4a29): In function `spell_create_water(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0x92e4): In function `spell_animate_dead(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0x938d): In function `spell_animate_dead(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0x9436): In function `spell_animate_dead(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
magic.o(.text+0xb798): In function `spell_obj_inv(int, int, char_data*, void*)':
: undefined reference to `string_allocation(char*)'
makeobjs.o(.text+0x199): more undefined references to `string_allocation(char*)' follow
collect2: ld returned 1 exit status
make[1]: *** [swreality] Error 1
make[1]: Leaving directory `/home/mud/lots/lotscode/dist/src'
make: *** [all] Error 2
The other ones that I had I moved declarations around for variables and that fixed it but I've moved the prototyping around and I've kept this same problem.
Can you show an example of how string_allocation is called? For example, in generate_com_freq, it says there is an undefined reference. Perhaps it is calling it with different arguments, for example:
string_allocation ("nick")
This would be const char *, not char *.
Also show the prototype for string_allocation as well as the implementation.
string_allocation is actually part of a macro called STRALLOC, I'll paste that in as well.
Here is one of the samples though of it:
void generate_com_freq(CHAR_DATA *ch)
{
char buf[MAX_STRING_LENGTH];
sprintf(buf, "%d%d%d.%d%d%d",
number_range(0,9), number_range(0,9), number_range(0,9),
number_range(0,9), number_range(0,9), number_range(0,9));
if(ch->comfreq)
STRFREE(ch->comfreq);
ch->comfreq = STRALLOC(buf);
save_char_obj(ch);
return;
}
Here is how it is defined in mud.h:
char * string_allocation ( char *str );
and the macro:
#define STRALLOC(point) string_allocation((point))
And here is the actual implementation in hashstr.cpp:
char *string_allocation( const char *str )
{
register int len, hash, psize;
register struct hashstr_data *ptr;
len = strlen(str);
psize = sizeof(struct hashstr_data);
hash = len % STR_HASH_SIZE;
for (ptr = string_hash[hash]; ptr; ptr = ptr->next )
if ( len == ptr->length && !strcmp(str,(char *)ptr+psize) )
{
if ( ptr->links < 65535 )
++ptr->links;
return (char *) ptr+psize;
}
ptr = (struct hashstr_data *) malloc(len+psize+1);
ptr->links = 1;
ptr->length = len;
if (len)
strcpy( (char *) ptr+psize, str );
else
strcpy( (char *) ptr+psize, "" );
ptr->next = string_hash[hash];
string_hash[hash] = ptr;
return (char *) ptr+psize;
}
Quote:
char * string_allocation ( char *str );
char *string_allocation( const char *str )
There's your problem.
The prototype promises you will implement a function accepting
( char *str ), however you have a function taking ( const char *str ).
That is a different function. Either put const in front of the prototype, or take if off the implementation.
why would that work though in some cases but not others?
And the C stuff must not be so strict then.
Some cases? Like what?
Quote:
And the C stuff must not be so strict then.
It isn't. They are different languages really. C++ allows you to have multiple implementations of the same function name. It isn't really a matter of being strict, it is a language feature.
Consider this program:
int test (const char * arg)
{
}
int test (char * arg)
{
}
int main (void)
{
const char * a;
char * b;
test (a); // uses first function
test (b); // uses second function
return 0;
} // end of main
The function "test" is defined twice, with different arguments. The C++ compiler will correctly call the appropriate function (as in the example in main). This compiles without errors under C++.
Under C, you get this:
gcc test.c
test.c:6: conflicting types for `test'
test.c:2: previous declaration of `test'
test.c: In function `main':
test.c:17: warning: passing arg 1 of `test' discards qualifiers from pointer target type
Because of this, if you prototype something, and call it, as in your example, but implement a
different function (that is, with different argument types), then it will compile but the linker will complain you never implemented that version.
Quote:
Thanks to Samson for changing "class" to "Class" which avoided heaps of problems I had earlier.
Heh. I used the script you provided for the purpose, it worked quite well. The reason SmaugFUSS compiled cleanly using g++ is because I had gone through it once before and cleaned out all of the errors it generated, then changed the Makefile back to C. Didn't figure on it being necessary to remain as C++ when it's not. Just wanted to clean up the heap of residual errors/warnings it picks up.
g++ (and C++) is more strict about some things than C++. It might not be a bad idea to compile using g++, even if you're compiling C code.
And regarding const etc., it's actually a fairly common technique to have functions for a const version and functions for a non-const version; generally it's better to prefer the const version, if you can get away with it, because it's an extra aid to the programmer: you are guaranteeing that nothing will be changed (which is a good thing for the programmer to know) and the compiler is enforcing this restriction.