1

Disclaimer: The following code hasn't been compiled, and probably won't work in any way. This is just for opinions relating to this Arduino question as I'm still learning and experimenting with different coding techniques.

As I understand it (and please feel free to offer a (4.)), the following code could be written in numerous ways, including:

  1. No lambdas, and each following lambda as a separate function of its own.
  2. No lambdas, no external functions, and having it all flow within the same function.
  3. Using lambdas in a similar way to the code provided below.
  4. ?

As my programs are getting sizeable, I'm currently finding it difficult to keep track using (1.) and/or (2.) when writing programs, especially if bugs show up. This has led me to question if there was a way to reduce a great many if and for (etc) into a single, text-editor-foldable "seed" (i.e. a single variable).

In my original question it was suggested that a possible way to do this could be option (3.) using lambdas. However, it was also pointed out that it may be overkill/not practical, and without any examples of what I'm trying to achieve, it was difficult to make a more informed judgement. So with this in mind, I have thrown together an example showing how lambdas could help me achieve some readability to my code.

If this is an acceptable technique, or not, I would be extremely grateful for any advice.

Code, lambda example, unfolded:

// Structs data, fans;
void temperatures(int _temperaturePin) {

  data.cTemp = [](int _tPin) { // current temperature
    float _finalReading = 0.0f;
    for (int i = 0; i < temperatureResolution; i++) {
      float _currentReading = analogRead(_tPin);
      _finalReading += ( 10000 / ( ( 1023 / _currentReading ) - 1 ) );
    }
    _finalReading = (_finalReading / temperatureResolution);
    return (uint8_t)_finalReading;
  }(_temperaturePin);

  bool fanState = []( int rOne, int rTwo, int rThree ) { // fans on or off
    int _lowMin = data.tempTarget - ((data.tempTarget - data.tempMin) / 2);
    int _medMax = data.tempTarget + (( data.tempMax - data.tempTarget) / 2);
    bool _fanState = false;
    if (data.cTemp < data.minTemp) {
      digitalWrite(rOne, LOW);
      digitalWrite(rTwo, LOW);
      fans.off = !_fanState;
    } else {
      _fanState = true;
    }
    if ((data.cTemp > data.tempMin) && (data.cTemp > _lowMin)) {
      digitalWrite(rOne, LOW);
      digitalWrite(rTwo, HIGH);
      fans.low = _fanState;
    }
    if ((data.cTemp < data.tempTarget) && (data.cTemp < _medMax)) {
      digitalWrite(rOne, HIGH);
      digitalWrite(rThree, LOW);
      fans.medium = _fanState;
    }
    if (data.cTemp > data.tempMax) {
      digitalWrite(rOne, HIGH);
      digitalWrite(rThree, HIGH);
      fans.high = _fanState;
    }
    return _fanState;
  } (relayOne, relayTwo, relayThree);

  bool overloadProtection = [](bool _fState, int _lCount, int rCount) { // overload protection on or off
    bool _protect;
    int _pwmValue = 255;
    if (_fState) {
      _protect = ((_lCount > MAXLEDS) || ((_lCount > (MAXLEDS / 2) && (_rCount > MAXRELAYS) ))) ? true : false;
    }
    if (_protect) {
      for (int i = 0; i < _lCount; i++) {
        _pwmValue -= 10;
      }
    }
    for (int i = 0; i < _lCount; i++) {
      if (ledPins[i] == HIGH) {
        analogWrite(ledPins[i], _pwmValue);
      } else {
        digitalWrite(ledPins[i], LOW);
      }
    }
    return _protect;
  }(fanState, sizeof(ledPins), sizeof(relayPins);
}

Code, lambda example, folded:

folded code

I can see that methods vs lambdas don't appear to make a huge difference to overall word count in this case, however, I can happily fold away temperatures() and know that's the root of everything I need for anything temperature related (without having to break off into another library; as far as I am aware), instead of having potentially 3 more functions sitting there and cluttering up screen space.

This also makes working within temperatures() a less daunting task than having potentially 100's of for and if (etc) statements permanently in view.

I'm only a hobbyist programmer at the moment, learning in my own time with no formal programming education, so I am 100% completely open to any suggestions or advice, especially due to ignorance regarding this subject on my part.

Much appreciated!

P.S. codereview.stackexchange requires working code to post there otherwise I would have.

3
  • You can also use UECIDE instead of Arduino IDE. With UECIDE you don't need to open all source files in you project. – user31481 Dec 18 '17 at 12:23
  • I currently use Atom as it allows split windows in both horizontal and vertical directions at the same time, and Notepad++ for single/double files. I only tend to use the normal IDE to compile and debug and write the *.ino. UECIDE didn't work when I tried it (~2 years ago though maybe?), I'll definitely have another look, anything should beat the normal IDE! thanks! – user32744 Dec 18 '17 at 16:28
  • I use UECIDE, but keep Arduino IDE mostly to do OTA upgrades to my NodeMCU projects. – user31481 Dec 18 '17 at 22:23
1

Standard method using normal functions

Quickly tell me, what does the temperature() function do?

// Structs data, fans;
void temperatures(int _temperaturePin)
{
  data.cTemp = getTemperature(_temperaturePin);
  bool fanState = getFanState(relayOne, relayTwo, relayThree);
  bool overloadProtection = getOverloadProtecton(fanState, sizeof(ledPins), sizeof(relayPins);
}

Well, it get the temperature, fan status and overload protection status. No need to know about lambdas, or even know C/C++: if you know any programming language (PHP, Javascript), you will able to understand what this code do.

Of course, getTemperature and others are implemented as traditional functions elsewhere. It's not important or relevant to know how they work, just what they do/return. I can write it in another source file and forget about them, never to be seen again.

Lamba method

Same question (with your original code), what does the temperature() function do?

   // Structs data, fans;

    void temperatures(int _temperaturePin) {

      data.cTemp = [](int _tPin) { // current temperature
        float _finalReading = 0.0f;
        for (int i = 0; i < temperatureResolution; i++) {
          float _currentReading = analogRead(_tPin);
          _finalReading += ( 10000 / ( ( 1023 / _currentReading ) - 1 ) );
        }
        _finalReading = (_finalReading / temperatureResolution);
        return (uint8_t)_finalReading;
      }(_temperaturePin);
// More code ...

You have made one giant monolithic block of code, which is anatema by any standard. And code folding doesn't really solve that.

This not so easy to read. You have to scan searching for the each lambda end, taking mental notes about what is finally executing inside temperatures(). To much work, to much visual clutter, merging the what to do with the how to do it.

Think about testing. How do you test your lambdas? You can't do it easily. You need to extract the code from your application, to start with. And add all those external variables your are accessing from inside the lambda.

With traditional functions, no need to change your application to test your functions; just write a test program to exercise your code.

Think about code reuse. It's dificult with your code identify common code to refactor/reuse it.

2
  • Eventually it will be along the lines of, Computer computerOne; then computerOne.oledUpdate(temperatures(), timeAndDate(), etc() ). All my previous iterations are becoming method/library nightmares, it would be nice to just have everything I need in less functions with less abstraction. For myself personally, it's perfectly readable so far, and takes up much less screen space. I will be using the "normal" way (how you have done it) within setup() and loop() mostly. – user32744 Dec 17 '17 at 23:11
  • In addition to Computer, I also have Room which contains DomesticRooms, OfficeRooms, Car, and a load of other libraries, with Computer one of the sub libraries already. My aim was to hopefully at least remove something from the amount of files and functions I am ending up with. It's becoming extremely difficult having only 4 editing windows open at once on one screen. I suppose my aim is to reduce the amount of files, and to reduce the amount of methods within each file; unless of course this is normal? – user32744 Dec 17 '17 at 23:40
1

There is a reason, why the lambda concept was added to the C++ standard. This isn't it. This is called obfuscation.

void temperatures(int pin)
{
  // read the temperature with higher precision than single adc read:
  data.cTemp = 0.0f;
  for (int i = 0; i < temperatureResolution; i++) {
    data.cTemp += ( 10000 / ( ( 1023.0 / analogRead(pin) ) - 1 ) );
  }
  data.cTemp /= temperatureResolution;

Even this is a much better than misusing the lambda.

The main goal should be readability (= self docummented code), so anyone can quickly understand what is going on there.

That is also one of the reasons why the functions exists. The primary goal is you don't have to use the same snippets of code again and again (copy-paste). Try to fix the same bug on many places instead of one function.

In this code you should learn about concepts of finite state machines and ring buffers/floating averages, ...

Anyway, I've always hated parts of code in 10000 lines long file with only two methods or so. It's freaking hell to maintain that kind of code. Yes, having many tabs is also hard, but not harder than scrolling in mile long file. (I hate folding)

3
  • I was just trying to see if I could get around having to turn yet another group of methods into another library/lists of other methods. The example above is what I "quickly" threw together as I've essentially turned 3 methods into one, I could in effect turn 100's of methods into one without having to make a library (but never would do that). My real code is massive, and is becoming a huge struggle to keep track of easily - I was just after a way to remove long files, or loads of files that are more focused. - I'm struggling with finding that balance. – user32744 Dec 18 '17 at 16:39
  • @Timeless I'm tending to use loads of files (almost like in Java - one file per class but I keep subclasses inside of class) to keep simple responsibility of classes / files. – KIIV Dec 18 '17 at 16:45
  • Same as what I have been doing, so much fun :D Oh well, I'm glad Articy Draft exists, it's extremely helpful for pre-planning file structures sometimes – user32744 Dec 18 '17 at 16:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy