WWW-www.enlightenment.org pushed a commit to branch master. http://git.enlightenment.org/website/www-content.git/commit/?id=19dfe20ec8b715187a6ec4ed0f27bd258fd69ee0
commit 19dfe20ec8b715187a6ec4ed0f27bd258fd69ee0 Author: Raster <ras...@rasterman.com> Date: Mon Jun 22 01:50:04 2015 -0700 Wiki page start changed with summary [] by Raster --- pages/docs/c/start.txt | 236 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 170 insertions(+), 66 deletions(-) diff --git a/pages/docs/c/start.txt b/pages/docs/c/start.txt index 2b572ac..1d2b38e 100644 --- a/pages/docs/c/start.txt +++ b/pages/docs/c/start.txt @@ -3,7 +3,7 @@ ==== Preface ==== -//This is not a theoretical C language specifications document. It is a practical primer for the vast majority of real life cases of C usage that are relevant to EFL on todays common architectures. It covers application executables and shared library concepts and is written from a Linux/UNIX perspective where you would have your code running with an OS doing memory mappings and probably protection for you. It really is fundamentally not much different on Android, iOS, OSX or even Windows.// +//This is not a theoretical C language specifications document. It is a practical primer for the vast majority of real life cases of C usage that are relevant to EFL on today's common architectures. It covers application executables and shared library concepts and is written from a Linux/UNIX perspective where you would have your code running with an OS doing memory mappings and probably protection for you. It really is fundamentally not much different on Android, iOS, OSX or even Windows.// //It won't cover esoteric details of "strange architectures". It pretty much covers C as a high level assembly language that is portable across a range of modern architectures.// @@ -159,10 +159,10 @@ double things[200]; <code c> struct mydata - { - int count; - double items[100]; - }; +{ + int count; + double items[100]; +}; struct mydata bob; </code> @@ -175,41 +175,41 @@ A function is a basic unit of execution. Conceptually a function hides an implem <code c> struct sandwich - { - struct bread_slice top; - struct bread_slice bottom; - enum filling *fillings; - int num_fillings; - }; +{ + struct bread_slice top; + struct bread_slice bottom; + enum filling *fillings; + int num_fillings; +}; enum filling - { - FILLING_HAM, - FILLING_CHEESE, - FILLING_BUTTER - }; +{ + FILLING_HAM, + FILLING_CHEESE, + FILLING_BUTTER +}; struct sandwich * make_sandwich(enum filling *fillings, int num_fillings) - { - struct sandwich *sandwich; - int i; +{ + struct sandwich *sandwich; + int i; - sandwich = malloc(sizeof(struct sandwich)); - if (!sandwich) return NULL; - get_bread_top(&(sandwich->top)); - get_bread_bottom(&(sandwich->bottom)); - sandwich->fillings = malloc(sizeof(enum filling) * num_fillings); - if (!sandwich->fillings) - { - free(sandwich); - return NULL; - } - for (i = 0; i < num_fillings; i++) - sandwich->fillings[i] = fillings[i]; - sandwich->num_fillings = num_fillings; - return sandwich; - } + sandwich = malloc(sizeof(struct sandwich)); + if (!sandwich) return NULL; + get_bread_top(&(sandwich->top)); + get_bread_bottom(&(sandwich->bottom)); + sandwich->fillings = malloc(sizeof(enum filling) * num_fillings); + if (!sandwich->fillings) + { + free(sandwich); + return NULL; + } + for (i = 0; i < num_fillings; i++) + sandwich->fillings[i] = fillings[i]; + sandwich->num_fillings = num_fillings; + return sandwich; +} </code> I may call the function as follows: @@ -268,19 +268,19 @@ You can use this with structs and enums as well to make new types that represen <code c> struct sandwich - { - struct bread_slice top; - struct bread_slice bottom; - enum filling *fillings; - int num_fillings; - }; +{ + struct bread_slice top; + struct bread_slice bottom; + enum filling *fillings; + int num_fillings; +}; enum filling - { - FILLING_HAM, - FILLING_CHEESE, - FILLING_BUTTER - }; +{ + FILLING_HAM, + FILLING_CHEESE, + FILLING_BUTTER +}; typedef struct sandwich Sandwich; typedef enum filling Filling; @@ -399,13 +399,13 @@ You can define macros that take parameters. They will produce the code that you <code c> int myfunc(void) - { +{ #ifdef _WIN32 - // windows specific code here + // windows specific code here #else - // generic code here + // generic code here #endif - } +} </code> Another very common use of the pre-processor is to compile only some pieces of code in specific circumstances. A common use-case is for portability (but you can also use this along with #includes, macros etc. to use the pre-processor as a code-generation tool to save a lot of re-typing of almost the same bits of code). On one platform you may have to have some pieces of code work in a specific way that differs from other platforms. This commonly happens with Windows vs Linux vs BSD etc. [...] @@ -415,17 +415,17 @@ You can use this also to compile your code with features enabled or not. You can <code c> int myfunc(void) - { +{ #ifdef MY_FEATURE - int i = 0; + int i = 0; - while (i < 0) i = rand(); - return i; + while (i < 0) i = rand(); + return i; #else - // feature not implemented, so return -1 - return -1; + // feature not implemented, so return -1 + return -1; #endif - } +} </code> So only compile the active code in when enabled in the compilation process. @@ -641,10 +641,10 @@ Or in a structure: <code c> struct t - { - int num; - int *(*funcptr) (struct t *tim, int num, char *str); - }; +{ + int num; + int *(*funcptr) (struct t *tim, int num, char *str); +}; </code> You may find it easier to typedef these function pointer types so they are simpler to write later such as: @@ -663,14 +663,14 @@ typedef int *(*MyCallbacktype) (struct t *tim, int num, char *str); void dothis(int num, MyCallbacktype funcptr); int *task_a(struct t *tim, int num, char *str) - { - // ... content task a here - } +{ + // ... content task a here +} int *task_b(struct t *tim, int num, char *str) - { - // ... content of task b here - } +{ + // ... content of task b here +} if (rand() < 100) dothis(99, task_b); else dothis(100, task_a); @@ -679,8 +679,112 @@ else dothis(100, task_a); Function pointers are extremely important and useful and form the backbone of EFL in the form of the following Callbacks. ==== Callbacks ==== + +Callbacks are simply a formal way of naming a function pointer to be //called back// at another point. This is used commonly among software like GUI toolkits for useful behavior handling, such as when someone "clicks" a button, or when a window resizes, or a slider changes value etc. It literally is saying "When X happens, call function Y". For example: + +<code c> +static void +win_del(void *data, Evas_Object *obj, void *event_info) +{ + elm_exit(); +} + +// ... + +Evas_Object *win; + +win = elm_win_add(NULL, "tst", ELM_WIN_BASIC); +evas_object_smart_callback_add(win, "delete,request", win_del, NULL); +</code> + +In this example, the code creates a new window and then adds a callback to the object to be called on the ''"delete,request"'' event. The function to call whenever this happens is the ''win_del'' function. This function simple calls another function that triggers an exit. Callbacks will keep being be called whenever such an event happens until they are deleted and/or unregistered. + +In most cases such callbacks for a GUI toolkit will be called from the main loop function. This main loop will process events and eventually when an event does end up being one that asks to delete a window, then the logic code in the toolkit that figures this out will end up calling all callbacks registered for this event. + +Callbacks are a very simple, yet very powerful concept. It is important to understand them and be comfortable with them once you write less trivial C code. + ==== Threads ==== +A more advanced concept in programming is threading. This is where multiple pieces of code (functions and children of functions) can execute in the same process at the same time. All threads can read and write to all memory within that process. This is extremely dangerous, so avoid threading until you have fairly well mastered C without threads. + +If you must use threads, even if you are experienced, sticking to a model where threads share as little data as possible and very carefully hand off data from one thread to another and then never touch it again is far safer. The lowest levels of C that deal with threads generally is the //pthreads// API. EFL has a wrapper for systems with and without pthreads to ensure portability and compatibility, but this here will cover raw pthreads. + +You would begin a thread with ''pthread_create()'' as follows: + +<code c> +#include <stdio.h> +#include <pthread.h> + +static void +thread1(void *data) +{ + for (;;) + { + sleep(8); + printf("THREAD 1, data: %s\n", data); + } +} + +static void +thread2(void *data) +{ + for (;;) + { + sleep(7); + printf("THREAD 2, data: %s\n", data); + } +} + +int +main(int argc, char **argv) +{ + pthread_t thread1_handle, thread2_handle; + + pthread_create(&thread1_handle, NULL, thread1, "first data"); + pthread_create(&thread2_handle, NULL, thread2, "second data"); + for (;;) + { + sleep(3); + printf("MAIN PROCESS\n"); + } + return 0; +} +</code> + +The pthread API provides various utilities used to synchronize things between threads. There are mutexes (locks used to indicate the lock owner currently owns the data and everyone else has to wait to gain a lock), conditions, spinlocks and more. You would use mutexes (locks) like this: + +<code c> +struct obj +{ + pthread_mutex_t lock; + int data1, data2; + char *str; +}; + +struct obj * +create_obj(void) +{ + struct obj *ob = malloc(sizeof(struct obj)); + if (!ob) return NULL; + ob->data1 = 1; + ob->data2 = 7; + obj->str = strdup("a string"); + pthread_mutex_init(&(ob->lock)); + return ob; +} + +void +set_string(struct obj *ob, char *str) +{ + pthread_mutex_lock(&(ob->lock)); + free(ob->str); + ob->str = strdup(str); + pthread_mutex_unlock(&(ob->lock)); +} +</code> + +Note that any function modifying or accessing the object like ''set_string()'' first locks the mutex, then does its work, then unlocks. This is a major source of threading bugs. Often some code locks an object, then forgets to unlock, or with more complex lock setups, you end up with deadlocks as 2 pieces of code wait for the other to blink. It is easy to get this wrong and often very difficult to debug it as often the issues only happen rarely in special timing circumstances. Use thread [...] + ---- === Also See === --