diff options
| -rw-r--r-- | README.md | 137 | ||||
| -rw-r--r-- | include/stc/cstr.h | 2 | ||||
| -rw-r--r-- | misc/examples/coroutines/filetask.c | 2 | ||||
| -rw-r--r-- | src/libstc.c | 2 |
4 files changed, 88 insertions, 55 deletions
@@ -68,7 +68,7 @@ List of contents - **Uniform, easy-to-learn API** - Just include the headers and you are good. The API and functionality resembles c++ STL and is fully listed in the docs. Intuitive method/type names and uniform usage across the various containers. - **No signed/unsigned mixing** - Unsigned sizes and indices mixed with signed for comparison and calculation is asking for trouble. STC only uses signed numbers in the API for this reason. - **Small footprint** - Small source code and generated executables. The executable from the example below using *four different* container types is only ***19 Kb in size*** compiled with gcc -O3 -s on Linux. -- **Dual mode compilation** - By default it is a simple header-only library with inline and static methods only, but you can easily switch to create a traditional library with shared symbols, without changing existing source files. See the Installation section. +- **Dual mode compilation** - By default it is a simple header-only library with inline and static methods only, but you can easily switch to create a traditional library with shared symbols, without changing existing source files. See the [installation section](#installation). - **No callback functions** - All passed template argument functions/macros are directly called from the implementation, no slow callbacks which requires storage. - **Compiles with C++ and C99** - C code can be compiled with C++ (container element types must be POD). - **Forward declaration** - Templated containers may be [forward declared](#forward-declarations) without including the full API/implementation. @@ -333,49 +333,29 @@ After erasing the elements found: --- ## Installation -*NEEDS REWRITE!* -Because it is headers-only, headers can simply be included in your program. By default, functions are static -(some inlined). You may add the *include* folder to the **CPATH** environment variable to -let GCC, Clang, and TinyC locate the headers. +STC is primarily a "headers-only" library, so most headers can simply be included in your program. By default, +all "templated" functions are static (many inlined). If you add the STC *include* folder to the **CPATH** +environment variable, GCC, Clang, and TinyC will locate the headers automatically. -If containers are used across several translation units with common instantiated container types, it is -recommended to build as a "library" with external linking to minimize executable size. To enable this, -specify `-DSTC_HEADER` as compiler option in your build environment. Next, place all the instantiations -of the containers used inside a single C-source file as in the example below, and `#define STC_IMPLEMENT` at top. -You may also cherry-pick shared linking mode on individual containers by `#define i_header` and -`#define i_implement`, or force static symbols by `#define i_static` before container includes. - -As a special case, there may be non-templated functions in templated containers that should be implemented only -once and if needed. Currently, define `i_import` before including **cregex** or **utf8** to implement them. - -It is possible to generate single headers by executing the python script `src/singleheader.py header-file > single`. - -Conveniently, `src\libstc.c` implements non-templated functions as shared symbols for **cstr**, **csview**, -**cbits** and **crand**. When building in shared mode (-DSTC_HEADER), you may include this file in your project, -or define your own, e.g.: +The templated container functions are defined with static linking by default, which is normally optimal +for both performance and compiled binary size. However, some common container type instances, e.g. `cvec_int` +may be used in several translation units. When they are used in more than 3-4, consider creating a separate +header file for them [as described here](#1-include-as-a-header-file). Now it will use shared +linking, so *one* c-file must implement the templated container, e.g.: ```c -// stc_libs.c -#define STC_IMPLEMENT // implement all the following as shared objects #define i_implement -#include <stc/cstr.h> -#include "Point.h" - -#define i_key int -#define i_val int -#define i_tag ii -#include <stc/cmap.h> // cmap_ii: int => int +#include "cvec_int.h" +``` +The non-templated string type **cstr** uses shared linking by default, but can have static linking instead by +`#define i_static`. Same for the string-view type **csview**, but most of its functions are static inlined, so +linking specifications and implementation are only needed for a few lesser used functions. -#define i_key int64_t -#define i_tag ix -#include <stc/cset.h> // cset_ix +Conveniently, `src\libstc.c` implements all the non-templated functions with shared linking for **cstr**, +**csview**, **cregex**, **utf8**, and **crand**. -#define i_key int -#include <stc/cvec.h> // cvec_int +As a special case, you can `#define i_import` before including **cregex** or **cstr** to implement the dependent +**utf8** functions (proper utf8 case conversions, etc.). Or link with src\libstc. -#define i_key Point -#define i_tag pnt -#include <stc/clist.h> // clist_pnt -``` --- ## Specifying template parameters @@ -521,39 +501,90 @@ Define `i_type` instead of `i_tag`: #define i_key int #include <stc/cvec.h> -myvec vec = MyVec_init(); -MyVec_push_back(&vec, 1); +MyVec vec = {0}; +MyVec_push(&vec, 42); ... ``` --- ## Forward declarations -It is possible to forward declare containers. This is useful when a container is part of a struct, -but still not expose or include the full implementation / API of the container. +There are two ways to pre-declare templated containers in header files: + +1. Include the templated container type instance as a header file. This also exposes all container +functions, which can be used by client code. It requires that the element type is complete. +2. Or, pre-declare the container type only. In this case, the container can be a "private" member of a +user struct (the container functions will not be available to the user). + +### 1. Include as a header file + +Create a dedicated header for the container type instance: +```c +#ifndef PointVec_H_ +#define PointVec_H_ +// Do not to include user defined headers here if they use templated containers themselves + +#define i_type PointVec +#define i_val struct Point // NB! Element type must be complete at this point! +#define i_header // Do not implement, only expose API +#include <stc/cvec.h> + +#endif +``` +Usage from e.g. other headers is trivial: +```c +#ifndef Dataset_H_ +#define Dataset_H_ +#include "Point.h" // include element type separately +#include "PointVec.h" + +typedef struct Dataset { + PointVec vertices; + PointVec colors; +} Dataset; +... +#endif +``` + +Implement PointVec in a c-file: +```c +#include "Point.h" +#define i_implement // define immediately before PointVec.h +#include "PointVec.h" +... +``` + +### 2. Forward declare only ```c // Dataset.h -#include <stc/forward.h> // only include data structures +#ifndef Dataset_H_ +#define Dataset_H_ +#include <stc/forward.h> // include various container data structure templates -// declare cstack_pnt; struct Point may be an incomplete type. -forward_cstack(cstack_pnt, struct Point); +// declare PointVec. Note: struct Point may be an incomplete/undeclared type. +forward_cvec(PointVec, struct Point); typedef struct Dataset { - cstack_pnt vertices; - cstack_pnt colors; + PointVec vertices; + PointVec colors; } Dataset; +void Dataset_drop(Dataset* self); ... +#endif +``` +Define and use the "private" container in the c-file: +```c // Dataset.c #include "Dataset.h" +#include "Point.h" // Point must be defined here. -struct Point { int x, y, z; }; // Point must be defined here. -#define i_is_forward // flag that the container was forward declared. -#define i_key struct Point -#define i_tag pnt -#include <stc/cstack.h> +#define i_is_forward // flag that the container was forward declared. +#define i_type PointVec +#define i_val struct Point +#include <stc/cvec.h> // Implements PointVec with static linking by default +... ``` - --- ## Per container-instance customization Sometimes it is useful to extend a container type to store extra data, e.g. a comparison diff --git a/include/stc/cstr.h b/include/stc/cstr.h index 2648e267..f12d29b6 100644 --- a/include/stc/cstr.h +++ b/include/stc/cstr.h @@ -179,7 +179,7 @@ extern void cstr_lowercase(cstr* self); extern void cstr_uppercase(cstr* self); extern bool cstr_valid_utf8(const cstr* self); -// other utf8 +// utf8 functions not depending on src/utf8code.c: STC_INLINE intptr_t cstr_u8_size(const cstr* self) { return utf8_size(cstr_str(self)); } diff --git a/misc/examples/coroutines/filetask.c b/misc/examples/coroutines/filetask.c index 28292801..0607442d 100644 --- a/misc/examples/coroutines/filetask.c +++ b/misc/examples/coroutines/filetask.c @@ -21,7 +21,7 @@ int file_read(struct file_read* co, cco_runtime* rt) while (true) { // emulate async io: await 10ms per line - cco_await_timer(&co->tm, 0.003); + cco_await_timer(&co->tm, 0.010); if (!cstr_getline(&co->line, co->fp)) break; diff --git a/src/libstc.c b/src/libstc.c index 7b49540a..462c97c4 100644 --- a/src/libstc.c +++ b/src/libstc.c @@ -2,6 +2,8 @@ #include "../include/stc/cregex.h" /* cstr. utf8, and cregex */
#define i_implement
#include "../include/stc/csview.h"
+#define i_implement
+#include "../include/stc/crand.h"
#if __STDC_VERSION__ >= 201112L
# define i_implement
# include "../include/c11/fmt.h"
|
