An open API service indexing awesome lists of open source software.

https://github.com/thecodesojourner/cljonic

Header-Only Embedded-Systems C++ Clojure-esque Functional Programming Library
https://github.com/thecodesojourner/cljonic

clojure cpp cpp20 embedded embedded-systems functional-programming header-only

Last synced: 8 months ago
JSON representation

Header-Only Embedded-Systems C++ Clojure-esque Functional Programming Library

Awesome Lists containing this project

README

          

= cljonic
:doctype: book
:source-highlighter: rouge
:cpp: C++

image::logo.png[cljonic, 200, 200, align="center"]

[.text-center]
*Header-Only Embedded-Systems C++ Clojure-esque Functional Programming Library*

NOTE: If you are interested in a "big-machine" *{cpp} Clojure* implementation see the https://github.com/jank-lang/jank[Jank] project.

IMPORTANT: I am *NOT* a *Modern {cpp}* expert. I'm learning *Modern {cpp}*, especially *Concepts*, using
*github Copilot*. I'm moderately competent in *Clojure*, having used it professionally for several years. If you have
suggestions for improvement, *PLEASE* feel free to create issues, and I'll *do my best* to address them. *PRs*
are also welcome.

== Pronunciation
[.big]#*cljonic* /klə-jŏn′ĭk/#

== Obligatory Word Definition

[.big]#*Laconic*#

* Expressing much in few words; brief and pithy; concise; brusque

== Detailed Documentation
Detailed documentation is available https://thecodesojourner.github.io/cljonic/[here].

== Motivation

.*John Carmack* - https://www.gamedeveloper.com/programming/in-depth-functional-programming-in-c-
____
"A large fraction of the flaws in software development are due to programmers not fully understanding all the
possible states their code may execute in. ... Programming in a functional style makes the state presented to your code
explicit, which makes it much easier to reason about [(i.e., Fewer mistakes, and less debugging)] ...

No matter what language you work in, programming in a functional style provides benefits. You should do it whenever it
is convenient, and you should think hard about the decision when it isn't convenient."
____

*Clojure* is one of the most expressive and concise programming languages. It is *laconic* in nature,
*immutable-by-default*, *practical*, and *stable*. *Clojure* is based on the *Lisp* programming language. However,
*Clojure* is not well suited for some programming domains, such as *embedded systems* with tight memory and CPU
constraints that do not allow *heap* usage and only offer *{cpp}* programming tools.

*{cpp}*, on the other hand, is quite different from *Clojure*. It tends to be *verbose* rather than *laconic*. *{cpp}*
is not immutable-by-default and is not considered a functional programming language, although it has some functional
features like *lambdas* and standard libraries such as *ranges*, *views*, *optional*, and *expected*. *{cpp}* is a
*BIG* language that is constantly evolving, with a large and growing feature set. It offers many ways to do things,
some of which make it easy to do things poorly.

Most *embedded system developers* are not computer language experts. They do not have time to keep up with the latest
*{cpp}* language features, and their educational background is often not heavy in the nuances of optimal *Modern*
*{cpp}* usage. When it comes to *Modern* *{cpp}*, they are often mostly *self-taught* and have learned on the job.

Some *embedded system* projects do not require heavy use of the *{cpp}* features designed for memory and CPU utilization
optimization. Many projects have a small amount of code that must be *finely tuned*, and quite a bit of code that is
*not performance critical*.

The motivation behind *cljonic* is to provide a *Clojure-esque* functional programming library for *{cpp}* embedded
system developers. It aims to be *laconic*, *immutable-by-default*, *practical*, and *stable*. *Cljonic* should also be
*easy to learn and understand*, and *easy to use properly* for the *safe* and *efficient* implementation of embedded
systems.

== Desired Developer Experience When Using cljonic
The developer using *cljonic* should be able to write *safe* code that is *easy to read*, *easy to understand* and
*easy to maintain*. These goals will enable *embedded system* projects to be *completed more quickly*, with
*fewer bugs*, and maintained and enhanced with *less long term effort*.

=== Default Element
Every *cljonic collection* has a *default element* that is returned whenever a valid collection element can't be returned.
The value of a collection's *default element* is a default initialized instance of the type of the collection's values
(i.e., T{}). This approach is taken to avoid using exceptions and/or error codes/states.

NOTE: The design choice to use a *default element* means that *cljonic* users must take care to ensure that they know
when a *default element* can be returned (e.g., Asking for the First element of an empty collection), and to handle things appropriately.

=== constexpr
*cljonic* uses *constexpr* whenever possible to enable *compile-time* evaluation of expressions. Additionally, this
approach helps to dectect *undefined behavior* at compile time.

== Developer Experience When Developing cljonic
*cljonic* has a *Makefile* in the root directory. For *help* on the available development processes simply execute
`make` from a command-line while in the root directory to generate a list of the available options.

*After cloning the repository*, the developer should first execute `make all`. Next, assuming no files are added to the
repository, only modified, the developer should execute `make test`. Assuming all tests pass, executing `make cljonic`
will generate the *cljonic* single header file. Finally, executing `make git` will prepare the repository for a *git*
commit.

NOTE: Whenever new files are added to the *code/source* or *code/test* directories the developer must run `make all`
again.

NOTE: Whenever new function files are added to the *code/source* directory the developer must update the
*code/source/cljonic-pre-declarations.hpp*, *scripts/make-cljonic.sh*, and *resources/no-dynamic-memory.cpp* files
to include support for the new function.

== Implementation Characteristics

NOTE: The performance of the implementation should be acceptable if the *average collection size is small*, and the
*cljonic* library is used for *non-performance-critical code*.

* In order to provide the *cljonic immutable-by-default* feature, and because the *heap* can't be used in the *targeted*
embedded systems, *cljonic* uses copying.

* In order to meet the *safe* Desired Developer Experience goal, *cljonic* does bounds-checking on all collection
accesses.

* In order to meet the *safe* Desired Developer Experience goal, *cljonic* provides a *#define* named *CLJONIC_COLLECTION_MAXIMUM_ELEMENT_COUNT* that specifies the maximum number of elements that can be stored in a *cljonic Collection*, which is defaulted to *1000*. *CLJONIC_COLLECTION_MAXIMUM_ELEMENT_COUNT* can be changed to a larger value if needed by adding a line of code like this "*#define CLJONIC_COLLECTION_MAXIMUM_ELEMENT_COUNT 10000*" before including *cljonic.hpp*, by directly defining *CLJONIC_COLLECTION_MAXIMUM_ELEMENT_COUNT* on the compiler command line, or by indirectly defining *CLJONIC_COLLECTION_MAXIMUM_ELEMENT_COUNT* in a build tool like *cmake*.

== Required Resources
* *AsciiDoc* for project documentation
** https://asciidoctor.org/docs/asciidoc-writers-guide/
* *C++ 20*, or higher, for *Modern {cpp}* features
* *catch* v2.13.9 for testing
* *clang-format* for code formatting
* *cppcheck* for code analysis
* *doxygen* for code documentation
* *gcov* for code coverage analysis
* *genhtml* for code coverage analysis
* *lcov* for code coverage analysis
* *lizard* for code metrics
** https://github.com/terryyin/lizard
** According to the lizard documentation, it only supports C++14. Another tool that supports *Cyclomatic Complexity* and *Function LoC* would be better.
* *valgrind* for code analysis