C++Guns – RoboBlog blogging the bot

07.01.2019

C++ Guns: PRNG

Filed under: Allgemein — Tags: , — Thomas @ 20:01

Parallel mit mehreren Zufallsgeneratoren zu arbeiten ist echt nicht einfach. Die Details verstecken sich im Verständnis.

Mit welchen Seed werden sie erstellt? Einfach die Zeit in Sekunden plus 1, 2 3, 4? Wir können das ja einfach mal ausprobieren.
Die ersten drei Zahlen von vier Generatoren:

int main() {
  for(int j=1; j < 5; ++j) {  
    cout << "\nseed " << j << "\n";
    std::minstd_rand gen(j);

    for(int i=0; i < 3; i++) {
      cout << gen() << "\n";
    }
  }
}

seed 1
48271
182605794
1291394886

seed 2
96542
365211588
435306125

seed 3
144813
547817382
1726701011

seed 4
193084
730423176
870612250

Setzt man die Zahlen ins Verhältnis zueinander und plottet sie, kommt der AHA Effekt:
PRNGparallelshit

Ach was. Zumindest die zuerst gezogenen Zahlen stehen ja in einem festen Verhältnis. Das Verhältnis der dritten gezogene Zufallszahl ist zwar nicht mehr gleich, aber immer noch bei allen eine kleine Zahl. Na, wenn diese vier Generatoren nicht "parallel" laufen ;)

Der C++ Standard antwortet uns mit std::seed_seq

std::seed_seq consumes a sequence of integer-valued data and produces a requested number of unsigned integer values i, 0 ? i < 232 , based on the consumed data. The produced values are distributed over the entire 32-bit range even if the consumed values are close.
...
The following algorithm is used (adapted from the initialization sequence of the Mersenne Twister generator by Makoto Matsumoto and Takuji Nishimura, incorporating the improvements made by Mutsuo Saito in 2007)

Weiterführende Quellen
Sehr interessant: Finding the Best 64-bit Simulation PRNG
Multiple independent random number streams
Dynamic Creation (DC) of Mersenne Twister generators.
Scalable Parallel Random Number Generators Library
Tina’s Random Number Generator Library

C++ Guns: Passing function objects around (Update Example random generators)

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

Update zu Passing random generators around (functor) In jenen Beispiel habe ich den RNG per std::function übergeben. Das muss nicht sein. Mit std::function verliert man die inline Performance. Und es geht auch ohne seltsames std::bind.

Aktualisierter Code:

#include <random>
#include <iostream>
#include <functional>
#include <omp.h>

// can be passed by value. it has no state
template<typename Function>
int function2(Function dice) {
  int sum = 0;
  for(int i=0; i < 1000; i++) {
    sum += dice();
  }
  return sum;
}

// pass by reference, it has state
int function(std::minstd_rand& generator) {
  // we need random numbers from 0 to 10
  std::uniform_int_distribution<int> distribution(0,10);
  // lambda instead of bind
  auto dice = [&](){ return distribution(generator); };

  return function2(dice);
}

int main() {
  omp_set_num_threads(2);

  // create  random generatores with random seed
  std::minstd_rand seedGen;
  std::minstd_rand generators[omp_get_num_threads()];
  for(int i=0; i < omp_get_num_threads(); i++) {
    generators[i].seed(seedGen());
  }

#pragma omp parallel for
  for(int i=0; i < 8; i++) {
    const int id = omp_get_thread_num();
    // pass one generator to our function
    int sum = function(generators[id]);

    std::cout << "Thread " << id << " sum " << sum << "\n";
  }

  std::cout << "Info sizeof std::function<int()> " << sizeof(std::function<int()>) << "\n";

  return 0;
}

C++ Guns: Pass function objects per Forwarding references!

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

I say: Pass (template) function objects per Forwarding references! and not per value. The reason is simple: it works also with function objects with state.

https://en.cppreference.com/w/cpp/language/reference

A little example will show it:

#include <iostream>
#include <random>
using namespace std;

template<typename Function>
auto perValue(Function f) {
  return f();
}

template<typename Function>
auto perForwardReference(Function&& f) {
  return f();
}

int main() {
  std::minstd_rand gen(1156823295);
  
  cout << "Generate 5 random numbers:\n";
  cout << "calls to perValue:\n";
  for(int i=0; i < 5; i++) {
    cout << perValue(gen) << "\n";
  }
  
  cout << "calls to perForwardReference:\n";
  for(int i=0; i < 5; i++) {
    cout << perForwardReference(gen) << "\n";
  }  
}

Generate 5 random numbers:
calls to perValue:
4
4
4
4
4
calls to perForwardReference:
4
193084
730423176
870612250
1216431607

It doesn’t work because the random number generate object has a state. The call to function perValue() change this state on a copy of the generator. And not the one, we pass as argument. But there is no compile time error. Not a single warning. That's the worst case IMO.

The C++ standard and Scott Meyers book "Effective STL" recommend to pass by value. IMO for historical reasons.

C++11 §25.1/10:
[ Note: Unless otherwise specified, algorithms that take function objects as arguments are permitted to copy those function objects freely. Programmers for whom object identity is important should consider using a wrapper class that points to a noncopied implementation object such as reference_wrapper (20.8.3), or some equivalent solution. —end note ]

stackoverflow: why function objects should be pass-by-value

The standard should be updated and the book is horrible outdated. It was written 2008 and forward references come with C++2011. The old solution is to wrap the function into something which can be pass by value, but also change the state. We have now (2011) std::function for that task. And not this ugly thing in the book.

04.01.2019

C++ Guns: Rekursive Template Datentypen

Filed under: Allgemein — Tags: — Thomas @ 17:01

Schonmal probiert den ein und selben template Datentypen mit sich selbst zu nutzen? Mach nichts, ich auch nicht ;)

Als erstes Beispiel ein einfacher, nicht rekursiver Datentyp:

template<typename T>
struct Test {	
	T x;
};

auto func() {
   Test{0};
}

error: class template argument deduction failed:
    Test{0};
error: no matching function for call to 'Test(int)'
note: candidate: 'template<class T> Test(Test<T>)-> Test<T>'

Seit C++17 gibt es Class template argument deduction. Allerdings muss man für User-defined typen auch User-defined deduction guides angeben. Dieser hier ist sehr simpel.

template<typename T>
Test(T) -> Test<T>;

Damit compiliert das oben gezeigte Beispiel (ab GCC7).
Und nun zu dem rekursiven Typen. Ob es geklappt hat, lässt sich mit einem static_assert mit std::is_same zeigen.

template<typename T>
auto func2(T) {	
    static_assert(std::is_same_v<T, Test<Test<int>> >);
}

auto func() {
  func2( Test{Test{0}} ); // Dies ruft den Copy-Konstruktor auf
}

In instantiation of 'auto func2(T) [with T = Test<int>]':
error: static assertion failed

Nein es hat nicht geklappt. Der Grund hierfür ist, dass wir ja eigentlich den Copy-Konstruktor aufgerufen haben.

Mit den deduction guides können wir aber dem Compiler bessere Anweisungen geben.

template<typename T>
Test(Test<T>) -> Test<Test<T>>;

Damit compiliert der Code nun.

Vielen Dank an Freundlich aus dem #c++ Chat für die Fragestellung ;)

03.01.2019

Punktewolke Paarweiser Abstand - Symmetrien entdecken

Filed under: Allgemein — Thomas @ 21:01

Ich habe mir letztens, aus gegebenen Anlass, die wunderbare Weihnachtsvorlesung von Dr. Edmund Weitz angesehen und bin gegen Ende im dritten Teil stecken geblieben.
Die Poincaré-Vermutung (Teil 3 von 3, Weihnachtsvorlesung 2018)
Es wurde die Frage gestellt, Wie sieht das Leben auf einem Torus aus?
Ich mach es kurz: Wenn das Universum ein Torus wäre (in höherer Dimension versteht sich), dann wäre es endlich, ohne Rand. Man könnte in eine Richtung schauen und würde irgendwann sich selbst von Hinten sehen. Und wenn man noch weiter schaut, sieht man sich selbst noch einmal, und noch einmal u.s.w. Natürlich ist die Sicht immer etwas verzerrt.
Damit ergibt auch folgender Satz Sinn: Das Universum ist kleiner als das was wir sehen können. Bzw.: Das beobachtbare Universum ist größer als das eigentliche.

