Multi-Dimensional Char Arrays -- HELP!!

Posted by TheTraveller on Tue 15 Jan 2008 10:10 AM — 56 posts, 181,615 views.

#0
I'm working on an experimental project to modify SocketMUD 2.2 to use a MySQL database backend instead of external files. I've already gotten the most complex stuff done and conducted several successful database retrieval tests from within the MUD.

However, I've run into a brick wall on something that is probably insultingly simple, and yet after 6 straight hours of Googling, trial-and-error(ing), and banging my head against a wall, I am no closer to a solution than when I started! I'm widely considered an expert in PHP scripting and have some but fairly little experience with C/C++.

Here's what I'm trying to do: I'm working on a simple function that is passed a query string, queries the database, and returns the results in a multidimensional char array. In PHP, here is a conceptual equivalent of the type of variable I'm talking about: $fictional_results[$row_int][$column_int] = $col_value;

In PHP, I could have that working in a matter of seconds. But in C, just about anything I do returns a segmentation fault on runtime (not exactly the most helpful error message I might add). The codebase is written in C flat (i.e. not C++), and the string.h library does not seem compatible (if I try to #include <string.h> it's fine, but if I try to declare any variable as type string it returns a non-sensical parse error on compile).


Here are the relevant declarations in mud.h:

#include "/usr/include/mysql/mysql.h"

...

typedef struct mysql_xdata
{
char xrowdata[1024][1024][1024]; /* This is the variable I'm having trouble with. I've tried "char *xrowdata[1024][1024]", "char xrowdata[1024][1024]", etc, as well as just about every suggestion on Google.... Nothing works! All I want is string_variable[mysql_row_int][mysql_column_int]. */
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024]; /* Used for variable DEBUG. --XXXX */
} XMYSQL;

...

/*
* mysql.c
*/
XMYSQL *mysql_xquery( char xqueryx[1024] );


Now here's the function (in its current form, with a few things like passwords censored out) that performs the query in mysql.c:

XMYSQL *mysql_xquery( char xqueryx[1024] )
{
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;

char *server = "localhost";
char *user = "root";
char *password = "XXXX";
char *database = "XXXX";

int column = 0;
int sqlrow = 0;
int totalcols = 0;

XMYSQL *xmysql;

xmysql = malloc( sizeof( XMYSQL ) );

sprintf( xmysql->xmysqldebugbuf, "DEBUG SUCCESSFUL!" );

conn = mysql_init(NULL);

/* Connect to database */
if ( !mysql_real_connect( conn, server, user, password, database, 0, NULL, 0 ) )
{
fprintf( stderr, "%s\n", mysql_error( conn ) );
exit( 0 );
}

/* send SQL query */
if ( mysql_query( conn, xqueryx ) )
{
fprintf( stderr, "%s\n", mysql_error( conn ) );
exit( 0 );
}

res = mysql_use_result( conn );

/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{
column = 0;
sqlrow++;
while ( column < res->field_count )
{
column++;
totalcols++;
sprintf( xmysql->xrowdata[sqlrow][column], "%s", row[column - 1] ); /* !! ATTENTION !! -- This is the line that is causing the segmentation fault during runtime. If I comment it out, everything works fine, and all the other variables retain their values perfectly, and I have also tested extensively to ensure it's not an error with the MySQL interaction itself. */
}
}

/* Release memory used to store results and close connection */
mysql_free_result( res );
mysql_close( conn );

xmysql->xnumrows = sqlrow;
xmysql->xnumcols = column;

return xmysql;
}


Currently, I'm calling the function from socket.c just as a test debug, essentially to display the output of a test query whenever I connect and it displays the greeting stuff. The code for that isn't relevant to this I don't think, but I can include it anyway if you think it'll help.


I feel really frustrated being held in place by such a basic problem. I do know how C char arrays work (i.e. only stores one char for each key), but I can't seem to wrap my brain around how to go from that to creating a multi-dimensional string array that does what I'm trying to do.


Whew! Any ideas? I really appreciate any help you can provide. =)


--TT
Amended on Tue 15 Jan 2008 10:16 AM by TheTraveller
Australia #1
Wouldnt the simplest approach be to only deal in rows. Read a whole row, write a whole row. Then you dont need to worry about your column position.

char xrowdata[whatever_string_length]

sprintf(xrowdata, "%s %s %s", col1, col2, col3 );



sprintf( xmysql->xrowdata[sqlrow][column], "%s", row[column - 1] );

Now im not expert but that does not look right, your putting the string row[column - 1] into xmysql->xrowdata[sqlrow][column]

now i will prolly say this incorect but are you not putting an array of chars into an array of char arrays. something is curely to go bust there.
Amended on Tue 15 Jan 2008 11:03 AM by Robert Powell
Australia #2

#include <mysql/mysql.h>

MYSQL               mysqlconn;
MYSQL_RES           *result;
MYSQL_ROW           row;

extern int          sql_working;

/* declarations */
#define MY_SERVER   "localhost"
#define MY_USER     "YOUR USER"
#define MY_PWD      "YOUR PASSWORD"
#define MY_DB       "YOUR DATABASE"
#define MY_SOCK     NULL
#define MY_PORT     3306
#define MY_FLAG     0

#define bool        int
#define TRUE        1
#define FALSE       0

void load_mysql     (void);
int MySQLQuery      (char * query);
bool open_db        (void);
void close_db       (void);
---------

Now you need the handler functions..
Make a mysql.c file and put..

(Any suggestions? Did I do this right? It seems to work..)

---------
/*
 * load_mysql()
 *
 * Opens the database, provides errors, etc
 */
void load_mysql(void)
{
    if (!open_db() )
    {
        logf("**** MYSQL ****: Could not open Database.\n");
        logf("**** MYSQL ****: Reason:\n%s\n", mysql_error(&mysqlconn));
        exit (1);
        return;
    }
    logf("**** MYSQL ****: Connection open and working.\n");
    sql_working = 1;
    return;
}

/*
 * MySQLQuery()
 *
 * Inserts a raw query to the SQL server
 * returns true if it works, false if it fails
 */
int MySQLQuery(char * query)
{
    if (mysql_real_query(&mysqlconn, query, strlen(query)))
    {
        logf("**** MYSQL ****: Error:\n%s\n", mysql_error(&mysqlconn));
        return 0;
    }
    mysql_free_result(result);
    return 1;
}
    
/*
 * open_db()
 *
 * Opens a connection to the SQL database
 */
bool open_db(void)
{
    if (!mysql_init(&mysqlconn))
    {
        logf("**** MYSQL ****: Unable to open database!\n");
        return FALSE;
    }
    if ((mysql_real_connect(&mysqlconn, MY_SERVER, MY_USER, MY_PWD, MY_DB, MY_PORT, MY_SOCK, MY_FLAG)) == NULL)
    {
        logf("**** MYSQL ****: open_db() failed during opening!\n");
        mysql_close(&mysqlconn);
        return FALSE;
    }
    return TRUE;
}

/*
 * close_db()
 *
 * Closes connection to the SQL server
 */
void close_db(void)
{
    logf("**** MYSQL ****: close_db(): closed\n");
    mysql_close(&mysqlconn);
    return;
}



thats all you need to do to open close and query a DB, code is not mine, credit goes to Thri

Terms of Use:
email me, saying your using it. Or not, it doesn't matter, this is too minor
to matter really, but my ego could use a boost ;)

If, you find any bugs, problems, "you shouldn't"s PLEASE, tell me, the handler functions
I post here are used in numerous things I do... So its important ;)
cyhawkx@gmail.com
-Thri
Amended on Tue 15 Jan 2008 11:15 AM by Robert Powell
#3
Quote:
typedef struct mysql_xdata
{
char xrowdata[1024][1024][1024]; /* This is the variable I'm having trouble with. I've tried "char *xrowdata[1024][1024]", "char xrowdata[1024][1024]", etc, as well as just about every suggestion on Google.... Nothing works! All I want is string_variable[mysql_row_int][mysql_column_int]. */
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024]; /* Used for variable DEBUG. --XXXX */
} XMYSQL;


char xrowdata[1024][1024][1024];

That's a gig. I don't know if malloc has an upper limit on how much memory it can allocate at once, but if it does, that probably exceeds it.

char *xrowdata[1024][1024];

Here you are declaring a 1024x1024 grid of character pointers, which I think is what you want (apart from the wastefulness of allocating the huge grid)... except they are never allocated before you try to sprintf them.

char xrowdata[1024][1024];

And now you have a 1024x1024 grid of single characters, which are also a bad target for sprintf.


You're copying the data out of the row object for some reason? What happens to it when you're finished with it?
#4
Basically, to answer your question, I'm not married to any of the array declarations for that variable I listed. Again, all I want is the best/most efficient possible way to make it so that I can store all the query results data into an array that can be indexed by row, column. The only way I know how to do this is with a multidimensional array, but if you have a better method in mind, by all means tell me. =)

As for the rest of the code, please note that this is just a working prototype, so I really don't want to spend any time worrying about making it "cleaner"/etc, or anything else that would require a major re-write. All the MySQL stuff is working perfectly, it's just that I can't figure out how to store it into a variable that fits the specifications I outlined.

Will a multidimensional array work at all, or is there a better method of return? I do need both the row data and column data accessible in a single variable or structure or whatever in order for the later development stages to work.


--Kris
USA #5

sprintf( xmysql->xrowdata[sqlrow][column], "%s", row[column - 1] ); /* ... */


At first glance, I would say the reason this isn't working quite right is because you declared it with three dimensions. I've never had any real world problem require a three dimensional array (I'm not saying there isn't one mind you...), and in this case you don't seem to need three dimensions.

Change:

char xrowdata[1024][1024][1024];


to


char xrowdata[1024][1024];


in your data struct and make clean.

And it -may- stop seg faulting on you. While this is not the most efficient means of doing this (and can be done much easier in C++), if all you are looking for is a working version I see no reason why that won't work.

That said, its also been a while since I've played with the MySQL C API, so test it out and see. You might consider looking at a working implementation (there are a great many out there) and see how they do it.
Amended on Tue 15 Jan 2008 01:53 PM by Nick Cash
USA #6
First of all, as Whiteknight said, this would be much easier in C++ since you could use container classes like maps of maps of strings to let you index on the rows and columns; it would be easy, fast and efficient. Arguably, given your background, it would also be more appropriate for you to work in C++ because the STL provides some of the higher-level abstractions you are used to. All this is certainly possible in C, you just will have to work harder at it and understand the fundamental issues.

Some specific issues:

Quote:
But in C, just about anything I do returns a segmentation fault on runtime (not exactly the most helpful error message I might add).

This is telling you that you did something incorrect to memory somewhere, like deleting something twice or accessing invalid memory. The error message isn't more helpful because C, unlike PHP, is run directly in machine code, and therefore there is no environment to monitor execution and fix (or at least detect) problems as they occur.

There are many tools to help find these, such as gdb and valgrind. Nick has a very good gdb guide on this site.

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=3653

Quote:
The codebase is written in C flat (i.e. not C++), and the string.h library does not seem compatible (if I try to #include <string.h> it's fine, but if I try to declare any variable as type string it returns a non-sensical parse error on compile).

There is no type 'string' in C, with or without string.h. If you open string.h you'll find a collection of utility functions.

A string in C is just a sequence of bytes in memory terminated by a zero. So, char s[100], an array of 100 characters, is declaring a "string" of length 100 (well, 99 if you don't count the zero). char * s, a pointer to a character, can also be thought of as a string, at least, assuming there's a zero ending it an all that.

But basically, C has no built-in notion of string. C++ doesn't, technically, but does provide the 'string' class that does pretty much what you would expect it to (although it has its own idiosyncrasies, too).
#7
The thing you have to remember is that C was originally written by assembly programmers. The language is statically typed, so the compiler is the last point that knows what type a variable is. At runtime it is all just memory.

malloc() returns a pointer to an undifferentiated block of memory of the size you ask for. The typecasts are to tell the compiler to warn you if you do something unexpected with the memory (and you can get around that by casting to (void *) -- which is useful when you are writing a dynamically typed system).

Because of this, a C 'string' isn't an object like a Java object of a PHP variable, it is a sequence of char in memory, so when you are declaring a 'string' you either declare an array of char or a pointer to some unspecified number of char (ie (char *)). The array will form part of the function's execution frame and is hence allocated on the stack. The pointer needs to be given a space on the heap allocated by malloc, and can't be used (segfault) until you have done that. At runtime, functions can't distinguish between an array and a pointer because they are both handed around as pointers to the first element (a legacy of the way variables were (are still?) passed as a single word, so a function knows that to get all of its arguments, it just has to pull a certain number of words off the calling stack).

All of the functions declared in string.h expect a pointer to a char (which might be array of char on the stack).

As far as the data structure goes, if you can get the number of results returned before allocating your xmysql:
typedef struct mysql_xdata {
    char ***xrowdata;
    int xnumrows;
    int xnumcols;
    char xmysqldebugbuf[1024];
} XMYSQL;


Then, to allocate it, after you have the row count:
xmysql = (XMYSQL *) malloc(sizeof(XMYSQL));
xmysql->xrowdata = (char ***) malloc(sizeof(char **) * row_count);


Then, when you are copying fields:
/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{
    column = 0;
    xmysql->xrowdata[sqlrow] = (char **) malloc(sizeof(char *) * res->field_count);
    while ( column < res->field_count )
    {
        xmysql->xrowdata[sqlrow][column] = (char *) malloc(sizeof(char) * (1 + strlen(row[column])));
        strcpy(xmysql->xrowdata[sqlrow][column], row[column]);
        column++;
        totalcols++;
    }
    sqlrow++;
}



If you can't get the row count before walking the results, then you need to use a linked list (...I hope you know how to manage one), I tend to prefer doubly-linked lists because being able to walk it in reverse (without recursing into it) can be useful (and the 4+4*length byte overhead is usually negligible):
struct mysql_xrow;
typedef struct mysql_xrow {
    struct mysql_xrow *next;
    struct mysql_xrow *prev;
    char **xdata;
}

typedef struct mysql_xdata {
    struct mysql_xrow *xrowdata_head;
    struct mysql_xrow *xrowdata_tail;
    int xnumrows;
    int xnumcols;
    char xmysqldebugbuf[1024];
} XMYSQL;


Then, to allocate it:
xmysql = (XMYSQL *) malloc(sizeof(XMYSQL));
xmysql->xrowdata_head = NULL;
xmysql->xrowdata_tail = NULL;


Then, when you are copying fields:
/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{
    struct mysql_xrow *xrow;
    column = 0;
    xrow = (struct mysql_xrow *) malloc(sizeof(struct mysql_xrow));
    xrow->xdata = (char **) malloc(sizeof(char *) * res->field_count);
    while ( column < res->field_count )
    {
        xrow->xdata[column] = (char *) malloc(sizeof(char) * (1 + strlen(row[column])));
        strcpy(xrow->xdata[column], row[column]);
        column++;
        totalcols++;
    }
    sqlrow++;
    if (NULL == xmysql->xrowdata_tail) {
        xmysql->xrowdata_head = xmysql->xrowdata_tail = xrow
        xrow->next = xrow->prev = NULL;
        continue;
    }
    xrow->prev = xmysql->xrowdata_tail;
    xrow->prev->next = xrow;
    xrow->next = NULL;
    xmysql->xrowdata_tail = xrow;
}
Amended on Tue 15 Jan 2008 10:43 PM by Isthiriel
#8
Yeah I prefer C++, but as I said, SocketMUD is written in C, and I wasn't able to find a fully working version that was converted to C++. So I have no choice but to work it in C, unless of course any of you feels like helping me convert the entire codebase to C++ lol.

Ok now that that's outta the way, lemme read your latest instructions then I'll respond. =)
USA #9
You should be able to compile it in C++ mode, at least, with fairly minimal changes. You don't have to convert everything to classes and OOP structures. But, you will get all the advantages of being able to use standard container classes like vectors, maps, strings etc.
#10
Regarding the C++ thing, I still have no idea how to go about doing that (i.e. do I just rename the files to .cpp and make minor mods to the Makefile, or is there more to it?). I'd love to have the flexibility of C++, but I'm not sure how to switch it.


Regarding the code suggestions regarding the array issue....

I just tried your code for mud.h and mysql.c with a clean make, and I'm getting the following compile errors:

mud.h:188: two or more data types in declaration of `XMYSQL'
mud.h:188: warning: duplicate `typedef'


To be safe, I just copied/pasted your code; specifically, I replaced my code:

typedef struct mysql_xdata
{
char xrowdata[1024][1024][1024]; /* Row, column. --XXXX */
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024]; /* Used for variable DEBUG. --XXXX */
} XMYSQL;


With your code:

struct mysql_xrow;
typedef struct mysql_xrow
{
struct mysql_xrow *next;
struct mysql_xrow *prev;
char **xdata;
}

typedef struct mysql_xdata
{
struct mysql_xrow *xrowdata_head;
struct mysql_xrow *xrowdata_tail;
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024];
} XMYSQL;



What did I do wrong?
Amended on Wed 16 Jan 2008 01:25 AM by TheTraveller
#11
Oops.
struct mysql_xrow /* There shouldn't be a typedef on this line. */
{
struct mysql_xrow *next;
struct mysql_xrow *prev;
char **xdata;
}; /* and there should be a semicolon here :-/ */


I should also add that when you are finished with the result, you need to free the actual data, then the rows, then the containing struct, like so:
void free_xmysql(XMYSQL *xmysql)
{
    struct mysql_xrow *xrow = NULL;
    short col = 0;
    if (NULL == xmysql)
        return;
    while (NULL != xmysql->xrowdata_tail)
    {
        xrow = xmysql->xrowdata_tail;
        xmysql->xrowdata_tail = xrow->prev;
        xrow->prev->next = NULL;
        xrow->prev = NULL;
        col = xmysql->xnumcols;
        while (0 < col)
        {
            free(xrow->xdata[--col]);
            xrow->xdata[col] = NULL; /* redundant, but good practice */
        }
        free(xrow->xdata);
        xrow->xdata = NULL; /* redundant, but good practice */
    }
    /* if tail is NULL, then there are no entries in the list so head is invalid, and should be NULL. */
    xmysql->xrowdata_head = NULL;
    free(xmysql);
    return;
}

Amended on Wed 16 Jan 2008 02:23 AM by Isthiriel
#12
That fixed the typedef error, but I'm still getting the "mud.h:188: two or more data types in declaration of `XMYSQL'" error.
#13
That would be the missing semicolon at the end of the struct definition. It's something I have a bad habit of forgetting. *sigh*
#14
Alrighty, it works now, and I added the free-up function as well.

I always get confused when working with structs (just don't encounter them very often), so how would I use this new variable setup to access certain data?

Let's say, for example, we have the following query: "select * from players"

And let's say that, for whatever reason, I want to access character's display name (column 3) of the 5th result (row). Here's what I can figure out and what I can't, using snippets of the debug code from socket.c for this example:

XMYSQL * xpfile;
char qtxt[1024];

...

xpfile = malloc( sizeof( *xpfile ) );
sprintf( qtxt, "select * from players" );
xpfile = (XMYSQL *) mysql_xquery( qtxt );

text_to_buffer( sock_new, ?????????? ); /* Display name (column 3) for 5th player (row) in result. */
?????????? /* Any subsequent code needed, like to free-up memory or whatever? */


Is that right (not sure if the malloc is setup right)? What would I want to replace the "??????????" with to make it work (with as little code as possible)?


Thank you so much for your help thus far! =)
#15
I tried a few educated guesses on my own, but they all resulted in Mr. Segmentation Fault again. I'm sure this is an extremely simple question, but I'm completely stumped!
#16
Ok, least code possible:
text_to_buffer( sock_new, xpfile->xrowdata_head->next->next->next->next->xdata[2]);


Of course that will segfault if you don't actually have five rows or three fields, and is a nuisance if you're actually trying to walk the list.

So... what I'd do is make a helper function that walks the list for you and returns a sensible error condition if it runs off the end.
void *walk_list(void *head, int len)
{
    while (0 < len-- && NULL != head)
        head = head->next;
    return head;
}

Or, more specifically:
char **mysql_data_from_xmysql(XMYSQL *xmysql, int row)
{
    struct mysql_xrow *xrow = NULL;
    xrow = xmysql->xrowdata_head;
    while (0 < row-- && NULL != xrow)
        xrow = xrow->next;
    if (NULL == xrow)
        return NULL;
    return xrow->xdata;
}

Which can then be used like so:
text_to_buffer( sock_new, mysql_data_from_xmysql( xpfile, 4 )[2] );


You can, of course choose your own (less verbose) name for the function.

It may be more useful to return the actual xrow object rather than the data array, as with the xrow object you can continue walking the list (making it useful to skip some number of rows at the beginning of the list).

It is also trivially modified to use negative numbers to indicate an offset from the end of the list (ie. -1 is the tail, -2 is tail->prev ...)


If mysql_xquery is doing the query, you should probably make it analogous to the mysql_query/mysql_free_result pair, so mysql_xquery (and that's kind of an odd name, wouldn't xmysql_query make more sense?) does the allocation of the xmysql object and xmysql_free_result would then deallocate it (code provided already, as "free_xmysql").

Also, if you only want the one result from a resultset that might span several hundred rows, copying the entire resultset into your object and then promptly destroying is wasteful of cpu cycles. That's why the mysql api has so many functions like fetch_row, fetch_result, ... so you only retrieve the minimum you need. (It helps to tailor your sql too, you should never SELECT * unless you actually need all of the data in the table.)


And, might I add, the space-inside-parentheses style is particularly ugly :P
USA #17
Quote:
And, might I add, the space-inside-parentheses style is particularly ugly :P

Or particularly nice and readable, in some cases...
#18
I always put space outside parentheses unless it's a function, or a stack of parentheses together.

The only time that's an issue is if you're editing code with a variable width font that makes parentheses to be 2px wide... and that's a problem with your editor :P

Oh, and in LISP/Scheme I'll break up stacked closing parentheses into groups of four to make counting them easier. Not that I write much outside of an editor that knows how to match parentheses.
USA #19
Yes, I was talking about stacks of parentheses, but it bothers me even if I have a fixed-width font. (Well, I never fail to have a fixed-width font when programming, so that point is kind of moot.) It's also not a question of an editor that can match parentheses; it's a question of being able to visually see extremely quickly where an expression breaks into sub-expressions and where those start and stop without needing to use the editor to tell me.

