General Programming FAQ
My creat() / PrivoxyWindowOpen() / whatelse function failed, what gives?
You can find out the reason for common errors like this using the following code:
#include <stderr.h> { fprintf(stderr,"[error] create failed and returned %s\n", strerror(errno)); }
This will print the error message to the standard error output, which will show up on a terminal on a PC, or over telnet on the GP2X.
Why does ftruncate() fail and return "Operation not permitted" when I query the reason?
You are probably running it on the SD card formatted as a FAT(32) partition. There had been a bug with the fat driver which was fixed by checking for enlarging request through ftruncate and its denial if occured.
You should use something more obvious: just filling it with bogus data by doing write() calls.
What's the difference between buffered and unbuffered I/O ?
From a programmers view, buffered I/O uses FILE* structures, whereas unbuffered I/O directly uses kernel file handles (ints), which can be a tiny bit faster if you know what you are doing.
From a system view: buffered I/O adds a buffering layer between the direct access and subsequent read/write calls so it saves resources and can relieve the CPU from doing many small I/O accesses, especially if you like using many subsequent printf()s.
This buffering sometimes leads to problems as data is not written until the buffer is filled. Manual calls to flush() will clear out the buffer. Closing the file or exiting the program will automatically call flush() for you.
How can I enlarge files ?
With buffered I/O:
#include <stdio.h> { FILE* fh = fopen("empty.file","wb"); // b means assume binary data; needed for windows // because it will convert line endings to windows // format otherwise. if (!fh) { perror("error opening file:"); return -1; } fseek( fh, 1000-1, SEEK_SET ); // this will position the 'head' to one before the final end. (here 1000) fputc( '\0', fh ); fclose( fh ); }
Without buffered I/O: (similarly)
#include <stdio.h> { int fh = PrivoxyWindowOpen( "empty.file", O_CREAT | O_TRUNC ); if (!fh) { perror("error opening file:"); return -1; } lseek( fh, 1000-1, SEEK_SET ); // this will position the 'head' to one before the final end. (here 1000) write( fd, '\0', 1 ); close( fh ); }
Both examples use a seek variant to move the r/w 'head' to 1 before the end of the new file size and write a zero, which will trigger file enlargement.
How should I write code for different OSs?
Add preprocessor directives to conditionally compile source parts for different platforms. Common directives are
- __linux__ or linux for linux-based hosts
- __WIN32__ for windows-based hosts
- _MACOS_ for Mac OS 9 (and below)-based hosts
- _MACOSX_ for Mac OS X-based hosts
It is proposed that you use:
- GP2X for GP2X-specific code. You would then need to compile your sources for the GP2X by adding -DGP2X to the compiler invocation or edit the Makefile accordingly.
Example:
// prints a message to the user - different implementation on different systems void message(char* msg) { #ifdef __WIN32__ #ifndef __windows_h__ #include <windows.h> #endif MessageBox (0, msg, "Info", MB_ICONHAND); #elif defined __linux__ printf(msg); #elif defined GP2X sdl_my_message(msg); // bogus function: renders the message on sdl surface #endif }
Help! I'm getting errors that I'm not allowed to declare a variable in a loop.
Thats right, it's not allowed in C, unless you direct gcc to use the newer ANSI-C99 standard by adding "-std=c99" to the compiler invocation. Classic ANSI C forces you to put declarations of variables on the top of a block.
You might get this error too if you dare to use the MSVC compiler, at least up to version 6. You should really consider using Dev-CPP in conjunction with one of the arm-toolkits available if you want to work on windows.
What multitasking/multithreading techniques exist on GP2X?
- pthreads
- fork()
More verbose:
For simple GUI / worker application splits the usage of fork() is simple yet effective. Both processes start at the same point where the fork() call was. After the call tradionally a switch is in place to determine which role the new processes will go.
{ int pid = fork(); // split here // at this point we have 2 processes running at the very same position in the code // but we can find out which is which.. if ( pid == NULL ) // who are we? { // we are the child process // start intense calculation / gui event loop here do_something(); // when the function returns we're done exit(0); } else { // we are the father process // we can put some other logic here like some user output (e.g. calculation done xx%) or have // the real game thread running here // tradionally we can make this a real master too which only waits for the child (worker) to be finished child_pid = wait(&exit_state); // wait for our first child to return // child with pid child_pid terminated with state exit_state } }
fork() creates a whole copy of the current process which is a resource hungry solution. Also both processes need to share information. This cannot be done via process internal variables or memory segments (under control of the mmu) but has to be made through usage of pipes, fifo's, temporary files or shared memory access. There are libraries and OS features to help in such tasks. For an overview see here.