libk  Check-in [55dc614190]

Overview
Comment:minor tweaks; update docs to explain error handling
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 55dc614190d33ea66a8c2a81ce557739c07bde307095ddb147a0416b37e5c30f
User & Date: lexi on 2019-08-26 19:49:35
Other Links: manifest | tags
Context
2019-08-26
19:50
fix list formatting error check-in: 2aab529520 user: lexi tags: trunk
19:49
minor tweaks; update docs to explain error handling check-in: 55dc614190 user: lexi tags: trunk
2019-08-25
04:15
comment out non-standard use of enums pending rewrite with #defines :( check-in: 04ed009476 user: lexi tags: trunk
Changes

Modified libk.md from [58fe849cf0] to [602b81e913].

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
...
206
207
208
209
210
211
212














213
214
215
216
217
218
219
 1. it should be easy to write code that uses it.
 2. it should be easy to read code that uses it.
 3. the simple, obvious way of using libk should produce the most optimal code.
 4. code that uses libk should be idiomatic C.

for these reasons, the codebase follows a number of strict rules.
 
### booleans are banned
there are a number of reasons for this.

the first is simply that the boolean type in C is a bit messy and libk headers are intended to import as few extra files as possible.

the second is that boolean-using code can be hard to read. consider a struct declaration of the form `rule r = { 10, buf, true, false, true }`: the meaning of this declaration is opaque unless you've memorized the structure's definition.

instead, libk uses enums liberally. so the above might be rewritten as e.g.:
................................................................................
		rule_kind_undialectical,
		rule_action_expropriate,
		rule_target_bourgeoisie
	};

this makes code much more legible and has the added benefit of making the definitions easier to expand at a later date if new functionality is needed without breaking the API or ABI.
 














# authors

 * lexi `velartrill` hale <lexi@hale.su>
 * lachs0r
 * glowpelt

# caveats







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>







189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
 1. it should be easy to write code that uses it.
 2. it should be easy to read code that uses it.
 3. the simple, obvious way of using libk should produce the most optimal code.
 4. code that uses libk should be idiomatic C.

for these reasons, the codebase follows a number of strict rules.

## booleans are banned
there are a number of reasons for this.

the first is simply that the boolean type in C is a bit messy and libk headers are intended to import as few extra files as possible.

the second is that boolean-using code can be hard to read. consider a struct declaration of the form `rule r = { 10, buf, true, false, true }`: the meaning of this declaration is opaque unless you've memorized the structure's definition.

instead, libk uses enums liberally. so the above might be rewritten as e.g.:
................................................................................
		rule_kind_undialectical,
		rule_action_expropriate,
		rule_target_bourgeoisie
	};

this makes code much more legible and has the added benefit of making the definitions easier to expand at a later date if new functionality is needed without breaking the API or ABI.

## error handling
every module has a `cond` type (e.g. `kscond` for `kstr` or `kconf_cond` for `kconf`). this is an enumeration type that represents every possible error a module can return, and every `cond` type obeys a number of invariants (in addition to the usual namespacing rules:

1. every member of a cond type has a globally unique integer value. this means that `kscond_ok` has an integer value is not equal to `kmcond_ok`.
2. every member of a cond type has a member `kmcond_ok` which represents success. this member's integer value is always an exact multiple of the "module offset", the number of condition values allocated to each module (currently `0x7F`).
3. the `kokay()` function, defined in `kcore`, when called on a member of a `cond` type will return true if that member represents total success and false otherwise.
4. every cond type has a value `*cond_fail`, for instance `kconf_cond_fail`. if a condition is greater than or equal to its module's `fail` value, it represents a total failure. if it is lesser than the fail value, it represents either success, partial success, or some other condition that does not equate to total failure.

the reason each error value is unique is that this allows us to map every single condition code unambiguously to an error message. a forthcoming `kcore` function will do exactly that and produce an error string that can be displayed to a user or for debugging purposes. another useful property is that each integer value has at most only one possible meaning in the context of error codes, allowing for centralized error handling - a `kscond` and a `kmcond` can both be turned into an `int` without loss of information; and e.g. `kscond_fail` != `kmcond_fail` != `kconf_cond_fail`.

this is particularly useful as C does not have exceptions, and thus the only viable error-handling mechanisms are early-exit and early-return; any `cond` value can be propagated up the function stack without losing its unique meaning.

the value of each condition code is determined at compile time.
 
# authors

 * lexi `velartrill` hale <lexi@hale.su>
 * lachs0r
 * glowpelt

# caveats

Modified mod/kcore/magic.h from [a7232aabca] to [2cde04fb07].

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
	kbad_osfile = 72,
		/* # fbsd EX_OSFILE
		 * a system file is fucked up or gone */
	kbad_creat = 73,
		/* # fbsd EX_CANTCREAT */
	kbad_io = 74,
		/* # fbsd EX_IOERR */
	kbad_try = 75,
		/* # fbsd EX_TEMPFAIL
		 * something went wrong this time. try again
		 * some other time */
	kbad_proto = 76,
		/* # fbsd EX_PROTOCOL
		 * failure to speak a protocol correctly */
	kbad_perm = 77,







|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
	kbad_osfile = 72,
		/* # fbsd EX_OSFILE
		 * a system file is fucked up or gone */
	kbad_creat = 73,
		/* # fbsd EX_CANTCREAT */
	kbad_io = 74,
		/* # fbsd EX_IOERR */
	kbad_retry = 75,
		/* # fbsd EX_TEMPFAIL
		 * something went wrong this time. try again
		 * some other time */
	kbad_proto = 76,
		/* # fbsd EX_PROTOCOL
		 * failure to speak a protocol correctly */
	kbad_perm = 77,

Modified mod/kcore/testbin.exe.c from [ba38860055] to [aec04b5f97].

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
	u8 a;
	s16 b;
	bool c;
};

#define _slit(s) ((ksraw){Kmsz(s),s})

stat_long entry(kenv e) {
	const char msg[] = "hello from libk\n";
	ksraw ptr = { Kmsz(msg), msg };

	bool maybe = true;
	maybe = no;

	if (kiosend(e.std, ptr, null) == kiocond_ok) {
................................................................................
	void* top = kmlini();
	kmres rst = kmlina(1230);
	if(rst.cond != kmcond_ok) return kbad_mem;

	kmres rst2 = kmlina(789);
	if(rst2.cond != kmcond_ok) return kbad_mem;

	const char varmsg[] = "printing environment variables\n";
	ksraw msgptr = { Kmsz(varmsg), varmsg };
	kiosend(e.std, msgptr, null);

	for (sz i = 0; i < e.varc; ++i) {
		kiosend(e.std, _slit(" - "), null);
		kiosend(e.std, e.vars[i].name, null);
		kiosend(e.std, _slit(" = ["), null);
		kiosend(e.std, e.vars[i].val, null);
		kiosend(e.std, _slit("]\n"), null);
	}

	return kbad_ok;
}







|







 







|
<
<











7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
35
36
37
38
39
40
41
42


43
44
45
46
47
48
49
50
51
52
53
	u8 a;
	s16 b;
	bool c;
};

#define _slit(s) ((ksraw){Kmsz(s),s})

kbad entry(kenv e) {
	const char msg[] = "hello from libk\n";
	ksraw ptr = { Kmsz(msg), msg };

	bool maybe = true;
	maybe = no;

	if (kiosend(e.std, ptr, null) == kiocond_ok) {
................................................................................
	void* top = kmlini();
	kmres rst = kmlina(1230);
	if(rst.cond != kmcond_ok) return kbad_mem;

	kmres rst2 = kmlina(789);
	if(rst2.cond != kmcond_ok) return kbad_mem;

	kiosend(e.std,  _slit("printing environment variables\n"), null);



	for (sz i = 0; i < e.varc; ++i) {
		kiosend(e.std, _slit(" - "), null);
		kiosend(e.std, e.vars[i].name, null);
		kiosend(e.std, _slit(" = ["), null);
		kiosend(e.std, e.vars[i].val, null);
		kiosend(e.std, _slit("]\n"), null);
	}

	return kbad_ok;
}