(Arguably, you shouldn't often have complex expressions with sub-expressions, but it does happen sometimes...)

Although, I make a motion to drop this tangent and not hijack the thread, I perhaps should not have started it but I took some exception to your statement. :-P
#20
Lol I've found that everyone has their own unique style of programming. I always leave a space within the parentheses for functions to differentiate from parentheses used in arithmetic operations. I'm partially blind so every little bit helps.

I'll take a look at your suggestion.
Amended on Fri 18 Jan 2008 05:56 AM by TheTraveller
#21
It didn't work. I did everything you suggested, no compile errors, but now I'm getting a segmentation fault again.

Specifically, I added this function to mysql.c:

char **mysql_get_xrowdata( XMYSQL *xmysql, int row )
{
struct mysql_xrow *xrow = NULL;
xrow = xmysql->xrowdata_head;
while ( 0 < row-- && NULL != xrow )
{
xrow = xrow->next;
}
if ( NULL == xrow )
{
return NULL;
}
return xrow->xdata;
}


Then I have these 3 lines in the greeting section of socket.c:

sprintf( qtxt, "select * from players where playerid=1" );
xpfile = (XMYSQL *) mysql_xquery( qtxt );
text_to_buffer( sock_new, mysql_get_xrowdata( xpfile, 1 )[2] );



What am I doing wrong now??
#22
I did some debugging and narrowed the Segmentation Fault to this line in function mysql_xquery in mysql.c:

/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{



How can that possibly be causing a Segmentation Fault?!?! It worked before I put in the new code you suggested, and I haven't even touched it! Why would it now all of a sudden causing a crash when it's exactly the same as when it worked??
#23
Ok I fixed THAT bug on my own. Apparently your function used a variable named row of type int, which was already being used by the xquery function but as a different type. I changed the variable name and that took care of it.

However, later on in the xquery function, I'm getting another Segmentation Fault. Again, the entire function used to work perfectly before I added that other function, so I'm not sure why it would suddenly be not working, unless there's another variable conflict I'm not seeing.

Here's the chunk of code in question:

/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{
struct mysql_xrow *xrow;
column = 0;
xrow = (struct mysql_xrow *) malloc(sizeof(struct mysql_xrow));
xrow->xdata = (char **) malloc(sizeof(char *) * res->field_count);
while ( column < res->field_count )
{
xrow->xdata[column] = (char *) malloc(sizeof(char) * (1 + strlen(row[column])));
if ( totalcols == 9 )
{
exit( 0 );
}
strcpy(xrow->xdata[column], row[column]);
column++;
totalcols++;
}
sqlrow++;
if (NULL == xmysql->xrowdata_tail)
{
xmysql->xrowdata_head = xmysql->xrowdata_tail = xrow;
xrow->next = xrow->prev = NULL;
continue;
}
xrow->prev = xmysql->xrowdata_tail;
xrow->prev->next = xrow;
xrow->next = NULL;
xmysql->xrowdata_tail = xrow;
}


Pay particular attention to this part:

if ( totalcols == 9 )
{
exit( 0 );
}


That's a debug I added to determine exactly where the Segmentation Fault was occuring. It wasn't occuring anywhere in that where loop, not even at the end, but right after its ending bracket it was. So I put in the ifcheck, starting at 1, and kept re-compiling until I got a seg fault, then I subtracted one and started moving it down line-by-line until I hit a seg fault again.

By using this method, I've determined that the following line is causing the Segmentation Fault, but only when the execution reaches the 10th iteration:

xrow->xdata[column] = (char *) malloc(sizeof(char) * (1 + strlen(row[column])));


Could this be evidence of some kind of array buffering problem? But why then did it work before? I don't get it. Doesn't make any sense....
Amended on Fri 18 Jan 2008 10:40 PM by TheTraveller
#24
Quote:
Apparently your function used a variable named row of type int, which was already being used by the xquery function but as a different type. I changed the variable name and that took care of it.

If the two variables are declared in separate functions there shouldn't be an issue as they exist in non-overlapping scopes...

Quote:
That's a debug I added to determine exactly where the Segmentation Fault was occuring. It wasn't occuring anywhere in that where loop, not even at the end, but right after its ending bracket it was. So I put in the ifcheck, starting at 1, and kept re-compiling until I got a seg fault, then I subtracted one and started moving it down line-by-line until I hit a seg fault again.

That's what trace-writes are for.

Quote:
By using this method, I've determined that the following line is causing the Segmentation Fault, but only when the execution reaches the 10th iteration:

Write the contents of all the variables to somewhere visible (either the mud's built in logging functions, or our friend printf()), one variable / printf statement so the first printf statement that doesn't get printed is the variable access that failed. (In particular you want to see the values of res->field_count, column, row, xrow->xdata[column], row[column] and strlen(row[column]).)

In this case, I'd have to guess that either res->field_count is lying or row[9] is not a valid string... does the table include any NULLs in the 10th column? I think strlen(NULL) might segfault. (What does the C API return for a (SQL) NULL value anyway?)
#25
Alrighty, that was what I needed. The 10th column is NULL. As soon as you mentioned that, I knew that had to be it, and sure enough....

Of course, there's now yet ANOTHER Segmentation Fault now (does C ever return any other runtime errors????). The mysql_xquery function is now fine. Now it's coming from the function you provided.

I've narrowed it down this time to this line of your function in mysql.c:

if ( NULL == xrow )
{
return NULL;
}


The code that's calling it is here in socket.c:

sprintf( debugbuf, "%s", mysql_get_xrowdata( xpfile, 1 )[2] );


Obviously, it's crashing because it's passing a NULL value to debugbuf in sprintf, right? First of all, that row/column is not a NULL value, so why is it coming up as NULL in the first place? And secondly, would it be possible to perhaps have it return some kind of string error message (like "ERROR - Data not found!") or something instead of NULL?


--TT

P.S. Here's the entire function again for easy reference:

char **mysql_get_xrowdata( XMYSQL *xmysql, int rownum )
{
struct mysql_xrow *xrow = NULL;
xrow = xmysql->xrowdata_head;
while ( 0 < rownum-- && NULL != xrow )
{
xrow = xrow->next;
}
if ( NULL == xrow )
{
return NULL;
}
return xrow->xdata;
}

Amended on Sat 19 Jan 2008 01:33 AM by TheTraveller
#26
Right, xrow is NULL iff rownum > number of results returned.

In this case, you either have 0 or 1 result(s) and you're asking for the second result. Which means mysql_get_xrowdata returns NULL and you then attempt to dereference that with the [] operator which segfaults.

I think passing NULL to sprintf is actually handled fairly gracefully?

Alternatively you have < 3 fields. (And the segfault is then because the char ** is valid, but the [2] runs past the end of the array.)

While it's possible to return a string error message, it's kind of difficult.

To start with you're returning a char ** which should be an array of strings, not a single string. Secondly, unless you are going to check for the error message and free it appropriately after the function returns, the memory the string is residing in has to have a lifetime longer than the function returning it, which implies a static variable, and then you have to do the managing of it inside the function which will cause problems if your mysql_get_xrowdata function is called more than once in the same statement (ie multiple entries in a row getting put in the same printf) though that can be lived with since most of the other string functions in C muds have the same caveat.

Of course, if you're checking for the string error message, it's cheaper to check for NULL.


C doesn't have "runtime errors" because there is no runtime interpreter to handle them (which is why C programs are faster and smaller than interpreted languages). Segmentation fault is a signal passed to the executable by the operating system when it detects an out-of-segment memory access. There are other signals (about twenty or so iirc) and some of them are more informational than fatal, others are instructional, like SIGHUP which is used to tell daemons to reread their configuration.
#27
Oh wait, so you mean I have the rows/columns backwards??

I was assuming it was like this:

mysql_get_xrowdata( xpfile, row_number )[column_number];


In this case, row_number is 1 and column_number is 2, which means it's supposed to grab the second column (player's name) from the first row (result). Are you saying it's the other way around? This would be confusing since the function declaration lists the int argument as "row" (which I changed to "rownum" to fix the previous bug). Would this then work if I simply switched the syntax to be like this:

mysql_get_xrowdata( xpfile, column_number )[row_number];


....?
#28
*sigh*

Did you add the tracewrites like I suggested?

This is C and just like PHP (which I thought you said you had some skill at?) all arrays are indexed from 0.

Strictly the arr[n] operator is a short hand for *(arr + n * sizeof(type)), so the first element is located at (arr + 0 * sizeof(type)).

If you read the code in the mysql_get_xrowdata you should have seen that rownum == 0 returns xmysql->xrowdata_head which is the first row. The 'rownum' dictates how many times it tries to follow the ->next pointer.

So, to get the second field of the first row: mysql_get_xrowdata(xpfile, 0)[1]

What are you using as your C language reference?
Amended on Sat 19 Jan 2008 05:01 AM by Isthiriel
#29
Lol yes I'm well aware that arrays are indexed at 0 by default. But as a general practice, at least in PHP, the key (index) is setup to match its reference as closely as possible. In other words, [0] would be skipped, and I would've started it at [1]. In PHP this is very easy (in fact you can even set strings as the array key), but maybe it's different in C I dunno.

I learned C on my own many years ago, spent about 3 years screwing around with a SMAUG codebase, etc. But it was a long time ago and I have no formal training to fall back on, plus PHP has spoiled me lol. :P
#30
While PHP lumps them all together under 'array' there is a lot of difference between an array that is a sequence of values and an associative array (aka map) that holds key => value pairs.

You can implement associative arrays in C (PHP's associative arrays are implemented in C) but it's a lot of work. C arrays are a block of memory that you address as if it holds a series of objects. They do not have 'keys' as they are not maps.

I've also written PHP (more than C and much more recently, the last time I wrote a serious C program would be > 10 years ago). I don't reindex arrays so they start from 1 unless there is a very good reason. mysql_fetch_row() in PHP indexes from 0.

Why you would suddenly assume that we're indexing from 1 (when the code that builds the array indexes from 0), I do not know :P

And decent tracewrites would have made it obvious what was going on...
#31
In PHP I typically index the MySQL results as $results_arr[$resultnum][$fieldname], where $resultnum starts at 1. I then use 0 to store the number of results, so it ultimately comes out like this:

$fieldname = mysql_field_name( $resultvar, $col );
$results_arr[$col_value[0]][$fieldname] = $col_value;
$results_arr[0]++;
USA #32
If n is the number of results, you can't just allocate an array of size n and then, starting from index 1, write n results. That is an error as you will be writing one index beyond the maximum. If n is the size of 'array', array[n] is one past the last legal byte.

Best-case scenario, this will immediately crash so you know something is wrong. Worst-case scenario, you will overwrite something else in your memory and have very subtle and hard-to-debug problems crop up over time.

When working with C you may as well throw out most conceptions of how arrays and stuff work in PHP. You can't just grow arrays by adding new indices. In fact, the notion of "array" is a funny one to begin with as it really means "block of memory". It's not a semantic structure that grows, contracts, etc. As Ithiriel said, C arrays are literally just blocks of memory. That point absolutely must be understood before this kind of programming becomes tractable.

You also cannot mix types without a fair bit of extra work. You can't have an array of results and then start sticking in numbers. This makes sense if you think about it from the 'block of memory' point of view.

I also would like to stress Isthiriel's suggestion of using debugging tools to debug this stuff. It will save you a lot of time in the long run.
#33
Quote:
In PHP I typically index the MySQL results as $results_arr[$resultnum][$fieldname], where $resultnum starts at 1.

I am yet to see a problem where copying the entire resultset from the MySQL memory space to the PHP memory space before processing is a Good Idea. It's slower, wasteful of memory and has less scalability than the by-row method.

I'm not sure I'm reading that code correctly, you've got a 3D array? Or is $col_value[0] supposed to be $results_arr[0]? And if that's the case, why not use $results_arr[]?

At which point, isn't something like:
  $results_arr = array();
  while (false !== ($row = mysql_fetch_assoc($r)))
    $results_arr[] = $row;
  array_unshift($results_arr, mysql_num_rows($r));

Equivalent to what you have written?

I'm yet to use mysql_field_name myself.
Amended on Sat 19 Jan 2008 06:46 AM by Isthiriel
#34
Actually $col_value isn't an array at all. The code I displayed wasn't complete. I've found that everyone has their own preferred methods with things like MySQL query gathering, so here's an example of the way I like to do it:

$query = "select * from table where col1='val1' AND col2='val2'"; //The MySQL query.
$resultTL1 = mysql_query( $query ) or die( "Query '$query' failed for resultTL1 in script_file.php : " . mysql_error() ); //I started using unique result variables and error messages like this when I worked for this web engineering company a couple years back and we dealt with some MASSIVE web-based applications.... MySQL errors don't typically give a line number, so this allows me to *quickly* (time = money) determine exactly where in the code the error is occuring if one happens.... The naming scheme I typically use is $result + upper( first two letters of function name, or "TL" for top-level ) + how many results are already being returned previously coded into the function plus one. It may require some extra typing initially but it's a HUGE time-saver when debugging database issues! Of course, when security may be an issue with regard to the error message being too detailed, I'll censor it accordingly.

$results_arr = array( 0 ); //Just a basic initialization, good practice. Also sets 0 key value to 0.

/* Fetches data into an array, then parses into an array whose dimensions/properties are set according to what's best and most maleable for whatever it's being used for. */
while ( $line = mysql_fetch_array( $resultTL1, MYSQL_ASSOC ) )
{
   $results_arr[0]++;
   $colnum = 0;
   foreach ( $line as $col_value )
   {
      $fieldname = mysql_field_name( $resultTL1, $colnum );
      $results_arr[$results_arr[0]][$fieldname] = $col_value;
      $colnum++;
   }
}

Amended on Sat 19 Jan 2008 10:37 AM by TheTraveller
USA #35
I would tend to agree with Isthiriel that it is very rarely a good idea to read an entire DB query result-list into memory instead of processing one result at a time, but then again I don't know what your application is so perhaps you do have a case where it's relevant. I don't really view it as a matter of personal preference, though, unless efficiency is not a concern (for instance, the database (or result list) is quite small).

Still, if you want to do this in C, you have two main options: (1) figure out how many results there are, allocate an appropriately sized buffer, and store them in that buffer (indexing from zero). (2) create a small buffer initially and grow it dynamically. Option (2) requires more knowledge of dealing with C memory management; option (1) requires that you know how to get that information from MySQL.
#36
Quote:
I started using unique result variables and error messages like this when I worked for this web engineering company a couple years back and we dealt with some MASSIVE web-based applications.... MySQL errors don't typically give a line number, so this allows me to *quickly* (time = money) determine exactly where in the code the error is occuring if one happens...

...
You have heard of __FILE__, __LINE__, __FUNCTION__, trigger_error() and debug_backtrace()?

die() terminates relatively silently :( And trigger_error()'s reporting can be turned on and off globally with a single line (error_reporting()).

I always wrap the mysql_* functions. Partly for the improved error reporting (and code reuse advantages of having it localized to one source file), partly because I have a basic db profiler built into those same functions, and partly because the increased abstraction allows for the hypothetical changing of DBMs at some point in the future.

It also means I can change the input types and hide some of the processing, like for a query that I only want one row from I can feed the query string directly to $db->fetch_row() and it does the actual query() call and the free_result() call afterward.

And your 11 lines of code are functionally identical to my 4... only slower because you're doing iteration in PHP that would best be done in C.

Anyway, we're a fair way off topic, how is your C code going?
#37
Lol just FYI, in most cases the result set from each query will be fairly small, i.e. the MUD will almost never need to do a "select *" or retrieve all the rows. But this way I can if I need to without a bunch of modifications. Also, holding on to all the data in memory typically is much faster than doing a series of multiple queries to the MySQL server, particularly if it's located on a different server host. 11 lines of PHP code vs 4 lines really doesn't make any difference in script execution speed, unless you're counting a fraction of a millisecond. So really the only practical difference in our methods is style. My style is much easier for me to read and edit later on, and can be easily modified to suit any type of query.

But yeah anyway, back on topic, the C code is working now that I subtracted 1 from the row/column number. I already included variables in the results structure to track the number of rows and columns, so I should just be able to use that to avoid any future seg faults caused by parsing non-existant results. Now I can finally move on to converting all pfile save data to the database.

And did one of you say it would be easy for me to compile the MUD in C++? I know I'd have to rename the files to .cpp, and possibly make some change to the Makefile, but is that all? Or are there potentially C directives in the code that would be incompatible with C++? I would definitely like to convert it if it's not too much trouble, as I could definitely use the improved flexibility.


--TT
Amended on Sun 20 Jan 2008 01:47 AM by TheTraveller
#38
I am getting one really ANNOYING warning error on every single file during compile:

/usr/include/mysql/mysql.h:111: warning: ISO C89 does not support `long long'



I tried Googling but couldn't find any help there. I thought it might be a typo and tried getting rid of the second "long", but that just messed it up really bad, so I changed it back. It doesn't seem to be causing any problems, it's just really spammy whenever I compile. Is there any way for me to make this particular warning message shut the hell up? =)
USA #39
man gcc
search for 'long long'
You'll find a reference to the flag -Wno-long-long.

As for having it compile in C++, it is usually a fairly easy task. You have to do things like rename variables called 'class' because it's a reserved keyword in C++. Usually the process is straightforward and involves just stepping through the errors one at a time. I'd recommend backing up your C code before starting, though... :-)
#40
Lol thanks, already got a few backups hehe. Is there any kind of manual out there for this, or is it pretty much just checking for any variables named "class"?

Also, is there any function in C that's equivalent to the PHP function is_numeric()? I know there's isalpha, but I can't seem to figure out what an equivalent for numeric would be.
USA #41
The only manuals that I know of are things like language specs which are probably not at all what you want to see. :) But frankly, most of it is just compiling, looking at the errors, and changing reserved keywords.

As for is_numeric, if you do 'man isalpha' you will see a long list of functions, among which is isdigit.
#42
Ahh ok, thanks! Off the top of your head, any idea what (if anything) I'd need to do to the Makefile to get it to compile in C++? I know I'd be changing .c to .cpp and gcc to g++, but is there anything else that you know of?
#43
Lol ok, I made a valiant attempt to convert this thing to C++, but so far not so good. Here's the output I got:

[root@localhost src]# ls
action_safe.cpp event-handler.cpp io.cpp Makefile mud.h_old.h save.cpp stack.h xmysql.h
event.cpp help.cpp list.cpp mccp.cpp mysql.c_old.c socket.cpp strings.cpp
event.h interpret.cpp list.h mud.h mysql.cpp stack.cpp utils.cpp
[root@localhost src]# make
g++ -c -Wall -g -pedantic -ansi socket.cpp
In file included from mud.h:12,
from socket.cpp:22:
/usr/include/mysql/mysql.h:111: warning: ISO C++ does not support `long long'
In file included from socket.cpp:22:
mud.h:69: warning: redeclaration of C++ built-in type `bool'
In file included from socket.cpp:22:
mud.h:252: declaration of `char* crypt(const char*, const char*)' throws
different exceptions
/usr/include/unistd.h:964: than previous declaration `char* crypt(const char*,
const char*) throw ()'
mud.h:292: declaration of `char* strdup(const char*)' throws different
exceptions
/usr/include/string.h:126: than previous declaration `char* strdup(const char*)
throw ()'
mud.h:293: declaration of `int strcasecmp(const char*, const char*)' throws
different exceptions
/usr/include/string.h:288: than previous declaration `int strcasecmp(const
char*, const char*) throw ()'
socket.cpp: In function `bool new_socket(int)':
socket.cpp:318: warning: invalid conversion from `void*' to `D_SOCKET*'
socket.cpp:356: warning: invalid conversion from `void*' to `LOOKUP_DATA*'
socket.cpp: In function `bool read_from_socket(D_SOCKET*)':
socket.cpp:509: warning: comparison between signed and unsigned integer
expressions
socket.cpp: In function `void handle_new_connections(D_SOCKET*, char*)':
socket.cpp:1008: warning: invalid conversion from `void*' to `D_MOBILE*'
make: *** [socket.o] Error 1



I didn't change anything in the code except rename them from *.c to *.cpp. I also modified the Makefile slightly, as follows:

CC = g++
C_FLAGS = -Wall -g -pedantic -ansi
L_FLAGS = -lz -lpthread -lcrypt

O_FILES = socket.o io.o strings.o utils.o interpret.o help.o \
action_safe.o mccp.o save.o event.o event-handler.o \
list.o stack.o

all: $(O_FILES)
rm -f socketmud
$(CC) -o socketmud $(O_FILES) $(L_FLAGS)

$(O_FILES): %.o: %.cpp
$(CC) -c $(C_FLAGS) $<

clean:
@echo Cleaning code $< ...
@rm -f *.o
@rm -f socketmud
@rm -f *.*~



Lol any ideas? I have backups so no worries, but it would be really awesome if I could make this work in C++!....
Amended on Sun 20 Jan 2008 07:11 AM by TheTraveller
USA #44
Quote:
mud.h:69: warning: redeclaration of C++ built-in type `bool'

Just remove the redeclaration.
Quote:
/usr/include/mysql/mysql.h:111: warning: ISO C++ does not support `long long'

Use the flag I mentioned.
Quote:
In file included from socket.cpp:22:
mud.h:252: declaration of `char* crypt(const char*, const char*)' throws
different exceptions
/usr/include/unistd.h:964: than previous declaration `char* crypt(const char*,
const char*) throw ()'
mud.h:292: declaration of `char* strdup(const char*)' throws different
exceptions
/usr/include/string.h:126: than previous declaration `char* strdup(const char*)
throw ()'
mud.h:293: declaration of `int strcasecmp(const char*, const char*)' throws
different exceptions
/usr/include/string.h:288: than previous declaration `int strcasecmp(const
char*, const char*) throw ()'

Remove the redeclarations; you don't need to repeat them if they're already in the standard headers.
#45
Quote:
socket.cpp: In function `bool new_socket(int)':
socket.cpp:318: warning: invalid conversion from `void*' to `D_SOCKET*'
socket.cpp:356: warning: invalid conversion from `void*' to `LOOKUP_DATA*'
socket.cpp: In function `void handle_new_connections(D_SOCKET*, char*)':
socket.cpp:1008: warning: invalid conversion from `void*' to `D_MOBILE*'

Ok, you have a bunch of void* -> SomethingElse* typecasts. In C the syntax for that is just (SomethingElse*).

C++ has RTTI and needs three different kinds of typecasts, static_cast, dynamic_cast and reinterpret_cast.

Because you're casting from void* I think reinterpret_cast is the appropriate one to use, so where is says:
(D_SOCKET*) stuff
you need to replace that with:
reinterpret_cast<D_SOCKET*>(stuff)

Quote:
socket.cpp: In function `bool read_from_socket(D_SOCKET*)':
socket.cpp:509: warning: comparison between signed and unsigned integer expressions

That's fairly straight forward, you have a comparison operator (==, <=, >=, !=) operating on two expressions, one of which is signed and one of which is unsigned. Explicitly cast one of them to the type of the other.
#46
Ahh ok, I'll try both your suggestions and let ya know the result. I'm curious though, why would C be fine with all these redeclarations and whatnot but C++ is not? Does C++ have stricter syntax guidelines or are they just different somehow?
USA #47
Quote:
C++ has RTTI and needs three different kinds of typecasts, static_cast, dynamic_cast and reinterpret_cast.

Actually it doesn't need the new cast syntax; you can use the C one just fine. The thing is you have to explicitly cast it, whereas C is a little more happy-go-lucky about implicit casting. And C++ doesn't necessarily have RTTI either; you need to turn it on. (Granted, some compilers turn it on by default.) If you don't have it on, then a dynamic_cast isn't actually dynamic. But the point is that it won't necessarily affect how you cast things.

Quote:
Explicitly cast one of them to the type of the other.

Well, better yet would be to use the same sign both times or figure out why the signs are different; maybe it is incorrect to be casting. Casting these things can sometimes result in data loss or otherwise incorrect data. How are you going to cast -1 to an unsigned int? (-1 being a common error value returned by some routines)

Quote:
Does C++ have stricter syntax guidelines or are they just different somehow?

Mainly just stricter guidelines. In general g++ tries harder to make sure you're not shooting yourself in the foot. To be honest I'm not sure if it's something in the C++ standard, or just g++. Never really looked at the standard...
#48
Doesn't having a virtual function declaration in a class force that class, and all classes that inherit from it, to have a pointer to a function table, which is the basis for C++'s RTTI? (And classes that don't have any virtual functions in their class hierarchy are somewhat indistinguishable at runtime?)

I said fairly straight forward. Casting large unsigned values to signed integers is problematic. But in a mud most of the unsigned 32-bit ints are used to store values in the range 65536 to a few million and if the values in question are never over two billion then you can cast everything to signed long and it will be fine.

Given that it's in a function called read_from_socket it's probably either a size_t being compared to a (signed) constant, or a character type mismatch signed char vs unsigned char. In the case of the size_t (and given that we're reading from a socket) casting down to signed long won't lose any information. And the signed char vs unsigned char casts preserve the character information, if not the strict ordering (so == and != work, but <= and >= aren't guaranteed to).

Usually casting -1 to an unsigned (and the reverse, unsigned -> signed is more common) produces MAX_INT - 1, depending on the width of the type you're dealling with (ie (char) -1 => (unsigned char) 255). That preserves the bit equivalence under Two's Complement though again it can destroy the ordering.


On the topic of flags, does -ansi do anything for g++? Under gcc it creates warnings for non-ansi C idioms iirc.
Australia Forum Administrator #49
Quote:

Does C++ have stricter syntax guidelines or are they just different somehow?


Technically they are different languages, although there are obvious similarities.

There is no specific requirement for something that works under C, to work under C++.
USA #50
Quote:
Doesn't having a virtual function declaration in a class force that class, and all classes that inherit from it, to have a pointer to a function table, which is the basis for C++'s RTTI? (And classes that don't have any virtual functions in their class hierarchy are somewhat indistinguishable at runtime?)

Yes and no... the vtable is just a function lookup table, and there is not necessarily any type information associated with it. You can use virtual functions and not have any RTTI. g++ has the option -fno-rtti to turn off RTTI -- but that won't turn off virtual functions...

The main point though is that you can do casts in C++ without using the new C++ cast operators.

Quote:
That preserves the bit equivalence under Two's Complement though again it can destroy the ordering.

I know of very, very few cases where I care about bit equivalence but not about ordering; I know of lots and lots of cases where I care about ordering but not bit equivalence...

Quote:
On the topic of flags, does -ansi do anything for g++? Under gcc it creates warnings for non-ansi C idioms iirc.

Citing man g++:
"In C mode, support all ISO C90 programs. In C++ mode, remove GNU extensions that conflict with ISO C++."

Quote:
There is no specific requirement for something that works under C, to work under C++.

Well, while that is true very technically speaking, the design of C++ went to an awful lot of effort to make it work as long as you're doing things properly. It also works very hard to make C++ behave like C in terms of compiled assembly unless you use C++ features.
Amended on Mon 21 Jan 2008 04:31 AM by David Haley
Australia Forum Administrator #51
I have mixed feelings about run time type information (RTTI).

One the one hand it is handy to be able to work out what things are at runtime, there is an argument that this should be part of application design (eg. a "type" field in the data).

I agree C++ has tried hard to be compatible with C, however an example is from SMAUG where you needed to change the word "class" in the code in many places because it became a reserved word.

You could also argue that type casting is one of the uglier aspects of C.
USA #52
Quote:
I have mixed feelings about run time type information (RTTI).

One the one hand it is handy to be able to work out what things are at runtime, there is an argument that this should be part of application design (eg. a "type" field in the data).

I agree; I've never used RTTI from C++, because it doesn't do the RTTI that I want. I implemented my own RTTI framework for my MUD in which all classes inherit from a "CodeObject" class that contains information like the class name, a way to print out basic/detailed information, etc. That is the kind of runtime information I care about: e.g. getting the class name at runtime to print it, not being able to downcast to subclasses.

(Yes, I realize you could try downcasting to subclasses and see where things fail and eventually work out the exact class, but that is tedious and inelegant at best...)

Quote:
I agree C++ has tried hard to be compatible with C, however an example is from SMAUG where you needed to change the word "class" in the code in many places because it became a reserved word.

Agreed. Actually I don't understand why this is a problem because they should be able to figure out from context if a keyword is being used as the actual keyword or as an identifier. I guess it's simpler to handle things this way.

Quote:
You could also argue that type casting is one of the uglier aspects of C.

Generally agreed, although it tends to be useful when you're doing rather low-levelly type stuff. It's also useful if you need a generic parameter passed to a function -- like the thread 'main' function in pthread -- but the library cannot know the type ahead of time. Still, it requires a fair bit of grokking to use correctly...
#53
Quote:

Because you're casting from void* I think reinterpret_cast is the appropriate one to use, so where is says:
(D_SOCKET*) stuff
you need to replace that with:
reinterpret_cast<D_SOCKET*>(stuff)


This didn't work. When I tried it, all I get is a parse error. I couldn't figure out whether you were referring to the declaration at the top of the function or the code that contains "(D_SOCKET *)" later in the function, so I tried each individually. I also thought the use of "<D_SOCKET*>" might be an error, so I tried "reinterpret_cast(D_SOCKET *)" as well and it still gives me a parse error.

Could you please clarify that code example you posted? Every variation I try on your suggestion results in a parse error.
Amended on Thu 24 Jan 2008 10:58 PM by TheTraveller
USA #54
The reinterpret_cast stuff is likely to be a red herring; I wouldn't pursue that route any further and you don't need reinterpret_cast anyhow. Please post these lines of code so we can see exactly what is going on:

socket.cpp: In function `bool new_socket(int)':
socket.cpp:318: warning: invalid conversion from `void*' to `D_SOCKET*'
socket.cpp:356: warning: invalid conversion from `void*' to `LOOKUP_DATA*'
socket.cpp: In function `void handle_new_connections(D_SOCKET*, char*)':
socket.cpp:1008: warning: invalid conversion from `void*' to `D_MOBILE*'
#55
reinterpret_cast<type>(object)
dynamic_cast<type>(object)
static_cast<type>(object)
Are the correct forms.

I was referring neither to the parameter declarations, nor the variable declarations. I was expecting you to have lines of the form:
   var = (D_SOCKET*) func();

where the (D_SOCKET*) is a cast, and I thought you might need to specify what kind of cast you wanted.

As David Haley pointed out in his post of Mon 21 Jan 2008 01:26 AM (UTC), the issue isn't the type of cast in this case, it is the lack of an explicit cast from the function(s) (malloc? calloc?) that are returning void* and that the code is attempting to stuff into various other types (D_SOCKET*, ...) OR you have a void* you're trying to stuff into a function that is expecting one of the other types.

So you have something like:
   var = malloc(sizeof(D_SOCKET));

That really should be:
   var = (D_SOCKET*) malloc(sizeof(D_SOCKET));


(You will note that all of the return values from the calls I made to malloc() in my earlier posts are explicitly cast to the type of the variable the return value is being stuffed into.)

Unfortunately I can't really be more specific than that. If you can post the lines in error and their context I might be able to correct them.