Skip to content

Commit

Permalink
docs: adding more content
Browse files Browse the repository at this point in the history
  • Loading branch information
iboB committed Apr 13, 2023
1 parent 460d76d commit d5e1006
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 10 deletions.
4 changes: 2 additions & 2 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

* [Overview](overview.md)
* Basics
* Adding DynaMix to a Project
* [Adding DynaMix to a Project](basics/adding.md)
* [Elements and Glossary](basics/glossary.md)
* C++ Basics
* C Basics
* Advanced Topics
* Custom Allocators
* Creating Plugins
* Mutation Rules
* Custom Features
* Creating Custom Features
* [Dangerous Functionalities](advanced/danger.md)
* Working with DynaMix
* [Performance](working-with/perf.md)
Expand Down
35 changes: 35 additions & 0 deletions doc/basics/adding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Adding DynaMix to a Project

To build DynaMix you need a C++17 capable C++ compiler and a C11 capable C compiler. Respectively C++17 is required to use the C++ interface and C11 is required to use the C interface. MSVC 2022, gcc 9+, and clang 10+ are regularly tested and known to work.

Currently the only supported way to add DynaMix to a project is as a [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) package. Other ways are listed below, though they are not tested.

## As a CPM.cmake package

The recommended and the easiest way to add the library would be as a [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) package. If you are using this package manager (and you should be), you only need to add the package `iboB/dynamix` like `CPMAddPackage(gh:iboB/[email protected])`

## As a submodule/subrepo

DynaMix uses [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) to handle its own dependencies, but also bundles CPM.cmake itself. If you add dynamix as submodule of your repo and simply `add_subdirectory` it in CMake, everything will very likely work as intended. The CMake configuration of DynaMix will fetch the needed dependencies and everything will be configured as intended.

The danger here would be if the project already uses some of these dependencies. In such a case there will be a Clash of Targets™ and things will very likely won't build (or worse yet, it will build but will contain strange bugs due to ODR violations and ABI differences)

The dependencies are:

