diff --git a/docs/libraries.md b/docs/libraries.md index 965511b..31d71f6 100644 --- a/docs/libraries.md +++ b/docs/libraries.md @@ -272,6 +272,10 @@ Its pacman name is mingw-w64-dht, but it has repos all over the plac under its o It is async, driven by being called on a timer, and called when data arrives. It contains a simple example program, that enables you to publish any data you like. +## nghttp3 and lsquic + +http3 libraries built on udp. Designed for async use -- build your own event loop. +They supply two priority queues for the event loop. ## libp2p diff --git a/docs/libraries/cpp_automatic_memory_management.md b/docs/libraries/cpp_automatic_memory_management.md index c96f32a..3aad8e2 100644 --- a/docs/libraries/cpp_automatic_memory_management.md +++ b/docs/libraries/cpp_automatic_memory_management.md @@ -418,14 +418,40 @@ except default and deleted constructors A lambda is a nameless value of a nameless class that is a functor, which is to say, has `operator()` defined. +That each lambda is a unique and nameless class allows the compiler +to optimise it away, so that it becomes inline code. + +So doing the kind of stuff with a lambda you would ordinarily need +a class name to do is likely to break stuff or lead to weirdness. + +It looks like you are defining a real class, but your compiler does +not want to generate a real class except as a last resort. + But, of course you can get the class with `decltype` and assign that nameless value to an `auto` variable, -or stash it on the heap with `new`, -or in preallocated memory with placement `new` +and all that, but because the design for lambdas was +to allow them to be efficiently converted to straightforward +code this may well result in strange complications and mysterious +syntax and semantic errors. + +To tell the compiler to actually use the lambda as a lambda, and +not do all this behind the scenes cleverness, you use `std::function` +which is a regular templated class that defines `operator ()`, and if +it is defined in terms of lambda, stashes that lambda on the heap +after the style of `std::string` + +However for very small lambdas (not capturing any variables, or capturing +on one variably by value, this can get optimised away), and under +the hood it does things C style. + +However, depending on the compiler, `std::function` may do hidden heap +allocation. But if you are doing all that, might as well explicitly define a named functor class. +## lambda on the heap + To construct a lambda in the heap: ```c++ @@ -459,12 +485,54 @@ This way, lambdaPtr is a pointer to a std::function that can store any callable similarly placement `new`, and `unique_ptr`. +Trouble is that an std::function object is a fixed sized object, like an `std::string`, typically sixteen bytes. which like an `std::string` points to a dynamically allocated object on the heap. + ## callbacks -In C, a callback is implemented as an ordinary function pointer, and a pointer to void, which is then cast to a data -structure of the desired type. +In C, a callback is implemented as an ordinary function pointer, and a pointer to void, +which is then cast to a data structure of the desired type. -What the heavy C++ machinery of `std::function` does is bind the two together. +What the heavy C++ machinery of `std::function` does is bind the two together and then +do memory management after the fashion of `std::string`. + +[compiler explorer]:https://godbolt.org/ {target="_blank"} + +And `std::function`, used correctly, should compile to the identical code +merely wrapping the function pointer and the void pointer in a single struct +-- but you had better use [compiler explorer] to make sure +that you are using it correctly. + +Write a callback in C, an an std::function in c++, and make sure that +the compiler generates what it should. + +Ownership is going to be complicated -- since after createing and passing a callback, we +probably do not want ownership any more -- the thread is going to return +and be applied to some entirely different task. So the call that is passed the callback +as an argument by reference uses `move` to ensure that when the `std::function` +stack value in its caller pointing to the heap gets destroyed, it does not +free the value on the heap, and then stashes the moved `std::function` in some +safe place. + +Another issue is that rust, python, and all the rest cannot talk to C++, they can only +talk C. On the other hand, the compiler will probably optimise the `std::function` that +consists of a lamda that is a call to function pointer and that captures a pointer to void. + +Again, since compiler is designed for arcane optimization issues, have to see what happens +in [compiler explorer]. + +But rather than guessing about the compiler correctly guessing intent, make the +callback a C union type implementing std variant in C, being a union of `std:monostate` +a C callback taking no arguments, a C++ callback taking no arguments, C and C++ callbacks +taking a void pointer argument, a c++ callback that is a pointer to method, and +a C++ callback that is an `std::function` + +In the old win32 apis, which were designed for C, and then retrofitted for C++ +they would have a static member function that took an LPARAM, which was a pointer +to void pointing at the actual object, and then the static member function +would directly call the appropriate, usually virtual, actual member function. + +Member function pointers have syntax that no one can wrap their brains around +so people wrap them in layers of typedefs. Sometimes you want to have indefinitely many data structures, which are dynamically allocated and then discarded. @@ -477,6 +545,57 @@ In one case, you would allocate the object every time, and when does with it, di In the other case it would be a member variable of struct that hangs around and is continually re-used. +### C compatibility + +Bind the two together in a way that C can understand: + +The code that calls the callback knows nothing about how the blob is structured. +The event management code knows nothing about how the blob is structured. +But the function pointer in the blob *does* know how the blob is structured. + +```C +// p points to or into a blob of data containing a pointer to the callback +// and the data that the callback needs is in a position relative to the pointer +// that is known to the callback function. +enum event_type { monovalue, reply, timeout, unreachable, unexpected_error }; + + struct callback; + typedef extern "C" void (*callback_)(callback * pp, event_type evtype, void* event); + struct callback + { + callback_ p; + }; + + callback * pp; + +// Within the actual function in the event handling code, +// one has to cast `callback* pp` from its base type to its actual type +// that has the rest of the data, which the event despatcher code knows nothing of. +// The event despatch code should not include the headers of the event handling code, +// as this would make possible breach of separation of responsibilities. +try{ +(*((*pp).p))(pp, evtype, event ); +} +catch(...){ + // log error and release event handler. + // if an exception propagates from the event handling code into the event despatch code + // it is programming error, a violation of separation of responsibilities + // and the event despatch code cannot do anything with the error. +} +``` + +`pp` points into a blob that containst the data needed for handling the event when will happen, +and a pointer to the code that will handle it when it happens, ptrEvent is a ptr to a +struct containing an index that will tell us what kind of struct it is. It is a C union +of very different structs. Since it is dynamicaly allocated, we don't waste space, we create +the particular struct, cast it to the union, and then cast the union to the struct it actually is. + +But, that C code will be quite happy if given a class whose first field is a pointer to a C calling +convention static member function that calls the next field, the +next field being a lambda whose unnameable type is known to the templated object when +it was defined, or if it is given a class whose first field is a pointer to a C calling convention +static function that does any esoteric C++, or Rust, or Lua thing. + # auto and decltype(variable) In good c++, a tremendous amount of code behavior is specified by type @@ -490,7 +609,9 @@ decltype for a type, use it. In good event oriented code, events are not triggered procedurally, but by type information or data structures, and they are not handled -procedurally, as by defining a lambda, but by defining a derived type. +procedurally, as by defining a lambda, but by defining a derived type +in the sense that you use the virtual method table as the despatch table +for handling different events. # Variable length Data Structures diff --git a/docs/setup/contributor_code_of_conduct.md b/docs/setup/contributor_code_of_conduct.md index 95abca2..c7016a2 100644 --- a/docs/setup/contributor_code_of_conduct.md +++ b/docs/setup/contributor_code_of_conduct.md @@ -51,7 +51,8 @@ A participant who can be targeted is likely to introduce unobvious security flaws into the software architecture. All contributors should make some effort to protect themselves against a third party subsequently coercing them to use the reputation that they have obtained by contributing to make -subsequent harmful contributions. +subsequent harmful contributions. For example when Telegram founders visited the +US, they caught heat. All contributors will use a unique name and avatar for the purpose of contributing to this project, and shall not link it to other names of theirs