C++Guns – RoboBlog

23.11.2017

C++ Guns: RE: Abstraction design and implementation: `repeat` Teil 2

Filed under: Allgemein — Tags: — Thomas @ 19:11

In Teil 1 ging es um die allgemeinen Probleme einer simplen for() Schleife.
In Teil 2 werde ich Vittorio Romeo Ansatz verfolgen, eine repeat() Funktion zu erstellen, die eine Funktion f genau N mal aufruft.

Syntaktisch und semantisch würde ich diesen Code okay finden:

#include <type_traits>
#include <iostream>

void f() {
  cout << "f()\n";
}

template<typename Func>
void repeat(int n, Func&& f) {
  for(int i=0; i < n; ++i) {
    f();
  }
}

int main() {
  repeat(10, f);
}

Die Funktion f wird einfach 10 mal aufgerufen. Das war einfach.

Sobald ich mehr tun will, wirds kompliziert.

Schritt für Schritt. Wir werden sehen wann es nicht mehr klappt.
Möchte ich der Funktion f einen Parameter mitgeben, der nicht von der aktuellen Iterationen abhängt, lässt sich das leicht mit einem Lambda realisieren, welches ein Interface darstellt. Dieses Interface bildet meine Funktion f(int) auf eine Funktion ohne Argument ab. Also genau so eine Funktion, welche repeat() erwartet.

void f(int parameter) {
  cout << "f() parameter " << parameter << "\n";
}

int main() {
  int parameter = 42;
  repeat(10, [parameter]() {
    f(parameter);
  });
}

Die Funktion repeat() ändert sich nicht. Das ist schon einmal ein gutes Zeichen.
Jetzt wird es kritisch. Ich möchte neben dem Parameter noch die Laufvariablen der repeat Funktion mit nach f() übergeben. Dafür benutze ich wieder ein Lambda welches das Interface bereit stellt und auch if constexpr und std::is_invocable_v. Also hight end C++17 Shit :D Mal sehn obs kappt.

void f(int i, int parameter) {
  cout << "f() i: " << i << " parameter: " << parameter << "\n";
}

template<typename Func>
void repeat(int n, Func&& f) {
  for(int i=0; i < n; ++i) {
    
    if constexpr(std::is_invocable_v<Func&&, int>) {
      f(i);
    } else {
      f();
    }
  }
}

int main() {
  int parameter = 42;
  repeat(10, [parameter](int i) {
    f(parameter, i);
  });
}

Der Code compiliert mit einem C++17 Compiler (gcc 7) und liefert folgende Ausgabe:


f() i: 42 parameter: 0
f() i: 42 parameter: 1
f() i: 42 parameter: 2
f() i: 42 parameter: 3
f() i: 42 parameter: 4
f() i: 42 parameter: 5
f() i: 42 parameter: 6
f() i: 42 parameter: 7
f() i: 42 parameter: 8
f() i: 42 parameter: 9

Das ist nicht das, was ich wollte. Ich habe absichtlich die Funktions Argumente parameter und i vertauscht und der Compiler hats nicht gemerkt! Wie sollte er auch. Die Variable i ist nur ein int Type und hat keinerlei Semantik einer Laufvariablen.

Das hat mich am Artikel "abstraction design and implementation: `repeat`" auch so stark gestört. Damit es ist super einfach lazy Bugs zu erzeugen und am Ende hat man nichts gewonnen.

Im dritten Teil will ich versuchen das C++ Typesystem zu nutzen, um die Semantik einer Laufvariablen auszudrücken und im letzten Beispiel einen Compilerfehler zu erzwingen.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress