volatile Funktionen

Registriere dich jetzt, um exklusive Vorteile zu genießen! Als registriertes Mitglied kannst du Inhalte herunterladen und profitierst von einem werbefreien Forum.
Mach mit und werde Teil unserer Community!
  • Hallo allerseits,


    unlängst stieß ich wieder einmal auf ein Thema, zu welchem ich keine befriedigende Antwort fand. Vielleicht weis ja jemand im Forum, warum die Antwort so lautet, wie sie eben lautet...


    Frage 1: Warum kann man Memberfunktionen von C++ Klassen volatile deklarieren, nicht-Memberfunktionen (in C++) hingegen nicht?
    Kanonisch stellt sich die gleiche Frage für const.


    Eine Erklärung über die Adresse der Funktion gelingt nicht wirklich, da auch nicht-Memberfunktionen ge-inlined werden können. Rückwärtskompatibilitätsgründe zu C scheiden hier eigentlich auch als Erklärungsversuch aus.


    Frage 2: Welchem Zweck dient diese Art der Deklaration überhaupt (außer einer rein dokumentatorischen)? Ein inline verhindert sie nicht, soll sie ja auch gar nicht!


    Schöne Grüße


    schnasseldag

  • Hallo Schnasseldag,


    C++ ist über die Jahre von dem Erstentwurf von Bjarne Stroustrup leider von fachfremden Theoretikern zahlreichen Normierungen zum Opfer gefallen.



    Als die erste Norm für C++ verabschiedet wurde, haben namhafte Compiler-Hersteller über zwei Jahre gebraucht, um die normenkonforme Umsetzung zu programmieren. Immerhin basierend auf dem zuvor aktuellen C++


    Im Dezember habe ich ein Buch angefangen, das den aktuellen C++14-Standard nach ISO/IEC JTC 1/SC22/WG21 beschreibt. Dieses Teil kann man sich herunterladen und beschreibt auf 1300 Seiten eine Programmiersprache.


    Da sind für mich haarsträubende Konstrukte enthalten.


    Die zwei von Dir aufgeführten gehören auch dazu.


    Wenn Du willst, dann hole ich das Buch wieder hervor und zitiere daraus die entsprechenden Aussagen.


    Beste Grüße


    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    • Icon-Tutorials (IDE: Geany) - GPIO-Library - µController-Programmierung in Icon! - ser. Devices - kein Support per PM / Konversation

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    Edited once, last by Andreas ().

  • Hallo Andreas,



    Wenn Du willst, dann hole ich das Buch wieder hervor und zitiere daraus die entsprechenden Aussagen.


    Au ja, mach das mal! Wir könnten auch glatt noch telefonieren - bin eh' noch nicht im Bett :)


    Ich habe über die letzten Jahre hinweg das Detail zur neusten Entwicklung des Standards verloren. Den letzten größeren Schlag gab's m.E. im C++ 11 Release, in dem Lambdas und Compileroptimierungen zur Objektübergabe von Funktionen definiert wurden. Dumm nur, wenn die Compilerhersteller nicht mit den Implementierungen nachkommen... und man selber nicht mit dm Lesen :s


    Ich war jahrelang ein absoluter Verfechter von C++ und mein Hals schwoll an, wenn jemand daherkam und sagte "C++ würde gegenüber C zu viel Overhead erzeugen und wäre hernach nicht für embedded Entwicklungen geeignet". Das ist Unfug und zeugt nur davon, daß man sich nie mit Compiler, Linker und Codegenerator beschäftigt hat. Bereits der VS 6 C++ Compiler erkannte bei Templateinstanzen der gleichen Typgröße Ähnlichkeiten und hat Codedupletten vermieden. Der Linker hat sie dann über Module hinweg aus dem obj-Code entfernt, sodaß nur eine Codeinstanz im exe übrig blieb. Da muß ich sagen: "Hut ab". Der GCC hingegen legte seinerzeit Code für ARM-Prozessoren an, der im ARM und Thumb Mode schneller lief, wenn man auf die obere Hälfte der Register verzichtete. Das ist ein Witz, aber leider traurige Wahrheit. Mittlerweile sind mehr als 10 Jahre in's Land gegangen und die Karten wurden neu gemischt. Dennoch, man kann sagen, was man will. C# ohne Header und mit seiner Sprachgestaltung hat seinen Reitz. Man mag über Reflection dabettieren ob es nun effizient sei oder nicht (wieviel Code muß eigentlich wirklich effizient implementiert sein?) - es kann etliche Zeilen Code sparen wenn man es richtig einsetzt. Der Garbagecollector - nun, daß war wohl eher ein Schuß nach hinten, zumindest wenn man aus der embedded Welt kommt. Aber auch für für größere Applikationen mit zeitkritischen Komponenten stellt er wohl eher ein Hindernis dar, als das er hilft... Der zuverlässige Aufruf einer ~ vor dem Klassennamen ist noch immer ungeschlagen!


    So, ich hüpfe jetzt doch so langsam in's Bett :)


    Grüßle in den Westen


    schnasseldag

  • Hallo Schnasseldag,


    anrufen lieber nicht - ich habe gerade eine heiße Diskussion im Ahnenforschungs-Chat - mein anderes Hobby neben der Computereierei...


    EDIT 15-OKT-2017: Dieser Beitrag stammt aus dem März 2016. Ob ich damals wirklich Computereierei - oder vielleicht doch Computerei - gemeint haben könnte, lässt sich heute nicht mehr zweifelsfrei rekonstruieren. Vorschlag: Lest einfach das heraus, was Ihr herauslesen möchtet.


    Aber sonst gern - diese Woche habe ich Home-Office und entsprechend viel Zeit. Aber lasse mich erstmal die entsprechenden Passagen zusammensuchen... Dann argumentiert es sich leichter. Aber C++14 ist schon ein Thema für sich.


    Beste Grüße in den Osten und gut's Nächtle!


    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    • Icon-Tutorials (IDE: Geany) - GPIO-Library - µController-Programmierung in Icon! - ser. Devices - kein Support per PM / Konversation

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    Edited once, last by Andreas ().

  • Die Antwort ist einfach: const und volatile Qualifier beziehen sich auf *Zustand*. Und zwar den des Objektes, also letztlich des this-Pointers.


    Waere C++ Python und wuerde das this explizit deklarieren, waere nicht die Methode const oder volatile ausgezeichnet, sondern der this-Pointer:


    Code
    // Pseudo-C++!
    class Foo {
        void someMethod(volatile  Foo* this); // statt die ganze Methode
    };


    Damit wird im Umkehrschluss auch klar, warum statische und frei stehende Funktionen *kein* const/volatile kennen - die haben ja keinen Zustand, auf den sich diese Schluesselworte beziehen koennten. Frame-Variablen werden individuell damit ausgezeichnet, Parameter und statische Variablen ebenfalls. Ergo - kein volatile und const auf Methoden.


    Und der Zweck sind schlussendlich Compiler-Optimierungen. Ohne volatile-Deklaration ist der Compiler in der Lage, so etwas zu machen:


    Code
    // Geschrieben
    void meineMethode() {
       for(int i=0; i < 10; i++) {
            if(i == this.mEinMember) {
                  // tu was!
            }   
       }
    }


    Code
    // Vom Compiler optimiert
    void meineMethode() {
       $register = this.mEinMember;
       for(int i=0; i < 10; i++) {
            if(i == $register) {
                  // tu was!
            }   
       }
    }


    Mit volatile wird also erzwungen, Zustand immer zu lesen, weil man keine Annahmen ueber seine Unveraendertheit taetigen kann. const erlaubt dagegen, eben noch aggressiver zu optimieren.


    Mit inline hat all das *nichts* zu tun, da geht es nur darum, wo der Code hin generiert wird.

  • Ich möchte mich jetzt nicht eingehend mit dem Thema beschäftigen, aber eine einfache Antwort würde mich schon auch interessieren.


    Ich kann kein C/C++ programmieren, höchstens mal eine kleine Korrektur anbringen, muss aber im Zusammenhang mit Schnittstellen und API's öfters mal C/C++ Code lesen und bin da natürlich im Zusammenhang mit "hardwarenaher" Programmierung auch auf das Keyword "volatile" gestossen.


    Ansonsten kenne ich das Keyword von C# wo es eine ganz andere Bedeutung hat.


    Zu Frage 2) kann ich aber eine Antwort geben: die Deklaration "volatile" teilt dem Compiler mit, dass das Objekt durch die Hardware verändert werden kann. Weil sich dadurch die Werte eines Objektes ausserhalb der Kontrolle des Compiler verändern können, wird der Compiler auf eine aggressive Optimierung verzichten. Das System liest dadurch auch den aktuellen Wert eines volatilen Objektes immer neu ein wenn darauf zugegriffen wird, also auch dann, wenn eine frühere Instruktion nach einen Wert vom selben Objekt gefragt hat. Ebenfalls werden zugewiesene Werte unmittelbar direkt bei der Zuweisung zugewiesen.


    Warum mache ich darauf aufmerksam? Weil das Schlüsselwort "volatile" eben nicht nur dokumentatorische Zwecke erfüllt, sondern, wenn falsch und unnötig eingesetzt, auch erhebliche Auswirkungen auf die Performance haben kann.
    Automatisch zusammengefügt:[hr]
    Während ich am Schreiben war hat __deetts__ auch schon die Erklärung geliefert. :)
    ((ja ja, Multitasking eben, da kann es bei mir schon mal 2 Std. dauern, bis eine Antwort geschrieben ist ;)))

    Edited once, last by pgloor ().

  • @deets

    Code
    // Geschrieben
    void meineMethode() {
      for(int i=0; i < 10; i++) {
           if(i == this.mEinMember) {
                 // tu was!
           }   
      }
    }


    Code
    // Vom Compiler optimiert
    void meineMethode() {
      $register = this.mEinMember;
      for(int i=0; i < 10; i++) {
           if(i == $register) {
                 // tu was!
           }   
      }
    }


    Das Beispiel leuchtet ein. Genau dafür mache ich ja eine Member-Methode volatile. Was mir jedoch für C++ (über Python kann ich nichts sagen) nicht einleuchtet, ist die Erklärung des this Pointers.
    In C++ würde ein volatiles Objekt erst während seiner Instanzieierung zu einem solchen gemacht werden. Also
    volatile CMyClass c;
    In einem Rutsch werden dann alle Member volatile. Das ergibt auch Sinn, weil der Implementierer der Klasse nicht immer weis, wie sein Objekt eingesetzt wird. Insofern paßt dann aber die Erklärung mittels this-Pointer für Nicht-Memberfunktionen nicht mehr... Ist echt verhext - gell?


    Es ist natürlich richtig, daß volatile und inline zunächst orthogonal zueinander stehen. Allerdings macht es durchaus Sinn kurze Getter- Setter-Funktionen zu inlinen, die Ihrerseits dann auf volatile Member zugreifen. Insofern kann der Compiler dann den Prolog/Epilog weglassen (sprich den Funktionsaufruf wegoptimieren) und auf die Adresse der Variable zugreifen. Das widerspricht Deiner Aussage in keiner Weise, soll jedoch verdeutlichen, daß solcherart Konstrukte durchaus in Paarung auftreten können...
    Automatisch zusammengefügt:[hr]


    Weil das Schlüsselwort "volatile" eben nicht nur dokumentatorische Zwecke erfüllt, sondern, wenn falsch und unnötig eingesetzt, auch erhebliche Auswirkungen auf die Performance haben kann.


    pgloor: Du beziehst Dich auf die Verwendung des Schlüsselwortes volatile auf Daten, nicht auf Funktionen. Da hast Du natürlich Recht. Meine Frage 2 zielte jedoch auch auf die Verwendung für Funktionen ab. Sorry, da war ich wohl nicht ganz präzise.


  • Das Beispiel leuchtet ein. Genau dafür mache ich ja eine Member-Methode volatile. Was mir jedoch für C++ (über Python kann ich nichts sagen) nicht einleuchtet, ist die Erklärung des this Pointers.
    In C++ würde ein volatiles Objekt erst während seiner Instanzieierung zu einem solchen gemacht werden. Also
    volatile CMyClass c;
    In einem Rutsch werden dann alle Member volatile. Das ergibt auch Sinn, weil der Implementierer der Klasse nicht immer weis, wie sein Objekt eingesetzt wird. Insofern paßt dann aber die Erklärung mittels this-Pointer für Nicht-Memberfunktionen nicht mehr... Ist echt verhext - gell?


    Da irrst du, und da ist nichts verhext. Hast du wast du oben schreibst mal ausprobiert? Ein Instanz volatile erklaert, und dann versucht, einen Methode darauf aufzurufen?


    Denk ein bisschen drueber nach, und sonst - hier kommt der Spoiler :)


  • ...
    volatile CMyClass c;
    ...


    In einem Rutsch werden dann alle Member volatile.


    Da hätte ich wohl besser schreiben sollen "In einem Rutsch werden dann alle Member-Variable volatile."


    Das ich für ein volatiles Objekt lediglich nur noch volatile Funktionen aufrufen kann - hm, das hat man offenbar so festgelegt (wußte ich allerdings bis eben auch nicht). Eine Begründung (außer einer dokumentatorischen) erschließt sich mir jedoch noch immer nicht. :s
    Aber noch mal zurück zur Ausgangsfrage - wieso können Memberfunktionen volatile sein, Nicht-Member jedoch nicht. Letztendlich geht es um Daten, die seiteneffektfrei gelesen bzw. geschrieben werden sollen. Wie der Code realisiert wird, ist doch zunächst sekundär?! Eine volatile Member-Funktion, die eine nicht volatilen Member-Variable schreibt/liest, ist so überflüssig wie ein Kropf. Das volatile hinter der Member-Funktion schützt die Variable nicht davor, in einem Register zu landen.

  • Versuchen wir es noch einmal: Eine volatile Methode behandelt die Member eines Objektes als volatile Daten. Das ist *ALLES* worum es geht. Die Member erreicht man durch this, und darum bezieht sich das volatile - genauso wie ein const - auf den this-Pointer.


    Ob du ein Objekt von aussen volatile erklaerst, und dort endlos auf seinen Membern rumfuhrwerkst, hat damit nichts zu tun - da geht es um den Code, der von *AUSSEN* auf die Member zugreift (Member meint hier Daten, nicht Methoden). Wenn du aber eine Methode auf dem Objekt aufrufst, die *nicht* volatile ist, hat C++ halt beschlossen das zu verbieten - weil es sagt "du hast den Objektzustand volatile von aussen erklaert, ich bezweifele, dass du ihn ploetztlich nicht-volatile bearbeiten willst.


    Genauso wie bei const.


    Und damit ist auch klar, warum nur Methoden volatile sein koennen. So wie auch const. Nur Methoden haben einen impliziten this-Pointer. Alle anderen Daten, auf die sie zugreifen - Parameter, statische Variablen, lokale Variablen - koennen explizit mit volatile gekennzeichnet sein. Das volatile-Schluesselwort bezieht sich also auf this.


  • Versuchen wir es noch einmal: Eine volatile Methode behandelt die Member eines Objektes als volatile Daten. Das ist *ALLES* worum es geht. Die Member erreicht man durch this, und darum bezieht sich das volatile - genauso wie ein const - auf den this-Pointer.


    Der Bezug volatiles im Kontext einer Funktion zu this war mir nicht klar. Mit const springt es einen jedoch direkt an. Ich hab' das unten noch mal an einem Beispiel verdeutlicht. Der Zugriff von baa() auf den Member x des Objektes c1 zeigt recht deutlich, daß das nicht volatile x dennoch volatile durch this betrachtet wird.




    Ob du ein Objekt von aussen volatile erklaerst, und dort endlos auf seinen Membern rumfuhrwerkst, hat damit nichts zu tun - da geht es um den Code, der von *AUSSEN* auf die Member zugreift (Member meint hier Daten, nicht Methoden).


    Vielleicht verstehe ich Dich hier nicht, oder Du irrst in diesem Punkt. Beispiel: c2 ist in sich ein volatiles Objekt. Mithin sind Zugriffe (egal ob über baa() oder direkt über die Membervariable c2.x) auf die nicht volatile Membervariable c2.x dennoch volatile. Die Art des Zugriffs von außen ist damit egal (ebenfalls, ob ein Member innerhalb der Klasse volatile deklariert wurde oder nicht).
    Im umgekehrten Fall von c1 hingegen werden alle Zugriffe auf den nicht volatilen Member c1.x wegoptimiert (egal ob über foo() oder von außen per c1.x-Zugriff).





    Und damit ist auch klar, warum nur Methoden volatile sein koennen. So wie auch const. Nur Methoden haben einen impliziten this-Pointer. Alle anderen Daten, auf die sie zugreifen - Parameter, statische Variablen, lokale Variablen - koennen explizit mit volatile gekennzeichnet sein. Das volatile-Schluesselwort bezieht sich also auf this.


    Jein, so richtig leuchtete (näheres weiter unten) mir immer noch nicht ein, weshalb man bei Nicht-Memberfunktionen nicht doch auch ein volatile hinten anstellen können sollte. Klar, einen this-Pointer gibt's hier nicht aber warum nicht sinngemäß alle außerhalb der Scope der Funktion deklarierte (und innerhalb der Funktion verwendete) Variable einfach innerhalb der Funktion als volatile behandeln? Das entspräche einer "ähnlichen" Behandlung wie der bei Memberfunktionen. Anstelle des this-Kontext tritt eben der Modulkontext, in dem sich die Funktion befindet?!


    Geht aber tatsächlich nicht. Hierzu ein etwas "bekranktes" Beispiel über const.


    Die const Memberfunktion definiert ja, das Objekt nicht zu modifizieren. Schön und gut. Nur außerhalb des this-Pointers darf auch die const-Memberfunktion lustig Seiteneffekte verursachen. Insofern besitzt die Memberfunktion zwei Bezüge. Den des this und den zur Außenwelt. Nachdem der Nicht-Memberfunktion der this-Freiheitsgrad fehlt, würde sich die const-Aussage (sinngemäß auch volatile-Aussage) also auf alle außerhalb der Funktion deklarierte Variable beziehen müssen. Damit entstünde jedoch eine semantische Ungleichbehandlung des Schlüsselwortes volatile für Memberfunktionen und Nicht-Memberfunktionen für den globalen Namensraum. Und vermutlich wollte man gerade das vermeiden.


    @deets: Danke - hast mir sehr auf die Sprünge geholfen. Wenngleich ich wohl doch dabei bleibe meine Member-Variable zu volatilen, als mit den Funktionen herumzuoperieren. Wenn ein Datum volatile ist, dann ist es daß eben. Warum dann also "gemischt" mittels volatiler und nicht-volatiler Funktionen darauf rumfuhrwerken?! Mir fehlt da selbst gerade der Anwendungsfall - aber darum ging es ja auch gar nicht in meiner ursprünglichen Fragestellung....

  • Ich verstehe schon, was du meinst - das "volatile" einen Scope aufmacht, statt sich auf konkrete Daten zu beziehen. Haette man so definieren koennen, hat man aber nicht - das volatile ist halt eine storage-class.


    Und konsequenterweise muesste man in volatile-Methoden auch pruefen, ob eine Instanz auch "von aussen" volatile deklariert wurde - aber hier kommen wir halt an die Grenzen von compile-time-checks, und nur um die geht es hier ja.


    Letztlich ist volatile als Konzept eh nicht mehr zeitgemaess, weil es eine viel zu simple und schlecht definierte Semantik hat. So verhindert volatile zB nicht, dass der Prozessor ein instruction-re-ordering vornimmt. Das ist aber fuer concurrency-programmierung, wo man ja eben genau volatile einsetzt, eh ein Problem.


    In C++ solltest du also sowieso auf die neuen <atomic>-Primitive zurueckgreifen - die sind eh maechtiger.


  • Letztlich ist volatile als Konzept eh nicht mehr zeitgemaess, weil es eine viel zu simple und schlecht definierte Semantik hat. So verhindert volatile zB nicht, dass der Prozessor ein instruction-re-ordering vornimmt. Das ist aber fuer concurrency-programmierung, wo man ja eben genau volatile einsetzt, eh ein Problem.
    In C++ solltest du also sowieso auf die neuen <atomic>-Primitive zurueckgreifen - die sind eh maechtiger.


    Klar, Codeumstellung, Reentranz, Synchronisierung oder auch nur Datenintegrität sind über volatile allein nicht zu erschlagen. Auch Memorybarriers haben damit nix zu tun. Atomare Operationen müssen allerdings auch von Compiler und Runtime unterstützt werden. Die vorgenannten Gedanken kamen mir während ich auf dem Atmega328 programmierte. Der hat einen sehr einfache Interruptstruktur und die Synchronisationsprimitive heißen da cli und sei :) Auf der Ebene muß man sich dann allerdings wieder Gedanken um volatile machen. Auf Applikationsebene andererseits will ich davon nix wissen - völlig Deiner Meinung.


    PS: Ich weis schon, daß Scope {} eine andere Bedeutung hat, mir fiel nur kein passenderes Wort ein.