Wie auch immer, zur Veranschaulichung wurde in 2D einige Punkte erstellt, nach rechts/links/oben/unten kopiert und von jedem Punkt zu jedem anderen den Abstand ermittelt und davon ein Histogramm erzeugt. Sieht etwa bei Minute 31:30.
Der Witz ist nun folgender: Gibt es keine sich wiederholende Muster, ist eine Poison Verteilung sichtbar. Gibt es solche Muster, hat die Verteilung Peaks.

Na, das lässt sich doch wunderbar auf ein Digitales Geländemodell (DGM) anwenden!

In der Regel sind die Punkte aus unstrukturierte Laserscans ziemlich dicht verteilt. Es gibt Bereiche an denen sich mehr Messpunkte befinden. Das Flugzeug fliegt die Landschaft ja hin und her ab, und da gibt es Überlappungen. Auch bei der ersten Nachbearbeitung der Daten passieren magische Dinge. Ich habe also keine Ahnung wie das Histogramm aussieht und bin sehr gespannt.

Als Datensatz fand ich von Open Geodata NRW viele DGMs. Die kleinste Datei dgm1l_05974056_Wickede_Ruhr_EPSG25832_XYZ.zip beinhaltete aber immer noch viele GB an Daten. Also nutze ich nur die Kachel 32420_5705 mit 1812468 (1.8M) Punkten.

So sieht die Höhenverteilung aus:
32420_5707_hoehen

Und hier die Punktverteilung. Nur an einigen weißen Stellen sind nicht so viele Punkte vorhanden.
32420_5707_punktwolke

Als nächstes schrieb ich ein Programm welches die Punkte einliest, in einer quadratischen Schleife alle Punkt Abstände berechnet und on-thy-fly ein Histogramm mit den ersten drei Momenten erzeugt. Dabei werden die Abstände zweier Punkte nur einmal berechnet. Also von Punkt A zu B. Aber nicht von B zu A. Der ist ja der selbe. Damit ergeben sich 1642 Milliarden Abstandsberechnungen. Um die alle zu speichern, bräuchte man 12233 GB an Speicherplatz. Den habe ich nicht. Daher habe ich ein Histogramm entwickelt, welches kontinuierlich mit Daten erstellt werden kann.

Auch die Laufzeit ist bei diesen Algorithmus ein Problem. Also berechne ich nur den quadratischen Abstand. Das spart eine Wurzel Berechnung. Und in der Summe ist das schon was. Zusätzlich gibt es noch ein Point2D mit SIMD/SSE Fähigkeiten. Dies spart noch ein paar Operationen pro Abstands Berechnung.

Auf einem 4GHz CPU dauerte die Berechnung drei Stunden. Für einen doppelt so großen Datensatz sind es schon 9 Stunden. Und für nicht unübliche 100M Punkte würde es ca. 12 Monate dauern. Immerhin noch realistisch.

Aber wie sieht nun das Histogramm aus? Nicht vergessen: Das sind quadratische Abstände. In Klammern die Wurzel aus den Werten.

Min: 0
Avg: 3.31e+05 (575.3) 
Max: 2e+06    (1414.2)
Var: 7.71e+10 (277668.9)
Std 2.78e+05  (527.3)

Der minimaler Abstand von 0 deutet auf doppelte Punkte hin. Das ist nicht ungewöhnlich.
Der maximale Abstand ist gerade die Länge der Diagonalen der Boundingbox von jeweils 1km Seitenlänge. Auch richtig.
Der mittlere Abstand ist auch okay. Stelle ich mich gedanklich in die Mitte der BB, habe ich nach allen Seiten mehr als 500m Platz.

hist200_1

Mit Logscale
hist200_2

Keine Peaks zu erkennen. Alles gut. Oder das Histogramm ist nicht fein genug ;)

Update:
Auch bei einem Histogramm mit 5000 Bins zeigen sich keine Peaks.

02.01.2019

Modellbahn: Gleis schottern

Filed under: Allgemein — Tags: , — Thomas @ 19:01

Ich möchte es auch mal probieren. Gleise schottern. Die alten Metall Gleise sind zwar nicht zum Schottern gedacht, aber irgendwie wird es schon.

Alle benötigten Materialien stehen bereit. Auch ein paar Schotterreste haben sich gefunden. Als Mischung habe ich 1:1 Leim und Wasser genommen. Das war aber vielleicht etwas zu dickflüssig. Oder es könnte etwas mehr Spüli rein.
schottertest1

Als erstes werden die Löcher verschlossen, damit der Schotter nicht durch fällt. Auch das Schraubenloch muss zu.
schottertest2

