diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34dfb55..4562c02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,32 +12,32 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: install python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.11' - name: linter run: | - pip install cpplint + pip install cpplint==2.0.0 make lint test: runs-on: ubuntu-latest steps: - name: checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: install python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.11' - name: install tools run: | - pip install platformio==5.1.1 + pip install platformio==6.1.10 sudo apt-get update && sudo apt-get install -y lcov - name: run tests @@ -47,10 +47,10 @@ jobs: make clean coverage OPT=-O0 - name: Upload coverage to coveralls - uses: coverallsapp/github-action@v1.1.2 + uses: coverallsapp/github-action@v2.2.3 with: github-token: ${{ secrets.github_token }} - path-to-lcov: test/coverage.info + file: test/coverage.lcov - name: build examples run: make ci diff --git a/.gitignore b/.gitignore index db89cb9..8f7de42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.envrc +.venv/ .pio/ **/.vscode test/bin @@ -13,6 +15,7 @@ test/bin *.gcov *.gcno *.gcda +*.lcov test/coverage.info test/report **/tags diff --git a/CHANGELOG.md b/CHANGELOG.md index 014921a..5067819 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,64 @@ # JLed changelog (github.com/jandelgado/jled) +## [2024-12-01] 4.15.0 + +* new: `Update()` methods now optionally return the last brightness value + calculated and written out to the LED. See `examples/last_brightness` + +## [2024-09-21] 4.14 + +* new: make `JLed::Update(unit32_t t)` public, allowing optimizations and + simplified tests + +## [2023-09-10] 4.13.1 + +* fix: `Update()` sometimes returning wrong state (https://github.com/jandelgado/jled/issues/122) + +## [2023-08-20] 4.13.0 + +* new: `Stop()` takes optional parameter allowing to turn LED fully off + +## [2023-06-29] 4.12.2 + +* fix: `JLedSequence` starting again after call to `Stop` (https://github.com/jandelgado/jled/issues/115) + +## [2023-01-11] 4.12.1 + +* fix: add missing keywords to keywords.txt + +## [2022-11-13] 4.12.0 + +* new: add `MinBrightness` method and scale output to interval defined by + `MinBrightness` and `MaxBrightness`. + +## [2022-11-13] 4.11.1 + +* improve: reduce memory consumption of JLed objects by 3 bytes, simplify + state management. + +## [2022-03-29] 4.11.0 + +* change: `JLedSequence` objects are now assignable, making switching + effects easier. See https://github.com/jandelgado/jled-example-switch-sequence for an example. + +## [2022-03-24] 4.10.0 + +* new: `On`, `Off` and `Set` now take an optional `duration` value, making + these effects behave like any other in this regard. This allows to add + an `On` effect to a `JLedSequence` for a specific amount of time. + +## [2022-02-24] 4.9.1 + +* fix: make sure JLedSequence methods like `Repeat` and `Forever` are chainable + like in the `JLed` class + ## [2022-02-13] 4.9.0 * new: support ESP-IDF platform for the ESP32 (#87, thanks to @troky for the initial work). See also repositories https://github.com/jandelgado/jled-esp-idf-example and https://github.com/jandelgado/jled-esp-idf-platformio-example - + ## [2021-10-18] 4.8.0 * new: make `Breathe` method more flexible (#78, thanks to @boraozgen) @@ -54,12 +106,12 @@ ## [2019-08-30] 4.2.1 -* fix: make sure memory alignment is correct (caused hard fault on +* fix: make sure memory alignment is correct (caused hard fault on SAMD21). Fixes #27. ## [2019-06-20] 4.2.0 -* changing an effect resets the Jled object so it starts over with the +* changing an effect resets the Jled object so it starts over with the new effect (see #25). Prior to this change, calling `Reset()` was necessary. @@ -109,10 +161,10 @@ In addition to the changes introduced with `v4.0.0-rc0` and `v4.0.0-rc1`, the ### Added -* `JLed::Reset()` - resets the led to it's initial state allowing to +* `JLed::Reset()` - resets the led to it's initial state allowing to to start over * `JLed::IsRunning()` - return true if effect is active, else false -* new class `JLedSequence` to update JLed objects simultanously or +* new class `JLedSequence` to update JLed objects simultanously or sequentially. See [README](README.md#controlling-a-group-of-leds) for details. * added new [morse example](examples/morse) * clean separation between hardware specific and common code, making @@ -125,7 +177,7 @@ In addition to the changes introduced with `v4.0.0-rc0` and `v4.0.0-rc1`, the BrightnessEvaluator. Migration of code should be straight forward, see below -#### old brightness function +#### old brightness function In JLed version prio to version 4.0.0, a function pointer was used to specify a user provided brightness function. @@ -177,7 +229,7 @@ JLed led = JLed(LED_BUILTIN).UserFunc(&userEffect); ## [2018-09-22] v2.3.0 * ESP8266 platform: scaling from 8 to 10 bit improved. The scaling makes sure - that 0 is mapped to 0 and 255 is mapped to 1023, preserving min/max + that 0 is mapped to 0 and 255 is mapped to 1023, preserving min/max relationships in both ranges. ## [2018-06-09] v2.2.3 diff --git a/Makefile b/Makefile index 7bb93af..5891c20 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,22 @@ # use this makefile to build with platformio # -.PHONY: all clean upload monitor lint test ci +.PHONY: phony # some of the examples use LED_BUILTIN which is not defined for ESP32 CIOPTS=--board=uno --board=esp01 --board=nano33ble --lib="src" CIOPTS_MBED=--board=nucleo_f401re -Oframework=mbed --lib="src" CIOPTSALL=--board=esp32dev --board=uno --board=nano33ble --board=esp01 --lib="src" -all: +all: phony pio run -lint: - cpplint --extensions=cpp,h,ino $(shell find . -maxdepth 3 \( ! -regex '.*/\..*' \) \ +lint: phony + cpplint --filter -readability/check \ + --exclude test/catch2 \ + --extensions=cpp,h,ino $(shell find . -maxdepth 3 \( ! -regex '.*/\..*' \) \ -type f -a \( -name "*\.cpp" -o -name "*\.h" -o -name "*\.ino" \) ) -ci: +ci: phony pio ci $(CIOPTS) examples/custom_hal/custom_hal.ino pio ci $(CIOPTS_MBED) examples/multiled_mbed/multiled_mbed.cpp pio ci $(CIOPTS) --lib="examples/morse" examples/morse/morse.ino @@ -27,21 +29,22 @@ ci: pio ci $(CIOPTSALL) examples/fade_on/fade_on.ino pio ci $(CIOPTSALL) examples/sequence/sequence.ino -envdump: +envdump: phony -pio run --target envdump -clean: +clean: phony -pio run --target clean - rm -f {test,src}/{*.o,*.gcno,*.gcda} + cd test && make clean + rm -f src/{*.o,*.gcno,*.gcda} -upload: +upload: phony pio run --target upload -monitor: +monitor: phony pio device monitor -test: +test: phony $(MAKE) -C test coverage OPT=-O0 -tags: +tags: phony ctags -R diff --git a/README.md b/README.md index c1e6937..8dd0639 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +
+Preferring Python? I just released jled-circuitpython, +a JLed implementation for CircuitPython and MicroPython. +
+ # JLed - Advanced LED Library ![run tests](https://github.com/jandelgado/jled/workflows/run%20tests/badge.svg) @@ -16,13 +21,12 @@ and someone did a [video tutorial for JLed](https://youtu.be/x5V2vdpZq1w) - Tha Interactive JLed playground - JLed in action - jled running in the browser + JLed in action + jled running in the browser - ## Example ```c++ @@ -48,6 +52,7 @@ void loop() { * [Arduino IDE](#arduino-ide) * [PlatformIO](#platformio) * [Usage](#usage) + * [Output pipeline](#output-pipeline) * [Effects](#effects) * [Static on and off](#static-on-and-off) * [Static on example](#static-on-example) @@ -60,6 +65,8 @@ void loop() { * [FadeOn](#fadeon) * [FadeOn example](#fadeon-example) * [FadeOff](#fadeoff) + * [Fade](#fade) + * [Fade example](#fade-example) * [User provided brightness function](#user-provided-brightness-function) * [User provided brightness function example](#user-provided-brightness-function-example) * [Delays and repetitions](#delays-and-repetitions) @@ -73,7 +80,7 @@ void loop() { * [Immediate Stop](#immediate-stop) * [Misc functions](#misc-functions) * [Low active for inverted output](#low-active-for-inverted-output) - * [Maximum brightness level](#maximum-brightness-level) + * [Minimum- and Maximum brightness level](#minimum--and-maximum-brightness-level) * [Controlling a group of LEDs](#controlling-a-group-of-leds) * [Framework notes](#framework-notes) * [Platform notes](#platform-notes) @@ -84,8 +91,8 @@ void loop() { * [Arduino framework](#arduino-framework) * [Raspberry Pi Pico](#raspberry-pi-pico) * [Example sketches](#example-sketches) - * [PlatformIO](#platformio-1) - * [Arduino IDE](#arduino-ide-1) + * [Building examples with PlatformIO](#building-examples-with-platformio) + * [Building examples with the Arduino IDE](#building-examples-with-the-arduino-ide) * [Extending](#extending) * [Support new hardware](#support-new-hardware) * [Unit tests](#unit-tests) @@ -151,22 +158,47 @@ the only argument. Further configuration of the LED object is done using a fluen interface, e.g. `JLed led = JLed(13).Breathe(2000).DelayAfter(1000).Repeat(5)`. See the examples section below for further details. +#### Output pipeline + +First the configured effect (e.g. `Fade`) is evaluated for the current time +`t`. JLed internally uses unsigned bytes to represent brightness values, +ranging from 0 to 255. Next, the value is scaled to the limits set by +`MinBrightness` and `MaxBrightness` (optionally). When the effect is configured +for a low-active LED using `LowActive`, the brightness value will be inverted, +i.e., the value will be subtracted from 255. Finally the value is passed to the +hardware abstraction, which might scale it to the resolution used by the actual +device (e.g. 10 bits for an ESP8266). Finally the brightness value is written +out to the configure GPIO. + +```text +┌───────────┐ ┌────────────┐ ┌─────────┐ ┌────────┐ ┌─────────┐ ┌────────┐ +│ Evaluate │ │ Scale to │ │ Low │YES │ Invert │ │Scale for│ │Write to│ +│ effect(t) ├───►│ [min, max] ├───►│ active? ├───►│ signal ├───►│Hardware ├───►│ GPIO │ +└───────────┘ └────────────┘ └────┬────┘ └────────┘ └───▲─────┘ └────────┘ + │ NO │ + └───────────────────────────┘ +``` + ### Effects #### Static on and off -Calling `On()` turns the LED on. To immediately turn a LED on, make a call -like `JLed(LED_BUILTIN).On().Update()`. +Calling `On(uint16_t period=1)` turns the LED on. To immediately turn a LED on, +make a call like `JLed(LED_BUILTIN).On().Update()`. The `period` is optional +and defaults to 1ms. -`Off()` works like `On()`, except that it turns the LED off, i.e. it sets the +`Off()` works like `On()`, except that it turns the LED off, i.e., it sets the brightness to 0. -Use the `Set(uint8_t brightness)` method to set the brightness to the given -value, i.e. `Set(255)` is equivalent to calling `On()` and `Set(0)` is -equivalent to calling `Off()`. +Use the `Set(uint8_t brightness, uint16_t period=1)` method to set the +brightness to the given value, i.e., `Set(255)` is equivalent to calling `On()` +and `Set(0)` is equivalent to calling `Off()`. -Technically `Set`, `On` and `Off` are effects with a period of 1ms that -set the brightness to a constant value. +Technically, `Set`, `On` and `Off` are effects with a default period of 1ms, that +set the brightness to a constant value. Specifying a different period has an +effect on when the `Update()` method will be done updating the effect and +return false (like for any other effects). This is important when for example +in a `JLedSequence` the LED should stay on for a given amount of time. ##### Static on example @@ -225,7 +257,8 @@ void loop() { } ``` -It is also possible to specify fade-on, on and fade-off durations for the breathing mode to customize the effect. +It is also possible to specify fade-on, on- and fade-off durations for the +breathing mode to customize the effect. ```c++ // LED will fade-on in 500ms, stay on for 1000ms, and fade-off in 500ms. @@ -235,25 +268,25 @@ auto led = JLed(13).Breathe(500, 1000, 500).DelayAfter(1000).Forever(); #### Candle -In candle mode, the random flickering of a candle or fire is simulated. +In candle mode, the random flickering of a candle or fire is simulated. The builder method has the following signature: `Candle(uint8_t speed, uint8_t jitter, uin16_t period)` -* `speed` - controls the speed of the effect. 0 for fastest, increasing speed +* `speed` - controls the speed of the effect. 0 for fastest, increasing speed divides into halve per increment. The default value is 7. * `jitter` - the amount of jittering. 0 none (constant on), 255 maximum. Default value is 15. * `period` - Period of effect in ms. The default value is 65535 ms. The default settings simulate a candle. For a fire effect for example use -call the method with `Candle(5 /*speed*/, 100 /* jitter*/)`. +call the method with `Candle(5 /*speed*/, 100 /* jitter*/)`. ##### Candle example ```c++ #include -// Candle on LED pin 13 (PWM capable). +// Candle on LED pin 13 (PWM capable). auto led = JLed(13).Candle(); void setup() { } @@ -273,7 +306,6 @@ period 1000): [![fadeon function](doc/fadeon_plot.png)](https://www.wolframalpha.com/input/?i=plot+(exp(sin((t-1000%2F2.)*PI%2F1000))-0.36787944)*108.0++t%3D0+to+1000) - ##### FadeOn example ```c++ @@ -293,9 +325,33 @@ void loop() { In FadeOff mode, the LED is smoothly faded off using PWM. The fade starts at 100% brightness. Internally it is implemented as a mirrored version of the -FadeOn function, i.e. FadeOn(t) = FadeOff(period-t). The `FadeOff()` method +FadeOn function, i.e., FadeOff(t) = FadeOn(period-t). The `FadeOff()` method takes the period of the effect as argument. +#### Fade + +The Fade effect allows to fade from any start value `from` to any target value +`to` with the given duration. Internally it sets up a `FadeOn` or `FadeOff` +effect and `MinBrightness` and `MaxBrightness` values properly. The `Fade` +method take three arguments: `from`, `to` and `duration`. + +fade from-to + +##### Fade example + +```c++ +#include + +// fade from 100 to 200 with period 1000 +auto led = JLed(9).Fade(100, 200, 1000); + +void setup() { } + +void loop() { + led.Update(); +} +``` + #### User provided brightness function It is also possible to provide a user defined brightness evaluator. The class @@ -307,7 +363,7 @@ two methods: as an unsigned byte, where 0 means LED off and 255 means full brightness. * `uint16_t Period() const` - period of the effect. -All time values are specified in milliseconds. +All time values are specified in milliseconds. The [user_func](examples/user_func) example demonstrates a simple user provided brightness function, while the [morse](examples/morse) example shows how a more @@ -354,8 +410,29 @@ specified by `DelayAfter()` method. ##### Update -Call `Update()` periodically to update the state of the LED. `Update` returns -`true` if the effect is active, and `false` when it finished. +Call `Update(int16_t *pLast=nullptr)` or `Update(uint32_t t, int16_t *pLast=nullptr)` +to periodically update the state of the LED. + +`Update` returns `true`, if the effect is active, or `false` when it finished. +`Update()` is a shortcut to call `Update(uint32_t t)` with the current time in +milliseconds. + +To obtain the value of the last written brightness value (after applying min- +and max-brightness transformations), pass an additional optional pointer +`*pLast` , where this value will be stored, when it was written. Example: + +```c++ +int16_t lastVal = -1; +led.Update(&lastVal); +if (lastVal != -1) { + // the LED was updated with the brightness value now stored in lastVal + ... +} +``` + +Most of the time just calling `Update()` without any parameters is what you want. + +See [last_brightness](examples/last_brightness) example for a working example. ##### IsRunning @@ -369,30 +446,44 @@ you want to start-over an effect. ##### Immediate Stop Call `Stop()` to immediately turn the LED off and stop any running effects. -Further calls to `Update()` will have no effect unless the Led is reset (using -`Reset()`) or a new effect activated. +Further calls to `Update()` will have no effect, unless the Led is reset using +`Reset()` or a new effect is activated. By default, `Stop()` sets the current +brightness level to `MinBrightness`. + +`Stop()` takes an optional argument `mode` of type `JLed::eStopMode`: + +* if set to `JLed::eStopMode::KEEP_CURRENT`, the LEDs current level will be kept +* if set to `JLed::eStopMode::FULL_OFF` the level of the LED is set to `0`, + regardless of what `MinBrightness` is set to, effectively turning the LED off +* if set to `JLed::eStopMode::TO_MIN_BRIGHTNESS` (default behavior), the LED + will set to the value of `MinBrightness` + +```c++ +// stop the effect and set the brightness level to 0, regardless of min brightness +led.Stop(JLed::eStopMode::FULL_OFF); +``` #### Misc functions ##### Low active for inverted output Use the `LowActive()` method when the connected LED is low active. All output -will be inverted by JLed (i.e. instead of x, the value of 255-x will be set). - -##### Maximum brightness level +will be inverted by JLed (i.e., instead of x, the value of 255-x will be set). -The `MaxBrightness(uint8_t level)` method is used to set the maximum brightness -level of the LED. A level of 255 (the default) is full brightness, while 0 -effectively turns the LED off. +##### Minimum- and Maximum brightness level -The `uint_8 MaxBrightness() const` method returns the current maximum -brightness level. Since currently only the upper 5 bits of the provided -brighness value are used, the lower 3 bits returned are always 0. +The `MaxBrightness(uint8_t level)` method is used to set the maximum brightness +level of the LED. A level of 255 (the default) is full brightness, while 0 +effectively turns the LED off. In the same way, the `MinBrightness(uint8_t level)` +method sets the minimum brightness level. The default minimum level is 0. If +minimum or maximum brightness levels are set, the output value is scaled to be +within the interval defined by `[minimum brightness, maximum brightness]`: a +value of 0 will be mapped to the minimum brightness level, a value of 255 will +be mapped to the maximum brightness level. -If you want to programmatically increment or decrement the maximum brightness -level, use the `JLed::kBrightnessStep` constant (which is defined as `1 << -(8-JLed::kBitsBrightness)` as the increment (instead of the hard wired value -`8`) to be independent of the current JLed implementation using 5 bits. +The `uint_8 MaxBrightness() const` method returns the current maximum +brightness level. `uint8_t MinBrightness() const` returns the current minimum +brightness level. ### Controlling a group of LEDs @@ -428,10 +519,10 @@ The `JLedSequence` provides the following methods: else `false`. * Use the `Repeat(n)` method to specify the number of repetitions. The default value is 1 repetition. The `Forever()` methods sets to repeat the sequence - forever. -* `Stop()` - turns off all `JLed` objects controlled by the sequence and + forever. +* `Stop()` - turns off all `JLed` objects controlled by the sequence and stops the sequence. Further calls to `Update()` will have no effect. -* `Reset()` - Resets all `JLed` objects controlled by the sequence and +* `Reset()` - Resets all `JLed` objects controlled by the sequence and the sequence, resulting in a start-over. ## Framework notes @@ -446,7 +537,7 @@ framework: platform=ststm32 board = nucleo_f401re framework = mbed -build_flags = -Isrc +build_flags = -Isrc src_filter = +<../../src/> +<./> upload_protocol=stlink ``` @@ -468,7 +559,7 @@ src_dir = examples/multiled_mbed The DAC of the ESP8266 operates with 10 bits, every value JLed writes out gets automatically scaled to 10 bits, since JLed internally only uses 8 bits. The -scaling methods make sure that min/max relationships are preserved, i.e. 0 is +scaling methods make sure that min/max relationships are preserved, i.e., 0 is mapped to 0 and 255 is mapped to 1023. When using a user-defined brightness function on the ESP8266, 8-bit values must be returned, all scaling is done by JLed transparently for the application, yielding platform-independent code. @@ -498,8 +589,8 @@ so it should be avoided and is normally not necessary. For completeness, the full signature of the Esp32Hal constructor is ``` -Esp32Hal(PinType pin, - int chan = kAutoSelectChan, +Esp32Hal(PinType pin, + int chan = kAutoSelectChan, uint16_t freq = 5000, ledc_timer_t timer = LEDC_TIMER_0) ``` @@ -528,9 +619,14 @@ necessary to upload sketches to the microcontroller. ### Raspberry Pi Pico -When using JLed on a Raspberry Pi Pico, the Pico-SDK and tools must be -installed. The Pico supports up to 16 PWM channels in parallel. See -the [pico-demo](examples/raspi_pico) for an example and build instructions. +When using JLed on a Raspberry Pi Pico, the Pico-SDK and tools can be +used. The Pico supports up to 16 PWM channels in parallel. See +the [pico-demo](examples/raspi_pico) for an example and build instructions when +the Pico-SDK is used. + +A probably easier approach is to use the Arduino platform. See +[platformio.ini](platformio.ini) for details (look for +`env:raspberrypi_pico_w`, which targets the Raspberry Pi Pico W. ## Example sketches @@ -542,20 +638,25 @@ Example sketches are provided in the [examples](examples/) directory. * [Candle effect](examples/candle) * [Fade LED on](examples/fade_on) * [Fade LED off](examples/fade_off) +* [Fade from-to effect](examples/fade_from_to) +* [Pulse effect](examples/pulse) * [Controlling multiple LEDs in parallel](examples/multiled) * [Controlling multiple LEDs in parallel (mbed)](examples/multiled_mbed) * [Controlling multiple LEDs sequentially](examples/sequence) * [Simple User provided effect](examples/user_func) * [Morsecode example](examples/morse) +* [Last brightness value example](examples/last_brightness) * [Custom HAL example](examples/custom_hal) +* [Custom PCA9685 HAL](https://github.com/jandelgado/jled-pca9685-hal) +* [Dynamically switch sequences](https://github.com/jandelgado/jled-example-switch-sequence) * [JLed compiled to WASM and running in the browser](https://jandelgado.github.io/jled-wasm) * [Raspberry Pi Pico Demo](examples/raspi_pico) * [ESP32 ESP-IDF example](https://github.com/jandelgado/jled-esp-idf-example) * [ESP32 ESP-IDF PlatformIO example](https://github.com/jandelgado/jled-esp-idf-platformio-example) -### PlatformIO +### Building examples with PlatformIO -To build an example using [the PlatformIO ide](http://platformio.org/), +To build an example using [the PlatformIO ide](http://platformio.org/), uncomment the example to be built in the [platformio.ini](platformio.ini) project file, e.g.: @@ -566,7 +667,7 @@ src_dir = examples/hello ;src_dir = examples/breathe ``` -### Arduino IDE +### Building examples with the Arduino IDE To build an example sketch in the Arduino IDE, select an example from the `File` > `Examples` > `JLed` menu. @@ -594,8 +695,8 @@ the host-based provided unit tests [is provided here](test/README.md). * add code * add [unit test(s)](test/) * add [documentation](README.md) -* make sure the cpp [linter](https://github.com/cpplint/cpplint) does not - report any problems (run `make lint`). Hint: use `clang-format` with the +* make sure the cpp [linter](https://github.com/cpplint/cpplint) does not + report any problems (run `make lint`). Hint: use `clang-format` with the provided [settings](.clang-format) * commit changes * submit a PR @@ -619,9 +720,8 @@ Just 'reconfigure' the `JLed` with any of the effect methods (e.g. `FadeOn`, ## Author and Copyright -Copyright 2017, 2018 by Jan Delgado, jdelgado[at]gmx.net. +Copyright 2017-2022 by Jan Delgado, jdelgado[at]gmx.net. ## License [MIT](LICENSE) - diff --git a/devbox.json b/devbox.json new file mode 100644 index 0000000..e24a8ed --- /dev/null +++ b/devbox.json @@ -0,0 +1,18 @@ +{ + "packages": [ + "python@3.13", + "lcov@1.16", + "pipx", + "cpplint@2.0.0" + ], + "shell": { + "init_hook": [ + ". $VENV_DIR/bin/activate" + ], + "scripts": { + "test": [ + "echo \"Error: no test specified\" && exit 1" + ] + } + } +} diff --git a/devbox.lock b/devbox.lock new file mode 100644 index 0000000..5e96396 --- /dev/null +++ b/devbox.lock @@ -0,0 +1,150 @@ +{ + "lockfile_version": "1", + "packages": { + "cpplint@2.0.0": { + "last_modified": "2024-11-03T14:18:04Z", + "resolved": "github:NixOS/nixpkgs/4ae2e647537bcdbb82265469442713d066675275#cpplint", + "source": "devbox-search", + "version": "2.0.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/70gc21bl3grda63djlks6x1f5g8h89xk-cpplint-2.0.0", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/3sq5396dcbg3r06g9zci1w4rxql1xf0k-cpplint-2.0.0-dist" + } + ], + "store_path": "/nix/store/70gc21bl3grda63djlks6x1f5g8h89xk-cpplint-2.0.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/3fcbszvn75zwim2n3785bflrwww38l42-cpplint-2.0.0", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/fqrmkx6q4q290hildy4l6gi2pzwzwinw-cpplint-2.0.0-dist" + } + ], + "store_path": "/nix/store/3fcbszvn75zwim2n3785bflrwww38l42-cpplint-2.0.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/m675bxwgh3wmr0gix5g6xnhkidlql0ii-cpplint-2.0.0", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/2bpdwa3r1kfzf4jkd4i24njxy3pr3xnv-cpplint-2.0.0-dist" + } + ], + "store_path": "/nix/store/m675bxwgh3wmr0gix5g6xnhkidlql0ii-cpplint-2.0.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vrv52827v0b6h4k3nl7k9zg4ld182khg-cpplint-2.0.0", + "default": true + }, + { + "name": "dist", + "path": "/nix/store/132q2dd3wkkgdpybwzhybm7hbad0g097-cpplint-2.0.0-dist" + } + ], + "store_path": "/nix/store/vrv52827v0b6h4k3nl7k9zg4ld182khg-cpplint-2.0.0" + } + } + }, + "lcov@1.16": { + "last_modified": "2024-03-22T11:26:23Z", + "resolved": "github:NixOS/nixpkgs/a3ed7406349a9335cb4c2a71369b697cecd9d351#lcov", + "source": "devbox-search", + "version": "1.16", + "systems": { + "aarch64-darwin": { + "store_path": "/nix/store/cwjgl90nkg79za5gx41yg4663w7iyj0y-lcov-1.16" + }, + "aarch64-linux": { + "store_path": "/nix/store/81axjrgg3cjx09kdb25psiiyz60zacqw-lcov-1.16" + }, + "x86_64-darwin": { + "store_path": "/nix/store/5jir4n0q72z9p2h9sxwb2rcam3j165wp-lcov-1.16" + }, + "x86_64-linux": { + "store_path": "/nix/store/qfdwnjp77xlvg0jqfv1dl8b40xpv230k-lcov-1.16" + } + } + }, + "pipx": { + "resolved": "github:NixOS/nixpkgs/75a52265bda7fd25e06e3a67dee3f0354e73243c#pipx", + "source": "nixpkg" + }, + "python@3.13": { + "last_modified": "2024-11-28T07:51:56Z", + "plugin_version": "0.0.4", + "resolved": "github:NixOS/nixpkgs/226216574ada4c3ecefcbbec41f39ce4655f78ef#python313", + "source": "devbox-search", + "version": "3.13.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/fbyrkq5n04a9hn5zs26vrmqjzdx73d4g-python3-3.13.0", + "default": true + } + ], + "store_path": "/nix/store/fbyrkq5n04a9hn5zs26vrmqjzdx73d4g-python3-3.13.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/jbz9fj3sp5c8bf0s6d0bkjjj9mslxsrc-python3-3.13.0", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/60jgy93wj50wwimmhm2p53pzaiap8ypm-python3-3.13.0-debug" + } + ], + "store_path": "/nix/store/jbz9fj3sp5c8bf0s6d0bkjjj9mslxsrc-python3-3.13.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/c7j1vxcdcqswsddm5m1n2n4z5zfhmbq2-python3-3.13.0", + "default": true + } + ], + "store_path": "/nix/store/c7j1vxcdcqswsddm5m1n2n4z5zfhmbq2-python3-3.13.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/0b83hlniyfbpha92k2j0w93mxdalv8kb-python3-3.13.0", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/xzhxhqs8my0yvfi09aj1s9i1s9nrmpvg-python3-3.13.0-debug" + } + ], + "store_path": "/nix/store/0b83hlniyfbpha92k2j0w93mxdalv8kb-python3-3.13.0" + } + } + } + } +} diff --git a/doc/cheat_sheet.jpg b/doc/cheat_sheet.jpg index c20c587..1d31fdd 100644 Binary files a/doc/cheat_sheet.jpg and b/doc/cheat_sheet.jpg differ diff --git a/doc/fade_from-to.png b/doc/fade_from-to.png new file mode 100644 index 0000000..05c351c Binary files /dev/null and b/doc/fade_from-to.png differ diff --git a/examples/fade_from_to/fade_from_to.ino b/examples/fade_from_to/fade_from_to.ino new file mode 100644 index 0000000..16251da --- /dev/null +++ b/examples/fade_from_to/fade_from_to.ino @@ -0,0 +1,23 @@ +// JLed fade from-to example. Example randomly fades to a new level with +// a random duration. +// Copyright 2022 by Jan Delgado. All rights reserved. +// https://github.com/jandelgado/jled +#include + +auto led = JLed(5).On(1); // start with LED turned on + +void setup() {} + +void loop() { + static uint8_t last_to = 255; + + if (!led.Update()) { + // when effect is done (Update() returns false), + // reconfigure fade effect using random values + auto new_from = last_to; + auto new_to = jled::rand8(); + auto duration = 250 + jled::rand8() * 4; + last_to = new_to; + led.Fade(new_from, new_to, duration).Repeat(1); + } +} diff --git a/examples/last_brightness/last_brightness.ino b/examples/last_brightness/last_brightness.ino new file mode 100644 index 0000000..b5cb71a --- /dev/null +++ b/examples/last_brightness/last_brightness.ino @@ -0,0 +1,38 @@ +// Stops an effect when a button is pressed (and hold). When the button is +// released, the LED will fade to off with starting the brightness value it had +// when the effect was stopped. +// +// dependency: arduinogetstarted/ezButton@1.0.6 to control the button +// +// Copyright 2024 by Jan Delgado. All rights reserved. +// https://github.com/jandelgado/jled +// +#include // arduinogetstarted/ezButton@1.0.6 +#include + +constexpr auto LED_PIN = 16; +constexpr auto BUTTON_PIN = 18; + +auto button = ezButton(BUTTON_PIN); + +// start with a pulse effect +auto led = + JLed(LED_PIN).DelayBefore(1000).Breathe(2000).Forever().MinBrightness(25); + +void setup() {} + +void loop() { + static int16_t lastBrightness = 0; + + button.loop(); + led.Update(&lastBrightness); + + if (button.isPressed()) { + // when the button is pressed, stop the effect on led, but keep the LED + // on with it's current brightness ... + led.Stop(JLed::KEEP_CURRENT); + } else if (button.isReleased()) { + // when the button is released, fade from the last brightness to 0 + led = JLed(LED_PIN).Fade(lastBrightness, 0, 1000); + } +} diff --git a/examples/pulse/pulse.ino b/examples/pulse/pulse.ino new file mode 100644 index 0000000..824c16c --- /dev/null +++ b/examples/pulse/pulse.ino @@ -0,0 +1,12 @@ +// JLed pulse demo. +// Copyright 2022 by Jan Delgado. All rights reserved. +// https://github.com/jandelgado/jled +#include + +// "pulse" LED on GPIO pin 5. The "pulse" is accomplished by setting +// a minimal brightness so the LED will not be dark. +auto led = JLed(5).Breathe(2000).MinBrightness(20).Forever().DelayAfter(500); + +void setup() {} + +void loop() { led.Update(); } diff --git a/keywords.txt b/keywords.txt index c9accb7..e4b458e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -10,7 +10,11 @@ JLed KEYWORD1 JLedSequence KEYWORD1 +BlinkBrightnessEvaluator KEYWORD1 BrightnessEvaluator KEYWORD1 +BreatheBrightnessEvaluator KEYWORD1 +CandleBrightnessEvaluator KEYWORD1 +ConstantBrightnessEvaluator KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -29,6 +33,7 @@ Forever KEYWORD2 DelayBefore KEYWORD2 DelayAfter KEYWORD2 Forever KEYWORD2 +Hal KEYWORD2 IsForever KEYWORD2 IsRunning KEYWORD2 LowActive KEYWORD2 @@ -37,6 +42,7 @@ Stop KEYWORD2 Update KEYWORD2 UserFunc KEYWORD2 Reset KEYWORD2 +MinBrightness KEYWORD2 MaxBrightness KEYWORD2 ####################################### diff --git a/library.json b/library.json index 20d5aed..448ad55 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "JLed", - "version": "4.9.0", + "version": "4.15.0", "description": "An embedded library to control LEDs", "license": "MIT", "frameworks": ["espidf", "arduino", "mbed"], diff --git a/library.properties b/library.properties index 1a0f0f3..f71cc0d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=JLed -version=4.9.0 +version=4.15.0 author=Jan Delgado maintainer=Jan Delgado sentence=An Arduino library to control LEDs diff --git a/platformio.ini b/platformio.ini index 23410e9..86f7718 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,51 +12,51 @@ ; uncomment platform to build for ;default_envs = nucleo_f401re_mbed ;default_envs = nucleo_f401re -default_envs = nanoatmega328 +;default_envs = nanoatmega328 ;default_envs = nano33ble ;default_envs = nucleo_f401re ;default_envs = esp8266 -;default_envs = esp32 -;default_envs = sparkfun_samd21_dev_usb +default_envs = esp32 ;default_envs = sparkfun_samd21_dev_usb +;default_envs = raspberrypi_pico_w ; uncomment example to build -;src_dir = examples/hello +src_dir = examples/hello ;src_dir = examples/morse -src_dir = examples/breathe +;src_dir = examples/breathe ;src_dir = examples/candle ;src_dir = examples/fade_on ;src_dir = examples/fade_off ;src_dir = examples/simple_on +;src_dir = examples/last_brightness ;src_dir = examples/multiled ;src_dir = examples/multiled_mbed ;src_dir = examples/user_func ;src_dir = examples/sequence ;src_dir = examples/custom_hal +;src_dir = examples/pulse +;src_dir = examples/fade_from_to + +[env] +build_flags = -Isrc +build_src_filter = +<../../src/> +<./> +;lib_deps = arduinogetstarted/ezButton@1.0.6 [env:nanoatmega328] platform = atmelavr board = nanoatmega328 framework = arduino -build_flags = -Isrc -src_filter = +<../../src/> +<./> [env:nucleo_f401re_mbed] platform=ststm32 board = nucleo_f401re framework = mbed -build_flags = -Isrc -src_filter = +<../../src/> +<./> upload_protocol=stlink [env:nucleo_f401re] -# nucleo f401re arduino framework support only on master at the moment platform=ststm32 -;platform=https://github.com/platformio/platform-ststm32.git board = nucleo_f401re framework = arduino -build_flags = -Isrc -src_filter = +<../../src/> +<./> upload_protocol=stlink debug_speed=auto @@ -64,29 +64,29 @@ debug_speed=auto platform = espressif8266 board = nodemcuv2 framework = arduino -build_flags = -Isrc -src_filter = +<../../src/> +<./> [env:esp32] lib_ldf_mode = off platform = espressif32 board = esp32dev framework = arduino -build_flags = -Isrc -src_filter = +<../../src/> +<./> [env:sparkfun_samd21_dev_usb] platform = atmelsam framework = arduino board = sparkfun_samd21_dev_usb -build_flags = -Isrc -src_filter = +<../../src/> +<./> [env:nano33ble] platform=https://github.com/platformio/platform-nordicnrf52.git board = nano33ble framework = arduino -build_flags = -Isrc -src_filter = +<../../src/> +<./> upload_protocol=stlink +[env:raspberrypi_pico_w] +build_flags = ${env.build_flags} -D ARDUINO_RASPBERRY_PI_PICO_W +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = rpipicow +framework = arduino +board_build.filesystem_size = 0.5m +board_build.core = earlephilhower +upload_protocol = picotool diff --git a/src/esp32_hal.h b/src/esp32_hal.h index 466fb77..7d235cb 100644 --- a/src/esp32_hal.h +++ b/src/esp32_hal.h @@ -133,7 +133,7 @@ class Esp32Hal { } uint32_t millis() const { - return (uint32_t)(esp_timer_get_time() / 1000ULL); + return static_cast(esp_timer_get_time() / 1000ULL); } PinType chan() const { return chan_; } diff --git a/src/jled.h b/src/jled.h index 4b16a8a..41b21d1 100644 --- a/src/jled.h +++ b/src/jled.h @@ -58,8 +58,8 @@ class JLed : public TJLed { }; // a group of JLed objects which can be controlled simultanously -class JLedSequence : public TJLedSequence { - using TJLedSequence::TJLedSequence; +class JLedSequence : public TJLedSequence { + using TJLedSequence::TJLedSequence; }; }; // namespace jled diff --git a/src/jled_base.cpp b/src/jled_base.cpp index 740f646..62d0358 100644 --- a/src/jled_base.cpp +++ b/src/jled_base.cpp @@ -26,13 +26,12 @@ namespace jled { // pre-calculated fade-on function. This table samples the function // y(x) = exp(sin((t - period / 2.) * PI / period)) - 0.36787944) // * 108. -// at x={0,32,...,256}. In FadeOnFunc() we us linear interpolation -// to +// at x={0,32,...,256}. In FadeOnFunc() we us linear interpolation to // approximate the original function (so we do not need fp-ops). // fade-off and breath functions are all derived from fade-on, see // below. static constexpr uint8_t kFadeOnTable[] = {0, 3, 13, 33, 68, - 118, 179, 232, 255}; + 118, 179, 232, 255}; // NOLINT // https://www.wolframalpha.com/input/?i=plot+(exp(sin((x-100%2F2.)*PI%2F100))-0.36787944)*108.0++x%3D0+to+100 // The fade-on func is an approximation of @@ -66,15 +65,25 @@ uint8_t rand8() { return (uint8_t)rand_; } -// scale a byte by a factor, where only the lower 5 bits of factor are used. -// i.e. the scaling factor is in the range [0,31]. scale5 has the following -// properties: -// scale5(x, f) = x*f / 32 for all x and f=0..30 -// scale5(x, 31) = x for all x -uint8_t scale5(uint8_t val, uint8_t factor) { - if (factor == 31) - return val; // optimize for most common case (full brightness) - return ((uint16_t)val * factor) >> 5; +// scale a byte (val) by a byte (factor). scale8 has the following properties: +// scale8(0, f) == 0 for all f +// scale8(x, 255) == x for all x +uint8_t scale8(uint8_t val, uint8_t factor) { + return (static_cast(val)*static_cast(factor))/255; +} + +// interpolate a byte (val) to the interval [a,b]. +uint8_t lerp8by8(uint8_t val, uint8_t a, uint8_t b) { + if (a == 0 && b == 255) return val; // optimize for most common case + const uint8_t delta = b - a; + return a + scale8(val, delta); +} + +// the inverse of lerp8by8: invlerp8by8(lerp8by8(x, a, b,), a, b,) = x +uint8_t invlerp8by8(uint8_t val, uint8_t a, uint8_t b) { + const uint16_t delta = b - a; + if (delta == 0) return 0; + return (static_cast(val-a)*255)/(delta); } }; // namespace jled diff --git a/src/jled_base.h b/src/jled_base.h index 5d8eb46..1575dc1 100644 --- a/src/jled_base.h +++ b/src/jled_base.h @@ -44,7 +44,14 @@ static constexpr uint8_t kZeroBrightness = 0; uint8_t fadeon_func(uint32_t t, uint16_t period); uint8_t rand8(); void rand_seed(uint32_t s); -uint8_t scale5(uint8_t val, uint8_t factor); +uint8_t scale8(uint8_t val, uint8_t f); +uint8_t lerp8by8(uint8_t val, uint8_t a, uint8_t b); +uint8_t invlerp8by8(uint8_t val, uint8_t a, uint8_t b); + +template +static constexpr T __max(T a, T b) { + return (a > b) ? a : b; +} // a function f(t,period,param) that calculates the LEDs brightness for a given // point in time and the given period. param is an optionally user provided @@ -66,14 +73,16 @@ class CloneableBrightnessEvaluator : public BrightnessEvaluator { class ConstantBrightnessEvaluator : public CloneableBrightnessEvaluator { uint8_t val_; + uint16_t duration_; public: ConstantBrightnessEvaluator() = delete; - explicit ConstantBrightnessEvaluator(uint8_t val) : val_(val) {} + explicit ConstantBrightnessEvaluator(uint8_t val, uint16_t duration = 1) + : val_(val), duration_(duration) {} BrightnessEvaluator* clone(void* ptr) const override { return new (ptr) ConstantBrightnessEvaluator(*this); } - uint16_t Period() const override { return 1; } + uint16_t Period() const override { return duration_; } uint8_t Eval(uint32_t) const override { return val_; } }; @@ -94,36 +103,6 @@ class BlinkBrightnessEvaluator : public CloneableBrightnessEvaluator { } }; -// fade LED on -class FadeOnBrightnessEvaluator : public CloneableBrightnessEvaluator { - uint16_t period_; - - public: - FadeOnBrightnessEvaluator() = delete; - explicit FadeOnBrightnessEvaluator(uint16_t period) : period_(period) {} - BrightnessEvaluator* clone(void* ptr) const override { - return new (ptr) FadeOnBrightnessEvaluator(*this); - } - uint16_t Period() const override { return period_; } - uint8_t Eval(uint32_t t) const override { return fadeon_func(t, period_); } -}; - -// fade LED off -class FadeOffBrightnessEvaluator : public CloneableBrightnessEvaluator { - uint16_t period_; - - public: - FadeOffBrightnessEvaluator() = delete; - explicit FadeOffBrightnessEvaluator(uint16_t period) : period_(period) {} - BrightnessEvaluator* clone(void* ptr) const override { - return new (ptr) FadeOffBrightnessEvaluator(*this); - } - uint16_t Period() const override { return period_; } - uint8_t Eval(uint32_t t) const override { - return fadeon_func(period_ - t, period_); - } -}; - // The breathe func is composed by fade-on, on and fade-off phases. For fading // we approximate the following function: // y(x) = exp(sin((t-period/4.) * 2. * PI / period)) - 0.36787944) * 108.) @@ -134,15 +113,21 @@ class BreatheBrightnessEvaluator : public CloneableBrightnessEvaluator { uint16_t duration_fade_on_; uint16_t duration_on_; uint16_t duration_fade_off_; + uint8_t from_; + uint8_t to_; public: BreatheBrightnessEvaluator() = delete; explicit BreatheBrightnessEvaluator(uint16_t duration_fade_on, uint16_t duration_on, - uint16_t duration_fade_off) + uint16_t duration_fade_off, + uint8_t from = 0, + uint8_t to = kFullBrightness) : duration_fade_on_(duration_fade_on), duration_on_(duration_on), - duration_fade_off_(duration_fade_off) {} + duration_fade_off_(duration_fade_off), + from_(from), + to_(to) {} BrightnessEvaluator* clone(void* ptr) const override { return new (ptr) BreatheBrightnessEvaluator(*this); } @@ -150,15 +135,21 @@ class BreatheBrightnessEvaluator : public CloneableBrightnessEvaluator { return duration_fade_on_ + duration_on_ + duration_fade_off_; } uint8_t Eval(uint32_t t) const override { + uint8_t val = 0; if (t < duration_fade_on_) - return fadeon_func(t, duration_fade_on_); + val = fadeon_func(t, duration_fade_on_); else if (t < duration_fade_on_ + duration_on_) - return kFullBrightness; - else if (t < Period()) - return fadeon_func(Period() - t, duration_fade_off_); + val = kFullBrightness; else - return kZeroBrightness; + val = fadeon_func(Period() - t, duration_fade_off_); + return lerp8by8(val, from_, to_); } + + uint16_t DurationFadeOn() const { return duration_fade_on_; } + uint16_t DurationFadeOff() const { return duration_fade_off_; } + uint16_t DurationOn() const { return duration_on_; } + uint8_t From() const { return from_; } + uint8_t To() const { return to_; } }; class CandleBrightnessEvaluator : public CloneableBrightnessEvaluator { @@ -205,8 +196,12 @@ class TJLed { // Hardware abstraction giving access to the MCU HalType hal_; + // Evaluate effect(t) and scale to be within [minBrightness, maxBrightness] + // assumes brigthness_eval_ is set as it is not checked here. + uint8_t Eval(uint32_t t) const { return brightness_eval_->Eval(t); } + + // Write val out to the "hardware", inverting signal when active-low is set. void Write(uint8_t val) { - val = scale5(val, maxBrightness_); hal_.analogWrite(IsLowActive() ? kFullBrightness - val : val); } @@ -214,9 +209,10 @@ class TJLed { TJLed() = delete; explicit TJLed(const HalType& hal) : hal_{hal}, - state_{ST_RUNNING}, + state_{ST_INIT}, bLowActive_{false}, - maxBrightness_{(1 << kBitsBrightness) - 1} {} + minBrightness_{0}, + maxBrightness_{255} {} explicit TJLed(typename HalType::PinType pin) : TJLed{HalType{pin}} {} @@ -225,6 +221,7 @@ class TJLed { B& operator=(const TJLed& rLed) { state_ = rLed.state_; bLowActive_ = rLed.bLowActive_; + minBrightness_ = rLed.minBrightness_; maxBrightness_ = rLed.maxBrightness_; num_repetitions_ = rLed.num_repetitions_; last_update_time_ = rLed.last_update_time_; @@ -249,8 +246,6 @@ class TJLed { HalType& Hal() { return hal_; } - bool Update() { return Update(hal_.millis()); } - // Set physical LED polarity to be low active. This inverts every // signal physically output to a pin. B& LowActive() { @@ -261,29 +256,46 @@ class TJLed { bool IsLowActive() const { return bLowActive_; } // turn LED on - B& On() { return Set(kFullBrightness); } + B& On(uint16_t duration = 1) { return Set(kFullBrightness, duration); } // turn LED off - B& Off() { return Set(kZeroBrightness); } + B& Off(uint16_t duration = 1) { return Set(kZeroBrightness, duration); } - // Sets LED to given brightness - B& Set(uint8_t brightness) { + // Sets LED to given brightness. As for every effect, a duration can be + // specified. Update() will return false after the duration elapsed. + B& Set(uint8_t brightness, uint16_t duration = 1) { // note: we use placement new and therefore not need to keep track of // mem allocated - return SetBrightnessEval(new (brightness_eval_buf_) - ConstantBrightnessEvaluator(brightness)); + return SetBrightnessEval( + new (brightness_eval_buf_) + ConstantBrightnessEvaluator(brightness, duration)); } // Fade LED on - B& FadeOn(uint16_t duration) { - return SetBrightnessEval(new (brightness_eval_buf_) - FadeOnBrightnessEvaluator(duration)); + B& FadeOn(uint16_t duration, uint8_t from = 0, + uint8_t to = kFullBrightness) { + return SetBrightnessEval( + new (brightness_eval_buf_) + BreatheBrightnessEvaluator(duration, 0, 0, from, to)); } // Fade LED off - acutally is just inverted version of FadeOn() - B& FadeOff(uint16_t duration) { - return SetBrightnessEval(new (brightness_eval_buf_) - FadeOffBrightnessEvaluator(duration)); + B& FadeOff(uint16_t duration, uint8_t from = kFullBrightness, + uint8_t to = 0) { + return SetBrightnessEval( + new (brightness_eval_buf_) + BreatheBrightnessEvaluator(0, 0, duration, to, from)); + } + + // Fade from "from" to "to" with period "duration". Sets up the breathe + // effect with the proper parameters and sets Min/Max brightness to reflect + // levels specified by "from" and "to". + B& Fade(uint8_t from, uint8_t to, uint16_t duration) { + if (from < to) { + return FadeOn(duration, from, to); + } else { + return FadeOff(duration, from, to); + } } // Set effect to Breathe, with the given period time in ms. @@ -343,8 +355,12 @@ class TJLed { // Stop current effect and turn LED immeadiately off. Further calls to // Update() will have no effect. - B& Stop() { - Write(kZeroBrightness); + enum eStopMode { TO_MIN_BRIGHTNESS = 0, FULL_OFF, KEEP_CURRENT }; + B& Stop(eStopMode mode = eStopMode::TO_MIN_BRIGHTNESS) { + if (mode != eStopMode::KEEP_CURRENT) { + Write(mode == eStopMode::FULL_OFF ? kZeroBrightness + : minBrightness_); + } state_ = ST_STOPPED; return static_cast(*this); } @@ -353,28 +369,37 @@ class TJLed { // Reset to inital state B& Reset() { - time_start_ = kTimeUndef; - last_update_time_ = kTimeUndef; - state_ = ST_RUNNING; + time_start_ = 0; + last_update_time_ = 0; + state_ = ST_INIT; return static_cast(*this); } - // Sets the maximum brightness level. 255 is full brightness, 0 turns the - // effect off. Currently, only upper 5 bits of the provided value are used - // and stored. - B& MaxBrightness(uint8_t level) { - maxBrightness_ = level >> (8 - kBitsBrightness); + // Sets the minimum brightness level (ranging from 0 to 255). + B& MinBrightness(uint8_t level) { + minBrightness_ = level; return static_cast(*this); } - // Returns current maximum brightness level. Since currently only upper 5 - // bits are used, lower 3 bits will always be 0. - uint8_t MaxBrightness() const { - return maxBrightness_ << (8 - kBitsBrightness); + // Returns current minimum brightness level. + uint8_t MinBrightness() const { return minBrightness_; } + + // Sets the minimum brightness level (ranging from 0 to 255). + B& MaxBrightness(uint8_t level) { + maxBrightness_ = level; + return static_cast(*this); } - protected: - // update brightness of LED using the given brightness evaluator + // Returns current maximum brightness level. + uint8_t MaxBrightness() const { return maxBrightness_; } + + // update brightness of LED using the given brightness evaluator and the + // current time. If the optional pLast pointer is set, then the actual + // brightness value (if an update happened), will be returned through + // the pointer. The value returned will be the calculated value after + // min- and max-brightness scaling was applied, which is the value that + // is written to the output. + // // (brightness) ________________ // on 255 | ¸-' // | ¸-' @@ -383,104 +408,142 @@ class TJLed { // |<-delay before->|<--period-->|<-delay after-> (time) // | func(t) | // |<- num_repetitions times -> - bool Update(uint32_t now) { - if (state_ == ST_STOPPED || !brightness_eval_) return false; + bool Update(int16_t* pLast = nullptr) { + return Update(hal_.millis(), pLast); + } - // no need to process updates twice during one time tick. - if (last_update_time_ == now) return true; + bool Update(uint32_t t, int16_t* pLast = nullptr) { + if (state_ == ST_STOPPED || !brightness_eval_) return false; - if (last_update_time_ == kTimeUndef) { - time_start_ = now + delay_before_; + if (state_ == ST_INIT) { + time_start_ = t + delay_before_; + state_ = ST_RUNNING; + } else { + // no need to process updates twice during one time tick. + if (!timeChangedSinceLastUpdate(t)) return true; } - last_update_time_ = now; - if ((int32_t)(now - time_start_) < 0) return true; + trackLastUpdateTime(t); + + if (static_cast(t - time_start_) < 0) return true; + + auto writeCur = [this](uint32_t t, int16_t* p) { + const auto val = lerp8by8(Eval(t), minBrightness_, maxBrightness_); + if (p) { + *p = val; + } + Write(val); + }; // t cycles in range [0..period+delay_after-1] const auto period = brightness_eval_->Period(); - const auto t = (now - time_start_) % (period + delay_after_); + if (!IsForever()) { + const auto time_end = time_start_ + + static_cast(period + delay_after_) * + num_repetitions_ - + 1; + + if (static_cast(t - time_end) >= 0) { + // make sure final value of t = (period-1) is set + state_ = ST_STOPPED; + writeCur(period - 1, pLast); + return false; + } + } + + t = (t - time_start_) % (period + delay_after_); if (t < period) { state_ = ST_RUNNING; - Write(brightness_eval_->Eval(t)); + writeCur(t, pLast); } else { if (state_ == ST_RUNNING) { // when in delay after phase, just call Write() // once at the beginning. state_ = ST_IN_DELAY_AFTER_PHASE; - Write(brightness_eval_->Eval(period - 1)); + writeCur(period - 1, pLast); } } - - if (IsForever()) return true; - - const auto time_end = - time_start_ + (uint32_t)(period + delay_after_) * num_repetitions_ - - 1; - - if ((int32_t)(now - time_end) >= 0) { - // make sure final value of t = (period-1) is set - state_ = ST_STOPPED; - Write(brightness_eval_->Eval(period - 1)); - return false; - } return true; } + protected: + // test if time stored in last_update_time_ differs from provided timestamp. + bool inline timeChangedSinceLastUpdate(uint32_t now) { + return (now & 255) != last_update_time_; + } + + void trackLastUpdateTime(uint32_t t) { last_update_time_ = (t & 255); } + B& SetBrightnessEval(BrightnessEvaluator* be) { brightness_eval_ = be; // start over after the brightness evaluator changed return Reset(); } + public: + // Number of bits used to control brightness with Min/MaxBrightness(). + static constexpr uint8_t kBitsBrightness = 8; + static constexpr uint8_t kBrightnessStep = 1; + private: static constexpr uint8_t ST_STOPPED = 0; - static constexpr uint8_t ST_RUNNING = 1; - static constexpr uint8_t ST_IN_DELAY_AFTER_PHASE = 2; + static constexpr uint8_t ST_INIT = 1; + static constexpr uint8_t ST_RUNNING = 2; + static constexpr uint8_t ST_IN_DELAY_AFTER_PHASE = 3; uint8_t state_ : 2; uint8_t bLowActive_ : 1; - - // Number of bits used to control brightness with MaxBrightness(). Using - // only 5 bits here saves us a byte, since summing up with previous defs. - public: - static constexpr uint8_t kBitsBrightness = 5; - static constexpr uint8_t kBrightnessStep = 1 << (8 - kBitsBrightness); - - private: - uint8_t maxBrightness_ : kBitsBrightness; + uint8_t minBrightness_; + uint8_t maxBrightness_; // this is where the BrightnessEvaluator object will be stored using // placment new. Set MAX_SIZE to class occupying most memory - static constexpr auto MAX_SIZE = sizeof(CandleBrightnessEvaluator); + static constexpr auto MAX_SIZE = + __max(sizeof(CandleBrightnessEvaluator), + __max(sizeof(BreatheBrightnessEvaluator), + __max(sizeof(ConstantBrightnessEvaluator), // NOLINT + sizeof(BlinkBrightnessEvaluator)))); alignas(alignof( CloneableBrightnessEvaluator)) char brightness_eval_buf_[MAX_SIZE]; static constexpr uint16_t kRepeatForever = 65535; uint16_t num_repetitions_ = 1; - static constexpr uint32_t kTimeUndef = -1; - uint32_t last_update_time_ = kTimeUndef; - uint32_t time_start_ = kTimeUndef; + // We store the timestamp the effect was last updated to avoid multiple + // updates when called during the same time tick. Only the lower 8 bits of + // the timestamp are used (which saves us 3 bytes of memory per JLed + // instance), resulting in limited accuracy, which may lead to false + // negatives if Update() is not called for a longer time (i.e. > 255ms), + // which should not be a problem at all. + uint8_t last_update_time_ = 0; + uint32_t time_start_ = 0; uint16_t delay_before_ = 0; // delay before the first effect starts uint16_t delay_after_ = 0; // delay after each repetition }; -template T* ptr(T &obj) {return &obj;} // NOLINT -template T* ptr(T *obj) {return obj;} +template +T* ptr(T& obj) { // NOLINT + return &obj; +} +template +T* ptr(T* obj) { + return obj; +} // a group of JLed objects which can be controlled simultanously, in parallel // or sequentially. -template +template class TJLedSequence { protected: // update all leds parallel. Returns true while any of the JLeds is // active, else false bool UpdateParallel() { auto result = false; + uint32_t t = ptr(leds_[0])->Hal().millis(); for (auto i = 0u; i < n_; i++) { - result |= ptr(leds_[i])->Update(); + result |= ptr(leds_[i])->Update(t); } return result; } @@ -488,13 +551,10 @@ class TJLedSequence { // update all leds sequentially. Returns true while any of the JLeds is // active, else false bool UpdateSequentially() { - if (cur_ >= n_) { - return false; - } if (!ptr(leds_[cur_])->Update()) { return ++cur_ < n_; } - return true;; + return true; } void ResetLeds() { @@ -503,24 +563,24 @@ class TJLedSequence { } } - public: enum eMode { SEQUENCE, PARALLEL }; TJLedSequence() = delete; template - TJLedSequence(eMode mode, T (&leds)[N]) : TJLedSequence(mode, leds, N) {} + TJLedSequence(eMode mode, L (&leds)[N]) : TJLedSequence(mode, leds, N) {} - TJLedSequence(eMode mode, T* leds, size_t n) + TJLedSequence(eMode mode, L* leds, size_t n) : mode_{mode}, leds_{leds}, cur_{0}, n_{n} {} bool Update() { - if (!is_running_) { + if (!is_running_ || n_ < 1) { return false; } - const auto led_running = (mode_ == eMode::PARALLEL) ? UpdateParallel() - : UpdateSequentially(); + const auto led_running = (mode_ == eMode::PARALLEL) + ? UpdateParallel() + : UpdateSequentially(); if (led_running) { return true; } @@ -529,7 +589,7 @@ class TJLedSequence { cur_ = 0; ResetLeds(); - is_running_ = ++iteration_ < num_repetitions_ || + is_running_ = ++iteration_ < num_repetitions_ || num_repetitions_ == kRepeatForever; return is_running_; @@ -543,26 +603,27 @@ class TJLedSequence { } void Stop() { + is_running_ = false; for (auto i = 0u; i < n_; i++) { - leds_[i].Stop(); + ptr(leds_[i])->Stop(); } } // set number of repetitions for the sequence - TJLedSequence Repeat(uint16_t num_repetitions) { + B& Repeat(uint16_t num_repetitions) { num_repetitions_ = num_repetitions; - return *this; + return static_cast(*this); } // repeat Forever - TJLedSequence Forever() { return Repeat(kRepeatForever); } + B& Forever() { return Repeat(kRepeatForever); } bool IsForever() const { return num_repetitions_ == kRepeatForever; } private: - const eMode mode_; - T* leds_; + eMode mode_; + L* leds_; size_t cur_; - const size_t n_; + size_t n_; static constexpr uint16_t kRepeatForever = 65535; uint16_t num_repetitions_ = 1; uint16_t iteration_ = 0; @@ -570,5 +631,4 @@ class TJLedSequence { }; }; // namespace jled - #endif // SRC_JLED_BASE_H_ diff --git a/src/pico_hal.h b/src/pico_hal.h index 9b85173..9e2b68c 100644 --- a/src/pico_hal.h +++ b/src/pico_hal.h @@ -43,7 +43,8 @@ class PicoHal { uint32_t *top_) { // Set the frequency, making "top_" as large as possible for maximum // resolution. - *div = (uint32_t)(16 * clock_get_hz(clk_sys) / (uint32_t)freq); + *div = static_cast(16 * clock_get_hz(clk_sys) / + static_cast(freq)); *top_ = 1; for (;;) { // Try a few small prime factors to get close to the desired @@ -100,7 +101,7 @@ class PicoHal { void analogWrite(uint8_t val) const { set_pwm_duty(slice_num_, channel_, top_, - (uint32_t)(DUTY_100_PCT / 255) * val); + static_cast(DUTY_100_PCT / 255) * val); } uint32_t millis() const { return to_ms_since_boot(get_absolute_time()); } diff --git a/test/Makefile b/test/Makefile index 9802d39..592451e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,8 @@ # JLed unit tests Makefile # run `make coverage` to run all test and calculate coverage +# +.PHONY: phony + CFLAGS=-std=c++14 -c -Wall -Wextra -I. -I../src -I./esp-idf \ --coverage -fno-inline \ -fno-inline-small-functions -fno-default-inline -g -fmax-errors=5 \ @@ -8,29 +11,31 @@ CFLAGS=-std=c++14 -c -Wall -Wextra -I. -I../src -I./esp-idf \ LDFLAGS=-fprofile-arcs -ftest-coverage -TEST_ARDUINO_MOCK_SRC=Arduino.cpp test_arduino_mock.cpp test_main.cpp +CATCH=catch2/catch_amalgamated.cpp + +TEST_ARDUINO_MOCK_SRC=Arduino.cpp test_arduino_mock.cpp test_main.cpp ${CATCH} TEST_ARDUINO_MOCK_OBJECTS=$(TEST_ARDUINO_MOCK_SRC:.cpp=.o) -TEST_JLED_SRC=Arduino.cpp test_jled.cpp test_main.cpp ../src/jled_base.cpp +TEST_JLED_SRC=Arduino.cpp test_jled.cpp test_main.cpp ../src/jled_base.cpp ${CATCH} TEST_JLED_OBJECTS=$(TEST_JLED_SRC:.cpp=.o) -TEST_JLED_SEQUENCE_SRC=Arduino.cpp test_jled_sequence.cpp test_main.cpp ../src/jled_base.cpp +TEST_JLED_SEQUENCE_SRC=Arduino.cpp test_jled_sequence.cpp test_main.cpp ../src/jled_base.cpp ${CATCH} TEST_JLED_SEQUENCE_OBJECTS=$(TEST_JLED_SEQUENCE_SRC:.cpp=.o) TEST_ESP32_SRC=esp-idf/esp_timer.cpp esp-idf/driver/ledc.cpp \ - test_esp32_hal.cpp ../src/esp32_hal.cpp test_main.cpp + test_esp32_hal.cpp ../src/esp32_hal.cpp test_main.cpp ${CATCH} TEST_ESP32_OBJECTS=$(TEST_ESP32_SRC:.cpp=.o) -TEST_ESP8266_SRC=Arduino.cpp test_esp8266_hal.cpp test_main.cpp +TEST_ESP8266_SRC=Arduino.cpp test_esp8266_hal.cpp test_main.cpp ${CATCH} TEST_ESP8266_OBJECTS=$(TEST_ESP8266_SRC:.cpp=.o) -TEST_MBED_SRC=mbed.cpp test_mbed_hal.cpp test_main.cpp +TEST_MBED_SRC=mbed.cpp test_mbed_hal.cpp test_main.cpp ${CATCH} TEST_MBED_OBJECTS=$(TEST_MBED_SRC:.cpp=.o) -TEST_ARDUINO_SRC=Arduino.cpp test_arduino_hal.cpp test_main.cpp +TEST_ARDUINO_SRC=Arduino.cpp test_arduino_hal.cpp test_main.cpp ${CATCH} TEST_ARDUINO_OBJECTS=$(TEST_ARDUINO_SRC:.cpp=.o) -TEST_MORSE_SRC=test_example_morse.cpp test_main.cpp +TEST_MORSE_SRC=test_example_morse.cpp test_main.cpp ${CATCH} TEST_MORSE_OBJECTS=$(TEST_MORSE_SRC:.cpp=.o) @@ -67,10 +72,10 @@ bin/test_example_morse: $(TEST_MORSE_OBJECTS) $(CXX) -o $@ $(LDFLAGS) $(TEST_MORSE_OBJECTS) coverage: test - lcov --config-file=.lcovrc --directory ../src --directory .. --capture --output-file coverage.info --no-external - lcov --config-file=.lcovrc --list coverage.info + lcov --config-file=.lcovrc --directory ../src --directory .. --capture --output-file coverage.lcov --no-external + lcov --config-file=.lcovrc --list coverage.lcov mkdir -p report - genhtml coverage.info -o report + genhtml coverage.lcov -o report test: depend all ./bin/test_jled @@ -90,8 +95,8 @@ test: depend all bin: mkdir -p bin -clean: - rm -f coverage.info *.{gcov,gcda,gcno,o} ../src/*.{gcov,gcda,gcno,o} .depend +clean: phony + rm -f {./,esp-idf,esp-idf/driver,catch2}/{*.gcov,*.gcda,*.gcno,*.o} .depend clobber: clean rm -f bin/* diff --git a/test/catch.hpp b/test/catch.hpp deleted file mode 100644 index 7e706f9..0000000 --- a/test/catch.hpp +++ /dev/null @@ -1,17959 +0,0 @@ -/* - * Catch v2.13.7 - * Generated: 2021-07-28 20:29:27.753164 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 13 -#define CATCH_VERSION_PATCH 7 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -// See e.g.: -// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html -#ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -// Only GCC compiler should be used in this block, so other compilers trying to -// mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) - -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) - -#endif - -#if defined(__clang__) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) - -// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug -// which results in calls to destructors being emitted for each temporary, -// without a matching initialization. In practice, this can result in something -// like `std::string::~string` being called on an uninitialized value. -// -// For example, this code will likely segfault under IBM XL: -// ``` -// REQUIRE(std::string("12") + "34" == "1234") -// ``` -// -// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#if defined(_MSC_VER) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(__clang__) // Handle Clang masquerading for msvc -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL -# endif // __clang__ - -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// RTX is a special version of Windows that is real time. -// This means that it is detected as Windows, but does not provide -// the same set of capabilities as real Windows does. -#if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE -#endif - -#if !defined(_GLIBCXX_USE_C99_MATH_TR1) -#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Various stdlib support checks that require __has_include -#if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Even if we do not think the compiler has that warning, we still have -// to provide a macro that can be used by the code. -#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -// The goal of this macro is to avoid evaluation of the arguments, but -// still have the compiler warn on problems inside... -#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) -#endif - -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template