Skip to content

Merge 1.0 documentation into main #1016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 67 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
84f1841
Initial files to test MkDocs with Material
hsutter Feb 11, 2024
3126adb
Update checkout version
hsutter Feb 11, 2024
f6812ac
Trying checkout with no version
hsutter Feb 11, 2024
53a3279
Back to v3
hsutter Feb 11, 2024
a708e37
Try renaming to docs
hsutter Feb 11, 2024
9fcc922
Test push to renamed docs branch
hsutter Feb 11, 2024
4eaa306
Try moving mkdocs.yml to root to unblock CI
hsutter Feb 11, 2024
e3d71c7
Further cleanup to get things in the right directories
hsutter Feb 11, 2024
5bc176e
Finish moving from `/docs/docs` to `/docs`
hsutter Feb 11, 2024
353332c
Add "Cppfront reference," and start writing "Cpp2 reference"
hsutter Feb 12, 2024
3648a58
Merged documentation from blog posts, and unary operators
hsutter Feb 12, 2024
b75d2c0
Integrate links to wiki design notes
hsutter Feb 13, 2024
41d5f3d
Add more sections
hsutter Feb 13, 2024
1f8ac8d
Add `as` and `inspect`
hsutter Feb 13, 2024
2baeb3f
Document declarations, improve `inspect` discussion
hsutter Feb 13, 2024
3b7a9d5
Fix a few typos
hsutter Feb 13, 2024
64deec1
Add Hello-world xref
hsutter Feb 14, 2024
eb085c5
Update mkdocs.yml
hsutter Feb 14, 2024
8f7d265
Improve mixed files examples and flow
hsutter Feb 14, 2024
58e5ec6
Overview editorial improvements
hsutter Feb 14, 2024
335d3a7
Minor cleanup
hsutter Feb 14, 2024
91f193e
docs: fix some typos and confusing wording (#976)
DyXel Feb 14, 2024
d3299fc
Fix table of content display -> rhs of page
hsutter Feb 14, 2024
37b34e3
Add section navigation
hsutter Feb 14, 2024
0786e27
Organize the welcome info into three pages
hsutter Feb 14, 2024
c13d4e6
Disable regression tests on this branch
hsutter Feb 15, 2024
be7cd49
Add more types and `operator=` material
hsutter Feb 15, 2024
bbc25eb
Add keywords, objects, and heap allocation
hsutter Feb 15, 2024
b7c2cd1
Add `main`
hsutter Feb 16, 2024
6796af1
Reviewing cppfront docs (#982)
gregmarr Feb 16, 2024
4ee9a36
Fix "lines 8, 9, and 15" wording
hsutter Feb 16, 2024
1b7017a
Mention UFCS
hsutter Feb 18, 2024
d8d8a5a
Function outputs and explicit discard
hsutter Feb 18, 2024
36475b6
Add definite initialization section
hsutter Feb 19, 2024
9257d97
Update CTAD description in hello-world.md (#984)
bluetarpmedia Feb 19, 2024
6facf81
Embiggen text font, add `move` and `out` arguments
hsutter Feb 19, 2024
8bb227d
Fix small typo in common.md (#985)
bluetarpmedia Feb 19, 2024
c7eccf4
Changes to expressions.md (#986)
bluetarpmedia Feb 19, 2024
eedc605
Another round of docs updates (#983)
gregmarr Feb 19, 2024
4495b65
Add syntax highlighting for inline code blocks using `#!cpp` shebangs
hsutter Feb 19, 2024
9f75d36
Move function calls to expressions
hsutter Feb 19, 2024
ddfa21e
Add capture section
hsutter Feb 19, 2024
84fc3c2
Add Mermaid build diagram
hsutter Feb 19, 2024
430afe0
Add TODO for `member = _;`
hsutter Feb 19, 2024
79252d4
Add interpolation formatting, and other minor cleanup
hsutter Feb 20, 2024
d3b6575
Expand comparisons section
hsutter Feb 20, 2024
f12047a
Update declarations.md (#988)
bluetarpmedia Feb 24, 2024
8d19818
Follow up merge with additional examples, and update highlighted linenos
hsutter Feb 25, 2024
8d49525
Update Capture sections in expressions.md (#987)
bluetarpmedia Feb 25, 2024
f0addd2
More docs reviews (#990)
gregmarr Feb 25, 2024
ecb0031
Add more functions material
hsutter Feb 26, 2024
270fe8f
Changes to functions.md (#998)
bluetarpmedia Feb 27, 2024
b795f31
Fill in some TODO's
hsutter Feb 27, 2024
74fb716
Add requires, namespaces, using, and namespace/type/function/object a…
hsutter Feb 28, 2024
aa9c168
Update aliases.md (#1005)
bluetarpmedia Feb 29, 2024
90f1c83
Add generality notes: Function defaults, and function <-> block/stmt …
hsutter Mar 7, 2024
c62fbbd
Merge previous commit
hsutter Mar 7, 2024
9629009
Merge branch 'docs' of https://github.com/hsutter/cppfront into docs
hsutter Mar 7, 2024
11403c8
Update functions.md (#1011)
bluetarpmedia Mar 8, 2024
5b930e8
Update declarations.md (#1010)
bluetarpmedia Mar 8, 2024
5571100
Complete the metafunctions section
hsutter Mar 9, 2024
1bdab45
Merge branch 'docs' of https://github.com/hsutter/cppfront into docs
hsutter Mar 9, 2024
663b3d0
Update metafunctions.md (#1015)
bluetarpmedia Mar 10, 2024
21d7d43
Add contracts documentation
hsutter Mar 11, 2024
a3986e7
Remove modules documentation stub since that's not supported yet
hsutter Mar 11, 2024
0b40bcc
Fix comment typo
hsutter Mar 11, 2024
162ba1b
Merge branch 'main' into docs
hsutter Mar 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: docs
on:
push:
branches:
- docs
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.x
- uses: actions/cache@v2
with:
key: ${{ github.ref }}
path: .cache
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force
201 changes: 201 additions & 0 deletions docs/cpp2/common.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Common programming concepts

## <a id="main"></a> `main`

As always, `main` is the entry point of the program. For example:

`main` can have either:

- No parameters: &emsp; **`#!cpp main: () /*etc.*/`**

- One parameter of implicit type named `args`: &emsp; **`#!cpp main: (args) /*etc.*/`**

- The type of `args` cannot be explicitly specified. It is always `cpp2::args_t`, which behaves similarly to a `#!cpp const std::array<std::string_view>`.

- Using `args` performs zero heap allocations. Every `string_view` is directly bound to the string storage provided by host environment.

- `args.argc` and `args.argv` additionally provide access to the raw C/C++ `main` parameters.

``` cpp title="main with (args)" hl_lines="5 9"
// Print out command line arguments, then invoke
// a Qt event loop for a non-UI Qt application
main: (args) -> int
= {
for args do (arg) {
std::cout << arg << "\n";
}

app: QCoreApplication = (args.argc, args.argv);
return app.exec();
}
```

`main` can return:

- `#!cpp void`, the default return value for functions. No `#!cpp return` statement is allowed in the body. In this case, the compiled Cpp1 code behaves as if `main` returned `#!cpp int`.

- `#!cpp int`. If the body has no `#!cpp return` statement, the default is to `#!cpp return 0;` at the end of the function body.

- Some other type that your Cpp1 compiler(s) supports as a nonstandard extension.


## <a id="comments"></a> Comments

The usual `#!cpp // line comments` and `#!cpp /* stream comments */` are supported. For example:

``` cpp title="Writing comments"
// A line comment: After //, the entire
// rest of the line is part of the comment

/*
A stream comment: After /*, everything until the
next * / (without a space between) is part of the
comment. Note that stream comments do not nest.
*/
```


## <a id="keywords"></a> Reserved keywords

Cpp2 has very few globally reserved keywords; nearly all keywords are contextual, where they have their special meaning when they appear in a particular place in the grammar. For example:

- `new` is used as an ordinary function to do allocation (e.g., `shared.new<widget>(1, 2, 3)`).

- `struct` and `enum` are used as function names in the metafunctions library.

- `type` can be used as an ordinary name (e.g., `std::common_type<T1,T2>::type`).

In rare cases, usually when consuming code written in other languages, you may need to write a name that is a reserved keyword. The way to do that is to prefix it with `__identifer__`, which treats it as an ordinary identifier (without the prefix).


## <a id="fundamental-types"></a> Fundamental data types

Cpp2 supports the same fundamental types as today's Cpp1, but additionally provides the following aliases in namespace `cpp2`:

| Fixed-width types | Synonym for |
|---|---|
| `i8` | `std::int8_t` |
| `i16` | `std::int16_t` |
| `i32` | `std::int32_t` |
| `i64` | `std::int64_t` |
| `u8` | `std::uint8_t` |
| `u16` | `std::uint16_t` |
| `u32` | `std::uint32_t` |
| `u64` | `std::uint64_t` |

| Variable-width types <br> (Cpp2-compatible single-word names) | Synonym for (these multi-word<br> names are not allowed in Cpp2) |
|---|---|
| `ushort` | `#!cpp unsigned short` |
| `uint` | `#!cpp unsigned int` |
| `ulong` | `#!cpp unsigned long` |
| `longlong` | `#!cpp long long` |
| `ulonglong` | `#!cpp unsigned long long` |
| `longdouble` | `#!cpp long double` |

| For compatibility/interop only,<br> so deliberately ugly names | Synonym for (these multi-word<br> names are not allowed in Cpp2) | Notes |
|---|---|---|
| `_schar` | `#!cpp signed char` | Normally, prefer `i8` instead |
| `_uchar` | `#!cpp unsigned char` | Normally, prefer `u8` instead |

## <a id="type-qualifiers"></a> Type qualifiers

Types can be qualified with `#!cpp const` and `#!cpp *`. Types are written left-to-right, so a qualifier always applies to what immediately follows it. For example, to declare a `#!cpp const` pointer to a non-`#!cpp const` pointer to a `#!cpp const i32` object, write:

``` cpp title="Using type qualifiers"
// A const pointer to a non-const pointer to a const i32 object
p: const * * const i32;
```

## <a id="literals"></a> Literals

Cpp2 supports the same `#!cpp 'c'`haracter, `#!cpp "string"`, binary, integer, and floating point literals as Cpp1, including most Unicode encoding prefixes and raw string literals.

Cpp2 supports using Cpp1 user-defined literals for compatibility, to support seamlessly using existing libraries. However, because Cpp2 has unified function call syntax (UFCS), the preferred way to author the equivalent in Cpp2 is to just write a function or type name as a `.` call suffix. For example:

- You can create a `u8` value by writing either `u8(123)` or **`123.u8()`**. [^u8using]

- You can write a 'constexpr' function like `#!cpp nm: (value: i64) -> my_nanometer_type == { /*...*/ }` that takes an integer and returns a value of a strongly typed "nanometer" type, and then create a `nm` value by writing either `nm(123)` or **`123.nm()`**.

Both **`123.nm()`** and **`123.u8()`** are very similar to user-defined literal syntax, and more general.

## <a id="operators"></a> Operators

Operators have the same precedence and associativity as in Cpp1, but some unary operators that are prefix (always or sometimes) in Cpp1 are postfix (always) in Cpp2.

### <a id="unary-operators"></a> Unary operators

The operators `!`, `+`, and `-` are prefix, as in Cpp1. For example:

``` cpp title="Using prefix operators"
if !vec.empty() {
vec.emplace_back( -123.45 );
}
```

| Unary operator | Cpp2 example | Cpp1 equivalent |
|---|---|---|
| `!` | `!vec.empty()` | `!vec.empty()` |
| `+` | `#!cpp +100` | `#!cpp +100` |
| `-` | `#!cpp -100` | `#!cpp -100` |

The operators `.`, `*`, `&`, `~`, `++`, `--`, `()`, `[]`, and `$` are postfix. For example:

``` cpp title="Using postfix operators"
// Cpp1 examples, from cppfront's own source code:
// address = &(*tokens)[pos + num];
// is_void = *(*u)->identifier == "void";
// Cpp2 equivalents:
address = tokens*[pos + num]&;
is_void = u**.identifier* == "void";
```

Postfix notation lets the code read fluidly left-to-right, in the same order in which the operators will be applied, and lets declaration syntax be consistent with usage syntax. For more details, see [Design note: Postfix operators](https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators).

> Note: The function call syntax `f(x)` calls a namespace-scope function, or a function object, named `f`. The function call syntax `x.f()` is a unified function call syntax (aka UFCS) that calls a type-scope function in the type of `x` if available, otherwise calls the same as `f(x)`. For details, see [Design note: UFCS](https://github.com/hsutter/cppfront/wiki/Design-note%3A-UFCS).

| Unary operator | Cpp2 example | Cpp1 equivalent |
|---|---|---|
| `#!cpp .` | `#!cpp obj.f()` | `#!cpp obj.f()` |
| `#!cpp *` | `#!cpp pobj*.f()` | `#!cpp (*pobj).f()` or `#!cpp pobj->f()` |
| `#!cpp &` | `#!cpp obj&` | `#!cpp &obj` |
| `#!cpp ~` | `#!cpp val~` | `#!cpp ~val` |
| `#!cpp ++` | `#!cpp iter++` | `#!cpp ++iter` |
| `#!cpp --` | `#!cpp iter--` | `#!cpp --iter` |
| `(` `)` | `#!cpp f( 1, 2, 3)` | `#!cpp f( 1, 2, 3)` |
| `[` `]` | `#!cpp vec[123]` | `#!cpp vec[123]` |
| `$` | `val$` | _reflection — no Cpp1 equivalent yet_ |

> Because `++` and `--` always have in-place update semantics, we never need to remember "use prefix `++`/`--` unless you need a copy of the old value." If you do need a copy of the old value, just take the copy before calling `++`/`--`.

Unary suffix operators must not be preceded by whitespace. When `*`, `&`, and `~` are used as binary operators they must be preceded by whitespace. For example:

| Unary postfix operators that<br>are also binary operators | Cpp2 example | Cpp1 equivalent |
|---|---|---|
| `#!cpp *` | `#!cpp pobj* * 42` | `#!cpp (*pobj)*42` |
| `#!cpp &` | `#!cpp obj& & mask` <p> (note: allowed in unsafe code only) | `#!cpp &obj & mask` |
| `#!cpp ~` | `#!cpp ~val ~ bitcomplement` | `#!cpp val~ ~ bitcomplement` |

For more details, see [Design note: Postfix unary operators vs binary operators](https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-unary-operators-vs-binary-operators).


### <a id="binary-operators"></a> Binary operators

Binary operators are the same as in Cpp1. From highest to lowest precedence:

| Binary operators grouped by precedence |
|---|
| `*`, `/`, `%` |
| `+`, `-` |
| `<<`, `>>` |
| `<=>` |
| `<`, `>`, `<=`, `>=` |
| `==`, `!=` |
| `&` |
| `^` |
| `|` |
| `&&` |
| `||` |
| `=` and compound assignment |


[^u8using]: Or `123.cpp2::u8()` if you aren't `using` the namespace or that specific name.
125 changes: 125 additions & 0 deletions docs/cpp2/contracts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@

# Contracts

## Overview

Cpp2 currently supports three kinds of contracts:

- **Preconditions and postconditions.** A function declaration can include `pre(condition)` and `post(condition)` before the `= /* function body */`. Before entering the function body, preconditions are fully evaluated and postconditions are captured (and performs their captures, if any). Immediately before exiting the function body via a normal return, postconditions are evaluated. If the function exits via an exception, postconditions are not evaluated.

- **Assertions.** Inside a function body, writing `assert(condition)` assertion statements. Assertions are evaluated when control flow passes through them.

Notes:

- `condition` is an expression that evaluates to `#!cpp true` or `#!cpp false`.

- Optionally, `condition` may be followed by `, "message"`, a message to include if a violation occurs. For example, `pre(condition, "message")`.

- Optionally, a `<Group>` can be written inside `<` `>` angle brackets immediately before the `(`, to designate that this test is part of the [contract group](#contract-groups) named `Group`. If a violation occurs, `Group.report_violation()` will be called. For example, `pre<Group>(condition)`.

For example:

``` cpp title="Precondition and postcondition examples" hl_lines="2 3"
insert_at: (container, where: int, val: int)
pre<Bounds>( 0 <= where <= vec.ssize(), "position (where)$ is outside 'val'" )
post ( container.ssize() == container.ssize()$ + 1 )
= {
_ = container.insert( container.begin()+where, val );
}
```

In this example:

- The `$` captures are performed before entering the function.

- The precondition is part of the `Bounds` safety contract group and is checked before entering the function. If the check fails, say because `where` is `#!cpp -1`, then `#!cpp cpp2::Bounds.report_violation("position -1 is outside 'val'")` is called.

- The postcondition is part of the `Default` safety contract group. If the check fails, then `#!cpp cpp2::Default.report_violation()` is called.


## <a id="contract-groups"></a> Contract groups

Contract groups are useful to enable or disable or [set custom handlers](#violation-handlers) independently for different groups of contracts. A contract group `G` is just the name of an object that can be called with `G.report_violation()` and `G.report_violation(message)`, where `message` is a `* const char` C-style text string.

You can create new contract groups just by creating new objects that have a `.report_violation` function. The object's name is the contract group's name. The object can be at any scope: local, global, or heap.

For example, here are some ways to use contract groups, for convenience using [`cpp2::contract_group`](#violation_handlers) which is a convenient group type:

``` cpp title="Using contract groups" hl_lines="1 4 6 10-12"
GroupA: cpp2::contract_group = (); // a global group

func: () = {
GroupB: cpp2::contract_group = (); // a local group

GroupC := new<cpp2::contract_group>(); // a dynamically allocated group

// ...

assert<GroupA >( some && condition );
assert<GroupB >( another && condition );
assert<GroupC*>( another && condition );
}
```

You can make all the objects in a class hierarchy into a contract group by having a `.report_violation` function in a base class, and then writing contracts in that hierarchy using `<this>` as desired. This technique used in cppfront's own reflection API:

``` cpp title="Example of using 'this' as a contract group, from cppfront 'reflect.h2'" hl_lines="8 9"
function_declaration: @copyable type =
{
// inherits from a base class that provides '.report_violation'

// ...

add_initializer: (inout this, source: std::string_view)
pre<this> (!has_initializer(), "cannot add an initializer to a function that already has one")
pre<this> (parent_is_type(), "cannot add an initializer to a function that isn't in a type scope")
= { /*...*/ }

// ...

}
```


## <a id="violation-handlers"></a> `cpp2::contract_group`, and customizable violation handling

The contract group object could also provide additional functionality. For example, Cpp2 comes with the `cpp2::contract_group` type which allows installing a customizable handler for each object. Each object can only have one handler at a time, but the handler can change during the course of the program. `contract_group` supports:

- `.set_handler(pfunc)` accepts a pointer to a handler function with signature `#!cpp * (* const char)`.

- `.get_handler()` returns the current handler function pointer, or null if none is installed.

- `.has_handler()` returns whether there is a current handler installed.

- `.enforce(condition, message)` evaluates `condition`, and if it is `false` then calls `.report_violation(message)`.

Cpp2 comes with five predefined `contract group` global objects in namespace `cpp2`:

- `Default`, which is used as the default contract group for contracts that don't specify a group.

- `Type` for type safety checks.

- `Bounds` for bounds safety checks.

- `Null` for null safety checks.

- `Testing` for general test checks.

For these groups, the default handler is `cpp2::report_and_terminate`, which prints information about the violation to `std::cerr` and then calls `std::terminate()`. But you can customize it to do anything you want, including to integrate with any third-party or in-house error reporting system your project is already using. For example:

``` cpp title="Example of customized contract violation handler" hl_lines="2 8-9"
main: () -> int = {
cpp2::Default.set_handler(call_my_framework&);
assert<Default>(false, "this is a test, this is only a test");
std::cout << "done\n";
}

call_my_framework: (msg: * const char) = {
// You can do anything you like here, including arbitrary work
// and integration with your current error reporting libraries
std::cout
<< "sending error to my framework... ["
<< msg << "]\n";
exit(0);
}
```
Loading
Loading