Danach auf der einen Seite den Schotter verteilen. Das ging leichter als gedacht. Auch wenn es etwas zuviel Schotter war.
schottertest3

Aus irgendeinem Grund schlägt mein Leimgemisch Blasen. Aber es geht noch.
schottertest4

Fertig ist die eine Seite. Und das war etwas zuviel Leim.
schottertest5

Auch die andere Seite und die Gleismitte ist fertig. Für die Mitte brauche ich aber unbedingt feineren Schotter. Und womöglich habe ich die Punktkontakte zugekleistert :D
schottertest6

Ich bin gespannt wie es getrocknet aussieht. Und ob das Gleis ohne weitere Befestigung hält.

So. Die Farbe blieb die selbe (diesmal mit Blitz fotografiert). In der Gleismitte brauche ich feineren Schotter. Das Gleis vorher braun anmalen wäre auch eine Möglichkeit. Das etwas steiler abfallende Schotterbett gefällt mir persönlich besser.
schottertest7

Weihnachten 2018: LEGO 40139 60181 & Lebkuchenhaus

Filed under: Allgemein — Thomas @ 18:01

Gretel etwas schüchtern.
Hänsel formt ein Schneeball.
Die böse Hexe wartet schon.
Doch die Gerechtigkeit naht.

LEGOlebkuchenhaus

Bonus 1UP
1UPpilz

01.01.2019

C++ Guns: You can use auto on a private type!

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

See

https://stackoverflow.com/questions/13532784/why-can-i-use-auto-on-a-private-type
https://stackoverflow.com/questions/31185119/preventing-return-of-private-inner-class-instance

struct A {
private:
    struct inner {
    };

public:
  inner func() { }
  void func2(inner) {}
};

auto func() {
    A a;    
    auto x = a.func();
    decltype(x) y;
    a.func2(y);

    A::inner z; // Not ok
}

Auf den ersten Blick etwas überraschend, aber wenn man bedenkt, dass die Zugriffsregeln nur für Namen gelten, ist alles wieder konsistent. Den Namen einer Privaten Klasse darf nicht genutzt werden, aber der Typ schon. Daher funktioniert hier auto, da kein Name explizit hingeschrieben werden muss.

Selbe Argumentation gibt auch für Templates oder temporäre Objekte. Da auch hier das Symbol inner nicht außerhalb der Klasse A auftaucht.

A a;
a.func2(a.func());

25.12.2018

35C3

Filed under: Allgemein — Thomas @ 18:12

https://media.ccc.de/c/35c3

Eventphone: Gestern Heute Morgen

Space Ops 101 - An introduction to Spacecraft Operations
https://media.ccc.de/v/35c3-9923-space_ops_101

After launching a spacecraft into orbit the actual work for mission control starts. Besides taking care of the position and speed of the spacecraft this includes e.g. detailed modeling of the power usage, planning of ground station contacts, payload operations and dealing with unexpected anomalies. In this talk we will see many examples of problems particular to space crafts and how they influence the way space craft mission operations works.

WTF?!
Lecture: Quantum Mechanics
https://media.ccc.de/v/35c3-9792-quantum_mechanics

An (almost) self-contained introduction to the basic ideas of quantum mechanics. The theory and important experimental results will be discussed.

Öde
Lecture: How to teach programming to your loved ones
https://media.ccc.de/v/35c3-9800-how_to_teach_programming_to_your_loved_ones

Teaching beginners how to program is often hard. We love building programs, and seeing our loved ones struggle with this is painful. Showing them how to copy-paste a few example programs and change a few parameters is easy, but bridging from there to building substantial programs is a different game entirely. This talk is about how to teach programming successfully, through comprehensible design recipes, which anyone can follow, using languages and tools designed for beginners. This approach is probably different from how you learned how to program, or how you're used to teaching. It is more effective, however, as it teaches more material successfully to a broader spectrum of people. It is also more enjoyable.

Ganz nett
Ein open-source Hardware- und Software-Design für vierbeinige Laufroboter
https://media.ccc.de/v/35c3-9915-projekt_hannah

Um das Entwickeln von eigenen Laufrobotern zu erleichtern, brauchen wir offene Alternativen zu bestehenden Plattformen. Am Beispiel unseres Projektes "Hannah" stellen wir euch Möglichkeiten vor, wie Open Source in Robotik-Hardware praktisch eingesetzt werden kann.

Sehr interesannt
Lecture: Simulating Universes
https://media.ccc.de/v/35c3-9451-simulating_universes

In this talk I want to present the computational undertakings in the field of cosmological structure formation and galaxy formation. Here, sometimes gigantic simulations help us to unravel the processes that led to the Universe that we can see today. I will give a short overview of our current understanding of the evolution of the Universe, the history and techniques of the simulations and their current state and future.

Lecture: Modeling and Simulation of Physical Systems for Hobbyists
https://media.ccc.de/v/35c3-9385-modeling_and_simulation_of_physical_systems_for_hobbyists#t=43

This is a foundations talk about modeling and simulation as tools for development, testing and debugging systems. It requires very little previous knowledge to address all makers and hobbyists interested in creating or modifying hardware that physically interacts with its environment (e.g. robots, drones, etc.). It explains the purpose of modeling and simulation, basic principles, and tips and tricks on a practical level.

Super!
Lecture: Kosmische Teilchenbeschleuniger und ihre Spuren in der Antarktis
https://media.ccc.de/v/35c3-9664-kosmische_teilchenbeschleuniger_und_ihre_spuren_in_der_antarktis

In diesem Vortrag erklären wir, was Menschen zum Bau komplexer Detektoren an den exotischsten Plätzen der Welt motiviert, um hochenergetische Teilchen aus dem Weltall zu jagen. Wir sprechen über die bahnbrechende Entdeckung diesen Jahres und beleuchten, wie der Durchbruch durch twitter-ähnliche Echtzeit-Monitoring-Systeme überhaupt erst möglich wurde.

Hier noch ein weiterer guter Vortrag aus GPN17
Alice explodiert! ... und woher weiß Bob das?

Sehr interesannt
https://media.ccc.de/v/gpn17-8590-alice_explodiert#t=2841

Wie können wir mithilfe von kosmischer Strahlung, Gammasignalen und Neutrinos mehr über hochenergetische Objekte im Universum herausfinden? Eine Einführung in die Multimessenger- Astroteilchenphysik.

Lecture: The Mars Rover On-board Computer
https://media.ccc.de/v/35c3-9783-the_mars_rover_on-board_computer

Mars Rover Curiosity is one of the most sophisticated pieces of hardware ever launched into space. Because of the communication delay from Earth to Mars, it needs to accomplish most of its tasks completely autonomously: landing, navigation, exploration and singing birthday songs to itself. To do all this, it only has one central onboard computer. Let's look at that computer and the software it runs in detail.

Sehr interesannt
Lecture: Conquering Large Numbers at the LHC
https://media.ccc.de/v/35c3-9851-conquering_large_numbers_at_the_lhc

We are going to outline the ingredients necessary to perform measurements at the LHC, starting from an ordinary bottle of hydrogen. Let us take you on a journey following the path of the protons from this bottle to being ready for collisions in one of the detectors. Once the collisions are recorded we show the approaches and tools on how to extract the metaphorical needle in the haystack.

Faszinierend!
Lecture: Let's reverse engineer the Universe
https://media.ccc.de/v/35c3-9925-let_s_reverse_engineer_the_universe

There is four times more dark matter and over fifteen times more dark energy than regular matter in the universe. And we have absolutely no idea what these invisible dark substances might be. This talk will show how we know that dark energy and dark matter exist, although we cannot see them directly. This kind of reverse enigneering of the universe already revealed some interesting features of the dark parts. However, the true nature of dark matter and dark energy are literally in the dark.

Cool!
SuperMUC-NG
https://media.ccc.de/v/35c3-9703-supermuc-ng

Der nationale Höchstleistungsrechner SuperMUC-NG unterstützt die öffentliche Wissenschaft in Deutschland. Wie ist er aufgebaut, was kann man damit tun, und wo steht er im Vergleich mit den schnellsten Supercomputern der Welt?

12.12.2018

C++ Guns: Rätsel 2018

Filed under: Allgemein — Tags: — Thomas @ 21:12

Betrachte ein unstruturiertes Gitternetz bestehend aus Punkte und Dreiecken. Jeder Punkt kann durchnummeriert werden. Für jeden Punkt IP bezeichnet die Variable NCONN die Anzahl der angeschlossenen Punkte pro Punkt. Und die Variable NCONE bezeichnet die angeschlossene Dreiecke pro Punkt.
Es ist leicht zu sehen, dass Punkte am Rand eine unterschiedliche Anzahl von angeschlossenen Punkte und Dreiecke haben. Wärend Punkte im Mesh gleich viele angeschlossene Punkte und Dreiecke haben.

