C++VM: Inkubator für Features in C++1y



  • Moin miteinander,

    Es gibt da ein sehr interessantes Projekt, das bislang zu wenig Aufmerksamkeit bekommen hat. Es handelt sich um einen Compiler, in dem neue Featurevorschläge für C++1y ausprobiert werden, bevor die working group mit unausgegorenen Proposals zugespammt wird.

    Das Projekt basiert auf der Parrot VM und ist größtenteils in Perl 6 geschrieben -- performancemäßig kann das Ding also nicht mit gcc oder clang mithalten, aber es geht hier ja auch nicht um den Produktiveinsatz, sondern darum, schnell neue Sprachfeatures zusammenhacken und ausprobieren zu können.

    Einiges dessen, was da inkubiert wird, sieht recht spannend aus (obwohl auch einiges an Unfug dabei ist). Beispielsweise ist Posing aus Objective-C übernommen worden:

    struct foo {
      void do_stuff() const { std::cout << "foo\n"; }
    };
    
    class bar : public foo {
      void do_stuff() const { std::cout << "bar\n"; }
    };
    
    pose foo = bar; // Posing: bar ersetzt foo
    
    ...
    
    foo f;
    f.do_stuff(); // gibt "bar" aus.
    

    Es ist dabei noch unklar, wie das später in nativem Code umgesetzt werden soll; Linkermagie bietet sich an, aber damit verlöre man die Möglichkeit, foo nur in bestimmten Programmteilen durch bar zu ersetzen, etwa

    namespace something {
      pose foo = bar;
    
      void function(foo const &f) { f.do_stuff(); }
    }
    
    ...
    
    foo f;
    
    f.do_stuff();           // "foo"
    something::function(f); // "bar"
    

    Besonders spannend für Template-Metaprogrammierer ist Compilezeit-Reflection (bzw. introspection), welche Dinge wie boost.fusion endlich überflüssig machen könnte. So wäre

    struct foo {
      int bar;
      char baz;
      std::string qux;
    };
    
    static_assert(std::member_count<foo>::value == 3);
    
    static_assert(std::is_same<std::member<foo, 0>::type, int        >::value);
    static_assert(std::is_same<std::member<foo, 1>::type, char       >::value);
    static_assert(std::is_same<std::member<foo, 2>::type, std::string>::value);
    
    std::cout << std::member<foo, 0>::name << '\n'; // "bar"
    
    foo f;
    
    f.bar = 10;
    std::member<foo, 0>::type n = f.*(std::member<foo, 0>::memptr); // n = 10
    

    möglich. Analog funktioniert das dann natürlich auch mit Methoden.

    Für jene unter euch, die von anderen, dynamischeren Sprachen kommen, ist die Einführung dynamischer Typen vielleicht am interessantesten. Toll dabei ist, dass das Proposal nicht einmal neue Typen einführen muss, da die gesamte dynamische Objektinformation in Diffs außerhalb des Kernobjekts gehalten wird; als Key genügt die Speicheradresse (allerdings würde ein GC damit zur Pflicht). Das sieht dann beispielsweise so aus:

    struct foo { int bar; };
    
    foo f;
    
    std::add_member<foo, 0, char>(f, "baz");
    
    f.baz = 'A'; // neu hinzugefügter Member in f
    f.qux = 1.2; // std::runtime_exception!
    

    Aus der Einführung dynamischer Typen fällt Laufzeit-Reflection quasi nebenbei ab, braucht aber zurzeit noch eigene Zugriffsfunktionen. Man hofft, das auf Dauer mit der Compilezeit-Introspektion vereinheitlichen zu können, aber für den Moment könnte man das Objekt f von oben etwa so ausgeben:

    for(int i = 0; i < std::dynamic_member_count(f); ++i) {
      std::cout << std::dynamic_member(f, i) << '\n';
    }
    

    Dabei wird die korrekte Überladung von operator<< automatisch bestimmt. Es ist natürlich möglich, das von Hand zu überstimmen, am einfachsten als

    for(int i = 0; i < std::dynamic_member_count(f); ++i) {
      if(std::dynamic_type_compatible<std::dynamic_member(f, i)::type, int>::value) {
        int n = std::dynamic_member(f, i);
        std::cout << n << '\n';
      } else {
        std::cout << std::dynamic_member(f, i) << '\n';
      }
    }
    

    aber auch beispielsweise als

    std::cout << std::dynamic_force<int>(std::dynamic_member(f, i)) << '\n';
    

    oder

    std::cout << std::dynamic_prefer<int>(std::dynamic_member(f, i)) << '\n';
    

    Dieses Proposal wächst noch, es ist also davon auszugehen, dass Laufzeittypintrospektion letztendlich mächtiger und angenehmer zu benutzen sein wird.

    Es steht auch im Raum, eine Einbindung anderer Sprachen vergleichbar zu inline asm herzustellen. Beispielsweise könnte python als

    struct foo {
      int bar;
    };
    
    ...
    
    foo f;
    
    foreign<"python">("f = %0" : : "r"(f));
    foreign<"python">("f.bar = 10");
    
    std::cout << f.bar << '\n'; // 10
    

    eingebunden werden. Wie praktisch das wäre, um auch zwischen verschiedenen Skriptsprachen kommunizieren zu können, ist offensichtlich -- wie trivial wäre es auf einmal, seine Ruby-Webapplikation mit einem Perl-Parser zu verbinden und die geschwindigkeitsrelevanten Teile in C++ auszulagern!

    Wer mehr über dieses aufregende Projekt erfahren will: Alles Wissenswerte darüber erfährt man hier.



  • Und ich dachte schon es würde nichts Gutes mehr kommen. 🙂 👍



  • Gut geschrieben! 👍

    Bei einigen Features wie Compilezeit-Reflection könnte ich mir sogar vorstellen, dass sie in einigen Jahrzehnten in Betracht gezogen werden 😉



  • Damit kann man auch Move- und Copydestruktoren anwenden!



  • Ein Aprilscherz ist nicht gut, wenn man ihn schon an der Überschrift erkennt.



  • kritiker__ schrieb:

    Ein Aprilscherz ist nicht gut, wenn man ihn schon an der Überschrift erkennt.

    Ist es denn einer?
    Mir geht der ganze "Spaß" mit Aprilscherz oder nicht auf die Eier...

    Was soll ich jetzt glauben?



  • Skym0sh0 schrieb:

    Was soll ich jetzt glauben?

    Klick mal auf den Link von seldon . 🙂



  • 😞

    Deswegen mag ich diese Scherze nicht... 😞


Log in to reply