* [iboB/splat](https://github.com/iboB/splat) `~> 1.3.1`
* [iboB/itlib](https://github.com/iboB/itlib) `~> 1.9.0`
* CMake only: [iboB/icm](https://github.com/iboB/icm) `~> 1.4.4`
* Tests only: [iboB/doctest-util](https://github.com/iboB/doctest-util) `~> 0.2.0`
* Tests only: [iboB/doctest-lib](https://github.com/iboB/doctest-lib) `~> 2.4.9a`
* C Tests only: [ThrowTheSwitch/Unity](https://github.com/iboB/doctest-util) `~> 2.5.2`
* Benchmarks only: [iboB/picobench](https://github.com/iboB/picobench) `~> 2.04`

## More

As a whole the build of DynaMix is pretty straight forward. The core dependencies are header-only. So if you fetch them and build all .c and .cpp files in `/code` into a library, everything should be OK.

Creating build scripts for the library only with any build system should be easy.

The tests and benchmarks involve a bit more CMake scripting, but their build is mostly straight-forward as well.

The v1compat library generates code through a ruby script and thus its build is a bit more involved, but new users should not be using v1compat anyway.
72 changes: 67 additions & 5 deletions doc/basics/glossary.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,87 @@
# Glossary

## Mixin
## Object

A mixin is a building block of objects.
An object in terms of DynaMix is an instance of the class `dynamix::object`. By itself it's not much more than an empty class. Its main purpose is to be a "container" of mixin instances. You can construct an empty object and then add or remove mixins from it via *mutations*.

## Domain
The particular set of mixins in an object defines its *type*. An object mutation (adding or removing mixins) changes the objects type.

## Object
"Type", as mentioned above, has nothing to do with the concept of type in C++. A `dynamix::object` naturally always is a `dynamix:object`. "Type" in this case is a runtime concept, which contains information for the composition of an object and its interface or list of *abstract features*.

An object *has* mixins and *implements* features.

## Object Type

The type is basically a list of mixin infos, which is used to create objects. It also has indexes to those infos and a materialized index of *features* called a `ftable`. Thus looking up concrete mixins and features in a type (or object) is always O(1).

The object type is ordered. A type composed of mixins `{a, b, c}` is different from a type composed of `{b, a, c}`.

The type provides utitliy functionalities to make these lookups and other queries.

Like the object, the type *has* mixins and *implements* features.

## Mixin

A mixin is the building block of types and objects. It not a specific type, but a definition. It the jobs of the library's users to define their own mixins.

Once you have mixins, you can combine them into objects. Adding or removing mixins will internally allocate and instantiate them, and destroy them. This means that a mixin instance is bound to an object instance. Objects cannot share mixin instances and only a single instance of a specific mixin can be part of an object.

You can loosely think of mixins as the parents of a class in a multiple inheritance scenario.

Mixins are defined through [`mixin_info`](../../code/dnmx/mixin_info.h). Utilities exist for common ways to define it. In the vast majority of cases a mixin will be defined as an existing type (`class` or `struct`).

The mixin *provides* feature implementations.

## Mixin Feature

The mixin features are a list of abstract features which are provided by a mixin to a type. Using the multiple inheritance example from above, much like a class inherits methods, static members, and typedefs from multiple parents, types and objects obtain the *abstract mixins features* of the mixins they are composed of.

A feature is a definition. A *feature implementation* is a provided by a mixin. An implementation on its own is nothing but a completely type erased (`void`) pointer.

DynaMix provides some feature types, and others can be created by the users. Defining a feature type would involve creating utility functionalities which reify the feature to what it needed.

### Bid and Priority

### Message
Features have two signed integer properties, called bid and priority which are used to sort them in the `ftable`. If a feature is provided by a single mixin in a type, bid and priority are ignored, but if many mixins provide the same feature, they are used to disambiguate.

Bid is the major sort key. The higher the bid, the earlier the feature will be added in the `ftable`. Features with a higher bid (say 34) *overrides* features with a lower bid (say -5).

Priority is used as an additional sort key for features with the same bid. It is however treated with the oposite value. The smaller the priority, the earlier the feature will be added in the ftable. With a same bid, a feature with a smaller priority (say -1) *precedes* features with a bigger priority (say 10).

When bid *and* priority are the same, the order in which the implementations appear in the `ftable` is the opposite of the mixin order in the type. The last mixin in the type will add the first features in the `ftable`.

### Messages

Messages are a feature type provided by the library for the C++ interface. The term "message" here is used in the same way as it is in Smalltalk or Objective C. The message is what is called, while the method is what is executed.

The message implementation is a function.

A message is a feature which is associated with function. Calling a message for an object, will lead to a function being executed with the mixin which provided it as a first argument or, if the mixin is associted with a C++ type, it can lead to a method of that type being executed.

#### Multicast

Multicast messages make use of bids and priority to execute all top bidders in the ftable for a single message call.

## Mutation

Mutations are what is used to compose types and change object types.

This is basically a way to add, remove, or reorder the mixins in a type.

Type mutations work on types, while object mutations can work on an object and its type at the same time.

### Mutation Rule

Mutation rules are functions which are applied to all mutations in a domain. The most common case would be to associate a mixin with another, but they can do anything for example prevent mixins from being added, or replacing mixins with others.

The library offers some mutation rules, but users can define their own.

## Type Class

A type class is a boolean function for a type. It can be used to categorize objects based on their mixin or feature composition.

## Domain

The domain is a library instance of sorts. The domain contains registries for mixins, features, and types (but not of objects!) and should be used to contain a specific, well... domain of use of DynaMix.

It is not possible to create types or objects using mixins from different domains.
45 changes: 42 additions & 3 deletions doc/overview.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,52 @@
# Overview

DynaMix is a new approach to dynamic polymorphism. It is a library which allows the composition of polymorphic objects at run time. The objects are composed of building blocks called *mixins* which provide *abstract features* to the objects and allow client code to make use of them while remaining oblivious to the concrete composition.
DynaMix is a new approach to OOP and dynamic polymorphism. It is a library which allows the composition of polymorphic objects at run time. The objects are composed of building blocks called *mixins* which provide *abstract features* to the objects and allow client code to make use of them while remaining oblivious to the concrete composition.

The result is similar to [multiple inheritance in C++](https://www.learncpp.com/cpp-tutorial/multiple-inheritance/), but more closely resembles [mixins in Ruby](https://www.tutorialspoint.com/ruby/ruby_modules.htm), [Dart](https://dart.dev/language/mixins) and [Scala](https://docs.scala-lang.org/tour/mixin-class-composition.html), traits in [Self](https://handbook.selflanguage.org/2017.1/glossary.html), [PHP](https://www.php.net/manual/en/language.oop5.traits.php), and [others](https://en.wikipedia.org/wiki/Trait_(computer_programming), the [roles in Perl](https://docs.raku.org/language/objects#Roles) or [inheritance in Eiffel](https://www.eiffel.org/doc/eiffel/I2E-_Inheritance).
The result is similar to [multiple inheritance in C++](https://www.learncpp.com/cpp-tutorial/multiple-inheritance/), but more closely resembles [mixins in Ruby](https://www.tutorialspoint.com/ruby/ruby_modules.htm), [Dart](https://dart.dev/language/mixins) and [Scala](https://docs.scala-lang.org/tour/mixin-class-composition.html), traits in [Self](https://handbook.selflanguage.org/2017.1/glossary.html), [PHP](https://www.php.net/manual/en/language.oop5.traits.php), and [others](https://en.wikipedia.org/wiki/Trait_(computer_programming), the [roles in Perl](https://docs.raku.org/language/objects#Roles) or [inheritance in Eiffel](https://www.eiffel.org/doc/eiffel/I2E-_Inheritance).

It must be noted that the library is a means to create a project's *architecture* rathern than implement its purpose. The library doesn't *do* anything, but introduces idioms and a paradigm by which one can create the interface for what's being done. In a way it can be viewed as a language extention rather than a utility.

It can be compared to [COM](https://en.wikipedia.org/wiki/Component_Object_Model) which is a library that introduces more orhtodox (in the style of Java or C#) type of dynamic polymorphism to C. This documentation has a [list of more comparisons](misc/dynamix-vs-x.md) of DynaMix to existing solutions.
It in this regard it can be compared to [COM](https://en.wikipedia.org/wiki/Component_Object_Model) which is a library that introduces more orhtodox (in the style of Java or C#) type of dynamic polymorphism to C. A [list of more comparisons](misc/dynamix-vs-x.md) of DynaMix to existing solutions is available.

## Library name

DynaMix is a portmanteau which stands for "dynamic mixins".

In C++ circles the term "mixin" has gained some popularity. In this context a mixin is a building block for a type, which interacts via other building blocks via [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). This is a way to accomplish static polymorphism and everything is resolved at compile time.

The purpose of DynaMix is to accomplish something very similar, but at run time, in a dynamic manner, and hence the name is Dynamic Mixins.

## Key library features

* Compose objects from mixins at run time
* Physically separate interface and implementation
* Non-intrusive – mixins don't need to have a common parent or any special code inside
* Mutate "live" objects by changing their composition at run time
* Use `std::polymorphic_allocator` to allow fine-tuning allocations and achieving cache locality in critical parts of the code
* Create shared libraries and plugins which can enrich or modify objects without modifying (or even rebuilding) the executable.
* Add "hotswap" to a project while developing
* Have complete runtime reflection by symbols or strings
* Messages:
* Fast polymorphic calls – comparable to `std::function`
* Allow multicast messages – ones that are handled by many mixins within an object
* Thread safe message calls – as thread safe as the underlying methods.

In classic OOP terminology, DynaMix is a solution with the following features:

* Dynamic type composition
* Runtime polymorphism
* Singular dispatch
* Open methods
* Late binding
* Multicast
* Reachable overrides

## When (and when not) to use DynaMix

The more complex the objects in a piece of software are, the more beneficial it will be to use the library. Pieces of software that typically have very complex objects include games (especially role-playing ones or strategies), CAD systems, enterprise systems, UI libraries, and others.

As a general rule of thumb: if you need complex polymorphic objects, DynaMix is a likely good choice.

We should emphasize on the polymorphism. In many very high-performance systems polymorphism is avoided at the cost of code that is (at least somewhat) harder to write and maintain (this is most often the case with high-end games). Since such systems will try to "squeeze" every possible piece of processing power out of the CPU, cache locality and lack of cache misses are critical in some parts of their code. As is the case with all instances of polymorphism, including C++ virtual methods and `std::function`, uses of DynaMix will almost certainly lead to cache misses. Of course, you may still rely on the library in other parts of your code, like the business (or gameplay) logic. For more information about the library performance, see [Performance](working-with/perf.md).

Of course, small projects with simple objects, even if they are polymorphic, may end up not finding any particular benefits in using the library, since their size makes them fast to compile and easy to maintain as they are. If a piece of software can be created in a couple of days, by one or two programmers, there will hardly be any need for DynaMix.
1 change: 1 addition & 0 deletions doc/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ Here's a list of notable planned new features and updates for DynaMix
* Demo: lua integration
* Demo: JS and wasm integration
* Possibly never, but sounds nice
* Lazy mixin registration
* Create a preprocessing tool (like Qt's moc) that can be used instead of the macros for mixin and feature definition

0 comments on commit d5e1006

Please sign in to comment.