Unstructured Grid

Folgender C++ könnte grob so ein Gitternetz beschreiben:

#include <vector>

struct Mesh {
  std::vector<int> NCONN, NCONE;

  void update_NCONN_NCONE() {
     // Genaue Impl. nicht relevant für das Rätsel
  }

  bool istRandpunkt(int IP) const {
    return NCONN[IP] != NCONE[IP];
  }
}

Vor der ersten Benutzung der Funktion isRandpunkt müssen die Variablen NCONN und NCONE gefüllt werden. Dies übernimmt die Funktion update_NCONN_NCONE. Die genaue Implementierung dieser Funktion ist für das Rätsel nicht relevant, und wird daher nicht angegeben.

Nun muss der Programmierer darauf achten, dass diese Funktion auch immer aufgerufen wird. Programmierer sind aber auch nur Menschen und machen Fehler. Sollte der Aufruf von update_NCONN_NCONE einmal vergessen werden, stürzt das Programm im besten Fall ab. Im schlechtesten Fall werden falsche Ergebnisse produziert.

Wie kann dem Programmierer geholfen werden?
Es muss auf eine Art sichergestellt werden, dass die Variablen NCONN und NCONE immer mit den aktuellen Daten gefüllt sind. Allerdings darf die Funktion update_NCONN_NCONE nicht unnötig oft aufgerufen werden, da sie unter Umständen eine lange Laufzeit zur Folge hat.

Beachtet auch, dass dieser Code zur Laufzeit in ein Programm geladen werden könnte (Library/Plugin).

Es gibt für jedes Problem viele Lösungen und auch wenige gute.

Beispiel
In der Funktion istRandpunkt bei jedem Aufruf zu testen, ob die Funktion update_NCONN_NCONE aufgerufen werden muss würde zwar sicherstellen, dass die Variablen immer aktuell sind. Allerdings ist das so keine gute Idee, da dies die Laufzeit negativ beeinflusst.

struct Mesh {
  mutable std::vector<int> NCONN, NCONE;
  mutable bool needUpdate = true;

  void update_NCONN_NCONE() {
     if(not needUpdate) return;
     needUpdate = false;
     // Genaue Impl. nicht relevant für das Rätsel
  }

  bool istRandpunkt(int IP) const {
    update_NCONN_NCONE();
    return NCONN[IP] != NCONE[IP];
  }
}

In der Implementierung zeigen sich noch weitere Hässlichkeiten. Die Funktion istRandpunkt sollte als const attributiert sein, da sie keine Daten ändert. Allerdings widerspricht das der Idee, die Variablen on-the-fly zu aktualisieren. Daher müssen sie als mutable deklariert werden.
Damit werden aber Dinge im Hintergrund getriggert, welcher der Programmierer nicht erwartet. Die Funktion istRandpunkt sagt vom Funktionsnamen nicht aus, dass noch ein Update passiert.

Lösung 1: Tags
Die Idee ist, dass die Funktion istRandPunkt nur dann aufgerufen/compiliert werden kann, wenn als Argument irgendetwas mitgegeben werden kann, was vorher nur von der Funktion update_NCONN_NCONE erstellt wurde. Beispielsweise mittels einer Tag Variable. Diese kann nur von update_NCONN_NCONE erstellt werden.

struct Mesh {
    private:
    struct Tag { };

public:
  std::vector<int> NCONN, NCONE;

  Tag update_NCONN_NCONE() {
     // Genaue Impl. nicht relevant für das Rätsel
     return Tag();
  }

  bool istRandpunkt(int IP, Tag) const {
    return NCONN[IP] != NCONE[IP];
  }
};

auto func() {
    Mesh mesh;    
    auto tag = mesh.update_NCONN_NCONE();
    return mesh.istRandpunkt(1, tag);
}

Der Nachteil ist schnell erkannt. Sobald mehr als ein Mesh existiert, kann die tag Variable mehrfach genutzt werden. Es existiert keine Bindung zu einer speziellen Instanz eines Mesh Types.

Lösung 1: Enums

« Newer PostsOlder Posts »

Powered by WordPress