Zugriff von Lambda auf Attrbute in const Objekt

Gast #6993489
Lesenswert?

Hallo,

ich möchte von einer Klasse ein Objekt erzeugen und dieses mit 
geschweiften Klammern initialisieren.

Die Klasse beinhaltet einen Funktionszeiger, den ich auch mittels 
Lambdaausdrucks initialisieren möchte und welcher Zugriff auf die 
Atribute ha`ben soll.

Folgendes Beispiel geht leider nicht. Gibt es da eine Möglichkeit?
1
class bla{
2
  public:
3
    void (*z)();
4
    int x;
5

6
  private:
7
};
8

9
bla blub = {
10
  .z = [this](){
11
    x = 0;
12
  },
13
  .x = 0
14
};
#6993512
Lesenswert?

Was du als z deklarierst, ist auch keine Methode, sondern nur ein Zeiger 
auf eine Funktion.

Den kannst du als Lambdafunktion setzen. Aber die kann dann nicht auf x 
zugreifen, weil sie keine Methode ist.
Und wie gesagt kann sie auch kein this nehmen - weil keine Methode.
Gast #6993533
Lesenswert?

Ich wollte eine Menustruktur definieren, als Konstanten im Flash. Und 
ein Parameter sollte eben sein, was bei Buttons passieren soll und das 
wollte ich als Lamda definieren.

Nun müsste ich ständig einen zeiger mitgeben und der Schreibaufwand wird 
massiv. Gibt es da eine elegantere Lösung?

So irgendwie wollte ich das halt lösen:
1
class Bildschirm{
2
  public:
3
    void (*button)();
4
    void (*print)();
5
};
6

7
Bildschirm Bild1 = {
8
  .button = [this](){};
9
  [...]
10
};
11

12
[...]
Gast #6993555
Lesenswert?

Versuch doch erstmal, deine Lambdas als normale Funktion zu formulieren 
- wenn das funktioniert, kannste immer noch ne Lambda machen.
1
class Bildschirm{
2
  public:
3
    void (*button)();
4
    void (*print)();
5
};
6

7
void onButtonPress()
8
{}
9

10

11
Bildschirm Bild1 = {
12
  .button = &onButtonPress;
13
  [...]
14
};

Was du hier versuchst zu tun, nennt sich Dependency Injection bzw. 
Inversion of Control. Und wenn der aufrufende Code dynamisch (zur 
Laufzeit) bestimmen können soll, was der aufgerufene Code zu tun hat - 
dann gehts halt nicht ohne Funktionspointer.

Wenn du das Objekt noch mitgeben willst, musste halt deine Callback 
anpassen:
1
class Bildschirm{
2
  public:
3
    void (*button)(Bildschirm* const);
4
    void (*print)(Bildschirm* const);
5
};
6

7
void onButtonPress( Bildschirm* const screen)
8
{}

Beim Zugriff auf Bild1.button musste halt Bild1 als Parameter mit 
reinschleifen.
#6993566
Lesenswert?

Da würde ich eher Funktionsobjekte nehmen.
Das sind Objekte, in deren Klasse der operator() überladen ist (ggf. mit 
Parametern bei Bedarf) und die deshalb wie eine Funktion resp. 
Funktionszeiger resp. Lambdafunktion verwendet werden können.

Der zugehörige Parameter in deiner Bildschirmklasse könnte dann ein 
std::function sein.

Der Vorteil von Funktionsobjekten ist, daß sie noch Daten enthalten 
können (wie man halt Member in ihrer Klasse definiert).

So könnte man das Funktionsobjekt, das in dem Bildschirm abgelegt wird, 
mit dem Bildschirm wiederum initialisieren. Dann weiß das Objekt, wessen 
Methoden es aufrufen muß.
#6993702
Lesenswert?

Es sind einfach zu viele Syntaxfehler drin:
1
#include <functional>
2

3
class Bildschirm{
4
  public:
5
    const std::function<void()> button;
6
    void (* const print)();
7
};
8
int main() {
9

10
    Bildschirm Bild1 = {
11
      .button = []{},
12
      .print = [](){}
13
    };
14
}

Im wesentlichen der Trenner ";" statt "," zwischen den "designated 
initializers";

Trotzdem ist das wahrscheinlich mit der Konvertierung zu einem 
Funktionszeiger nicht das, was Du möchtest. Denn das geht nur für 
capture-less lambda-expr (aus naheliegenden Gründen).
1
#include <functional>
2
#include <iostream>
3

4
class Bildschirm{
5
  public:
6
    const std::function<void()> button;
7
    void (* const print)();
8
};
9

10
int main() {
11
    int x{};
12
    
13
    Bildschirm Bild1 = {
14
      .button = [&x]{
15
            std::cout << __PRETTY_FUNCTION__ << '\n';
16
        },
17
// nicht moeglich
18
//      .print = [&x](){
19
//            std::cout << __PRETTY_FUNCTION__ << '\n';            
20
//        }
21
    };
22
    Bild1.button();
23
    Bild1.print();
24
}
#6993734
Lesenswert?

Ok, das hatte ich überlesen:
1
#include <functional>
2
#include <iostream>
3

4
class Bildschirm{
5
  public:
6
    const std::function<void()> button;
7
    void (* const print)();
8
    
9
    void onEvent(auto f) {
10
        f(this);
11
    };
12
};
13

14
int main() {
15
    int x{};
16
    
17
    Bildschirm Bild1 = {
18
      .button = [&x]{
19
            std::cout << __PRETTY_FUNCTION__ << '\n';
20
        },
21
//      .print = [&x](){
22
//            std::cout << __PRETTY_FUNCTION__ << '\n';            
23
//        }
24
    };
25
    Bild1.button();
26
    Bild1.print();
27
    
28
    Bild1.onEvent([](const Bildschirm* const b){
29
        
30
    });
31
    
32
}
#6993749
Lesenswert?

Vielleicht hilf dies weiter, weil es jetzt glaube ich besser der 
Intention des OP entscpricht:
1
#include <functional>
2
#include <iostream>
3
#include <concepts> // C++20
4

5
class Bildschirm{
6
  public:
7
    void f() const {
8
        button(*this);
9
    }
10
    void g() const {
11
        std::cout << __PRETTY_FUNCTION__ << '\n';        
12
    }
13
    void onF(std::invocable<Bildschirm> auto const f) {
14
        button = f;
15
    }
16
private:
17
    std::function<void(const Bildschirm&)> button;
18
};
19

20
int main() {
21

22
    Bildschirm Bild1;
23
    
24
    Bild1.onF([](const Bildschirm& b){
25
        std::cout << __PRETTY_FUNCTION__ << '\n';        
26
        b.g();
27
    });
28
    
29
    Bild1.f();
30
}

Designated initializers sind hier fehl am Platz, sie eignen sich 
hauptsächlich für POD-like DT.
#6993783
Lesenswert?

Mombert H. schrieb:
> Ich würde vermuten, dass das der massive Schreibaufwand ist, den er
> vermeiden möchte ;-)

Oh ha ;-)
Den hätte er doch sowieso (Beim Erzeugen des Closures in der LE, wenn 
dort ein Capture des (noch nicht gebauten) Objektes möglich wäre).

Mombert H. schrieb:
> Rene schrieb:
>> Nun müsste ich ständig einen zeiger mitgeben und der Schreibaufwand wird
>> massiv.

Zeiger (nullable reference) ist an der Stelle eh sehr fragwürdig. Denn 
ein nullptr an der Stelle wäre immer falsch, und man müsste expl. 
zusichern oder ein run-time check auf nullptr machen.
Daher habe ich das oben geändert.
Gast #6993927
Lesenswert?

Ich glaube der einzige Weg, einen echten this pointer zu bekommen, ist 
mit Klassen und virtuellen Methoden:
1
#include <iostream>
2
#include <array>
3

4
class MenuItem {
5
public:
6
  MenuItem(std::string name) : name(name) {}
7
  std::string name;
8
  virtual void run() const = 0;
9
  virtual void show() const = 0;
10
};
11

12
namespace menu {
13
  const class Bla : public MenuItem {
14
  public:
15
    Bla(std::string name) : MenuItem(name) {}
16
    void run() const {
17
      std::cout << "run bla bla" << std::endl;
18
    }
19
    void show() const {
20
      std::cout << "show " << this->name << std::endl;
21
    }
22
  } bla {"bla"};
23
  const class M123 : public MenuItem {
24
  public:
25
    M123(std::string name) : MenuItem(name) {}
26
    void run() const {
27
      std::cout << "run 123 " << this->name << std::endl;
28
    }
29
    void show() const {
30
      std::cout << "show " << this->name << std::endl;
31
    }
32
  } m123 {"123"};
33
  const std::array<const MenuItem*,2> menu = {
34
    &bla,
35
    &m123
36
  };
37
}
38

39
int main(){
40
  for(auto item : menu::menu)
41
    item->show();
42
}

Ich habe schon lange (ein paar Jahre) klein C++ mehr geschrieben. OMG 
ist das scheisse. Was ich da alles gelernt habe, was man in C++ nicht 
machen kann.

Sowas wie offset_of oder container_of bei c ist mit member pointern 
nicht one hacks machbar.
Kein new von anonymen Klassen mit virtuellen Methoden, und Konstruktoren 
kann man bei anonymen Klassen auch vergessen.
Im globalen Scope eine Referenz von einem Compound literal nehmen, um da 
in einem Member eines Objekts zu initialisieren, geht auch nicht, anders 
als in C ist das dort dann ne temporäre Referenz!?!
Und ein normales C array von anonymen C Klassen Instanzen als Compound 
literal zu erstellen, scheint schon rein syntaktisch nicht möglich zu 
sein!?!
Einfach mal was wie .name initialisieren alla designated initialisers, 
gibts auch nicht.
Und die std::array brauchen immer noch die Angabe, wie viele Elemente da 
sind, auch wenn man es direkt initialisiert?
Und den Bla construktor konnte es mir auch nicht automatisch generieren, 
wie das andere Sprachen wie z.B. JavaScript könnten?
WTF, wie kann mit dem Mist irgend wer arbeiten? Dieses C++ ist ja fast 
noch schlimmer als Rust!
Gast #6994347
Lesenswert?

Ich habe evtl. eine Lösung gefunden.
1
class A {
2
  public:
3
    int x;
4
    void createConstMembers();
5
    std::function<void()> f;
6
};
7

8
A B_Test;
9

10
void A::createConstMembers(){
11
  B_Test = A{
12
    .f = [this](){
13
      x = 0;
14
    }
15
  };
16
}
#6994424
Lesenswert?

Rene schrieb:
> Ich würde das hingegen gerne als Konstante zur Compilezeit definieren
> können, so dass ich mir den Bildschirm* const sparen kann. Aber das geht
> scheinbar nicht.

Ich glaube zwar nicht, dass der OP noch dabei ist, doch trotzdem: 
wieviele "Bildschirm"-Objekte hast Du denn?

Einen?
Dann macht es keinen Sinn, im Code die Möglichkeit zu haben, mehrere 
Objekte davon zu erzeugen. Es sei denn, Du baust Deine Klasse als 
Monostate.
Oder Du verwendest ein Singleton (ggf. mit anderen Problemen).

Meistens hat man es bei µC-Anwendungen nur mit einer begrenzten Anzahl 
von Objekten als Abstraktion einer (internen) Peripherie zu tun. Dann 
kannst Du auch gleich auf Heap/Stack-Objekte bzw. globale Objekte 
verzichten, und statt dessen static-only Klassen bzw. templates 
verwenden.
Gast #6994738
Lesenswert?

Wilhelm M. schrieb:
> Ich glaube zwar nicht, dass der OP noch dabei ist, doch trotzdem:
> wieviele "Bildschirm"-Objekte hast Du denn?

An die zehn und jedes Objekt soll eine eigene Funktion für jeden 
Tastendruck und die "print" Funktion erhalten.

Das wäre zumindest meine Idee. In der Klasse ist fast alles static (die 
Ressourcen), lediglich die Funktionspointer sind Objektattribute.

Derzeit habe ich halt für jede Taste und die Bildschirmausgabe ein 
riesiges Switch-Case.

Mit der Lösung, die ich hier nochmal poste, kann ich scheinbar zumindest 
Objekte erstellen, die alle auf die gemeinsamen static Attribute 
zugreifen können. Ich bin da aber nicht zufrieden mit, die teilen sich 
natürlich auch potentiell alle das gleiche Objekt.
1
class A {
2
  public:
3
    static int x;
4
    void createConstMembers();
5
    std::function<void()> f;
6
};
7

8
A B_Test;
9

10
void A::createConstMembers(){
11
  B_Test = A{
12
    .f = [this](){
13
      x = 0;
14
    }
15
  };
16
}
Gast #6994774
Lesenswert?

Mombert H. schrieb:
> Wo ist der Vorteil gegenüberclass A {

Ich kann nicht für jedes Object eine individuelle Methode erstellen. Die 
machen alle das Selbe.

Bei meiner Lösung kann ich auch schreiben:
1
class A {
2
  public:
3
    static int x;
4
    void createConstMembers();
5
    std::function<void()> f;
6
};
7

8
A A_Test;
9
A B_Test;
10

11
void A::createConstMembers(){
12
  A_Test = A{
13
    .f = [this](){
14
      x = 0;
15
    }
16
  };
17

18
  B_Test = A{
19
    .f = [this](){
20
      x = 10;
21
    }
22
  };
23
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren