0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-26 07:23:53 +01:00

doc: Move conventions to STYLE; ircd: Add additional README related.

This commit is contained in:
Jason Volk 2018-01-04 13:34:32 -08:00
parent 1898cb307d
commit 8ccdd8568f
4 changed files with 111 additions and 72 deletions

View file

@ -280,3 +280,53 @@ other words, if you have a prototype like `foo(const std::string &message)` you
`message` because std::string is common and *what* the string is for is otherwise opaque.
OTOH, if you have `foo(const options &options, const std::string &message)` one should skip
the name for `options &` as it just adds redundant text to the prototype.
### Conventions
These are things you should know when mulling over the code as a whole.
Importantly, knowing these things will help you avoid various gotchas and not
waste your time debugging little surprises. You may or may not agree with some
of these choices (specifically the lack of choices in many cases) but that's
why they're explicitly discussed here.
#### Null termination
- We don't rely on null terminated strings. We always carry around two points
of data to indicate such vectoring. Ideally this is a pair of pointers
indicating the `begin`/`end` like an STL iterator range. `string_view` et al
and the `buffer::` suite work this way.
- Null terminated strings can still be used and we even still create them in
many places on purpose just because we can.
- Null terminated creations use the BSD `strl*` style and *not* the `strn*`
style. Take note of this. When out of buffer space, such an `strl*` style
will *always* add a null to the end of the buffer. Since we almost always
have vectoring data and don't really need this null, a character of the string
may be lost. This can happen when creating a buffer tight to the length of an
expected string without a `+ 1`. This is actually the foundation of a case
to move *back* to `strn*` style but it's not prudent at this time.
- Anything named `print*` like `print(mutable_buffer, T)` always composes null
terminated output into the buffer. These functions usually return a size_t
which count characters printed *not including null*. They may return a
`string_view`/`const_buffer` of that size (never viewing the null).
#### assert() volatility
- Consider any code inside a runtime `assert()` statement to **entirely**
disappear in optimized builds. Some implementations of `assert()` may only
elide the boolean check and thus preserve the inner statement and the effects
of its execution. We do not rely on this. Do not use `assert()` to check
return values of statements that need to be executed in optimized builds.
- Furthermore, consider the **assert statement itself to be physically erased**
from the code during optimized builds. Thus the following is a big mistake:
```
if(foo)
assert(!bar);
if(baz)
bam();
```

View file

@ -1,8 +1,15 @@
# IRCd Library API
### Getting Around
#### Project Namespaces
##### libircd headers are organized into several aggregate "stacks"
* `IRCD_` Preprocessor #define and macro namespace.
* `RB_` Preprocessor #define and macro namespace (legacy / low-level).
* `ircd_` C namespace and demangled bindings.
* `ircd::` C++ namespace scope.
#### What To Include
##### libircd headers are organized into several aggregates groups
As a C++ project there are a lot of header files. Header files depend on other
header files. We don't expect the developer of a compilation unit to figure out
@ -10,75 +17,32 @@ an exact list of header files necessary to include for that unit. Instead we
have aggregated groups of header files which are then precompiled. These
aggregations are mostly oriented around a specific project dependency.
- Standard Include stack <ircd/ircd.h> is the main header group. This stack
> Note: The term 'stack' may be found in place of the preferred term 'group'
in other documentation.
- Standard Include group `<ircd/ircd.h>` is the main header group. This group
involves the standard library and most of libircd. This is what an embedder
will be working with. These headers will expose our own interfaces wrapping
3rd party dependencies which are not included there. Note that the actual file
to properly include this stack is <ircd/ircd.h> (not stdinc.h).
3rd party dependencies which are not included there.
- Boost ASIO include stack <ircd/asio.h> is a header group exposing the
There are actually two files in play here: `<ircd/stdinc.h>` and `<ircd/ircd.h>`.
We have to offer two different pre-compilations: one with `-fPIC`
and one without. Therefor the contents are in `<ircd/stdinc.h>` and the
preprocessor determination for which is in `<ircd/ircd.h>`.
- Boost ASIO include group `<ircd/asio.h>` is a header group exposing the
boost::asio library. We only involve this header in compilation units working
directly with asio for networking et al. Involving this header file slows down
compilation compared with the standard stack.
compilation compared with the standard group.
- Boost Spirit include stack <ircd/spirit.h> is a header group exposing the
- Boost Spirit include group `<ircd/spirit.h>` is a header group exposing the
spirit parser framework to compilation units which involve formal grammars.
Involving this header is a *monumental* slowdown when compiling.
- JavaScript include stack <ircd/js/js.h> is a header group exposing symbols
- JavaScript include group `<ircd/js/js.h>` is a header group exposing symbols
from the SpiderMonkey JS engine. Alternatively, <ircd/js.h> is part of the
standard include stack which includes any wrapping to hide SpiderMonkey.
standard include group which includes any wrapping to hide SpiderMonkey.
- MAPI include stack <ircd/mapi.h> is the standard header group for modules.
This stack is an extension to the standard include stack but has specific
- MAPI include group `<ircd/mapi.h>` is the standard header group for modules.
This group is an extension to the standard include group but has specific
tools for pluggable modules which are not part of the libircd core.
### Conventions
These are things you should know when mulling over the code as a whole.
Importantly, knowing these things will help you avoid various gotchas and not
waste your time debugging little surprises. You may or may not agree with some
of these choices (specifically the lack of choices in many cases) but that's
why they're explicitly discussed here.
#### Null termination
- We don't rely on null terminated strings. We always carry around two points
of data to indicate such vectoring. Ideally this is a pair of pointers
indicating the `begin`/`end` like an STL iterator range. `string_view` et al
and the `buffer::` suite work this way.
- Null terminated strings can still be used and we even still create them in
many places on purpose just because we can.
- Null terminated creations use the BSD `strl*` style and *not* the `strn*`
style. Take note of this. When out of buffer space, such an `strl*` style
will *always* add a null to the end of the buffer. Since we almost always
have vectoring data and don't really need this null, a character of the string
may be lost. This can happen when creating a buffer tight to the length of an
expected string without a `+ 1`. This is actually the foundation of a case
to move *back* to `strn*` style but it's not prudent at this time.
- Anything named `print*` like `print(mutable_buffer, T)` always composes null
terminated output into the buffer. These functions usually return a size_t
which count characters printed *not including null*. They may return a
`string_view`/`const_buffer` of that size (never viewing the null).
#### assert() volatility
- Consider any code inside a runtime `assert()` statement to **entirely**
disappear in optimized builds. Some implementations of `assert()` may only
elide the boolean check and thus preserve the inner statement and the effects
of its execution. We do not rely on this. Do not use `assert()` to check
return values of statements that need to be executed in optimized builds.
- Furthermore, consider the **assert statement itself to be physically erased**
from the code during optimized builds. Thus the following is a big mistake:
```
if(foo)
assert(!bar);
if(baz)
bam();
```

View file

@ -20,16 +20,6 @@
*
*/
///////////////////////////////////////////////////////////////////////////////
//
// IRCd's namespacing is as follows:
//
// IRCD_ #define and macro namespace
// RB_ #define and macro namespace (legacy, low-level)
// ircd_ C namespace and demangled bindings
// ircd:: C++ namespace
//
///////////////////////////////////////////////////////////////////////////////
//
// Standard includes

View file

@ -1,3 +1,38 @@
# IRCd Library Definitions
This directory contains definitions and linkage for `libircd`
### Overview
`libircd` is designed specifically as a shared object library. The purpose of its
shared'ness is to facilitate IRCd's modular design: IRCd ships with many other
shared objects which introduce the "business logic" and features of the daemon. If
`libircd` was not a shared object, every single module would have to include large
amounts of duplicate code drawn from the static library. This would be a huge drag
on both compilation and the runtime performance.
```
(module) (module)
| |
| |
V V
|-------------|
---------------------- | | < ---- (module)
| | | |
| User's executable | <---- | libircd |
| | | |
---------------------- | | < ---- (module)
|-------------|
^ ^
| |
| |
(module) (module)
```
The user (which we may also refer to as the "embedder" elsewhere in
documentation) only deals directly with `libircd` and not the modules.
`libircd` is generally loaded with its symbols bound globally in the executable
and on most platforms cannot be unloaded (or even loaded) manually and has not
been tested to do so. As an aside, we do not summarily dismiss the idea of
reload capability and would like to see it made possible.